vitess.io/vitess@v0.16.2/go/vt/schema/tablegc.go (about)

     1  /*
     2  Copyright 2020 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package schema
    18  
    19  import (
    20  	"fmt"
    21  	"regexp"
    22  	"strings"
    23  	"time"
    24  
    25  	"vitess.io/vitess/go/textutil"
    26  )
    27  
    28  // TableGCState provides a state for the type of GC table: HOLD? PURGE? EVAC? DROP? See details below
    29  type TableGCState string
    30  
    31  const (
    32  	// HoldTableGCState is the state where table was just renamed away. Data is still in tact,
    33  	// and the user has the option to rename it back. A "safety" period.
    34  	HoldTableGCState TableGCState = "HOLD"
    35  	// PurgeTableGCState is the state where we purge table data. Table in this state is "lost" to the user.
    36  	// if in this state, the table will be fully purged.
    37  	PurgeTableGCState TableGCState = "PURGE"
    38  	// EvacTableGCState is a waiting state, where we merely wait out the table's pages to be
    39  	// gone from InnoDB's buffer pool, adaptive hash index cache, and whatnot.
    40  	EvacTableGCState TableGCState = "EVAC"
    41  	// DropTableGCState is the state where the table is to be dropped. Probably ASAP
    42  	DropTableGCState TableGCState = "DROP"
    43  	// TableDroppedGCState is a pseudo state; a hint that the table does not exists anymore
    44  	TableDroppedGCState TableGCState = ""
    45  )
    46  
    47  const (
    48  	GCTableNameExpression string = `^_vt_(HOLD|PURGE|EVAC|DROP)_([0-f]{32})_([0-9]{14})$`
    49  )
    50  
    51  var (
    52  	gcUUIDRegexp      = regexp.MustCompile(`^[0-f]{32}$`)
    53  	gcTableNameRegexp = regexp.MustCompile(GCTableNameExpression)
    54  
    55  	gcStates = map[string]TableGCState{
    56  		string(HoldTableGCState):  HoldTableGCState,
    57  		string(PurgeTableGCState): PurgeTableGCState,
    58  		string(EvacTableGCState):  EvacTableGCState,
    59  		string(DropTableGCState):  DropTableGCState,
    60  	}
    61  )
    62  
    63  // IsGCUUID answers 'true' when the given string is an GC UUID, e.g.:
    64  // a0638f6bec7b11ea9bf8000d3a9b8a9a
    65  func IsGCUUID(uuid string) bool {
    66  	return gcUUIDRegexp.MatchString(uuid)
    67  }
    68  
    69  // generateGCTableName creates a GC table name, based on desired state and time, and with optional preset UUID.
    70  // If uuid is given, then it must be in GC-UUID format. If empty, the function auto-generates a UUID.
    71  func generateGCTableName(state TableGCState, uuid string, t time.Time) (tableName string, err error) {
    72  	if uuid == "" {
    73  		uuid, err = CreateUUIDWithDelimiter("")
    74  	}
    75  	if err != nil {
    76  		return "", err
    77  	}
    78  	if !IsGCUUID(uuid) {
    79  		return "", fmt.Errorf("Not a valid GC UUID format: %s", uuid)
    80  	}
    81  	timestamp := ToReadableTimestamp(t)
    82  	return fmt.Sprintf("_vt_%s_%s_%s", state, uuid, timestamp), nil
    83  }
    84  
    85  // GenerateGCTableName creates a GC table name, based on desired state and time, and with random UUID
    86  func GenerateGCTableName(state TableGCState, t time.Time) (tableName string, err error) {
    87  	return generateGCTableName(state, "", t)
    88  }
    89  
    90  // AnalyzeGCTableName analyzes a given table name to see if it's a GC table, and if so, parse out
    91  // its state, uuid, and timestamp
    92  func AnalyzeGCTableName(tableName string) (isGCTable bool, state TableGCState, uuid string, t time.Time, err error) {
    93  	submatch := gcTableNameRegexp.FindStringSubmatch(tableName)
    94  	if len(submatch) == 0 {
    95  		return false, state, uuid, t, nil
    96  	}
    97  	t, err = time.Parse(readableTimeFormat, submatch[3])
    98  	return true, TableGCState(submatch[1]), submatch[2], t, err
    99  }
   100  
   101  // IsGCTableName answers 'true' when the given table name stands for a GC table
   102  func IsGCTableName(tableName string) bool {
   103  	return gcTableNameRegexp.MatchString(tableName)
   104  }
   105  
   106  // GenerateRenameStatementWithUUID generates a "RENAME TABLE" statement, where a table is renamed to a GC table, with preset UUID
   107  func GenerateRenameStatementWithUUID(fromTableName string, state TableGCState, uuid string, t time.Time) (statement string, toTableName string, err error) {
   108  	toTableName, err = generateGCTableName(state, uuid, t)
   109  	if err != nil {
   110  		return "", "", err
   111  	}
   112  	return fmt.Sprintf("RENAME TABLE `%s` TO %s", fromTableName, toTableName), toTableName, nil
   113  }
   114  
   115  // GenerateRenameStatement generates a "RENAME TABLE" statement, where a table is renamed to a GC table.
   116  func GenerateRenameStatement(fromTableName string, state TableGCState, t time.Time) (statement string, toTableName string, err error) {
   117  	return GenerateRenameStatementWithUUID(fromTableName, state, "", t)
   118  }
   119  
   120  // ParseGCLifecycle parses a comma separated list of gc states and returns a map of indicated states
   121  func ParseGCLifecycle(gcLifecycle string) (states map[TableGCState]bool, err error) {
   122  	states = make(map[TableGCState]bool)
   123  	tokens := textutil.SplitDelimitedList(gcLifecycle)
   124  	for _, token := range tokens {
   125  		token = strings.ToUpper(token)
   126  		state, ok := gcStates[token]
   127  		if !ok {
   128  			return states, fmt.Errorf("Unknown GC state: %s", token)
   129  		}
   130  		states[state] = true
   131  	}
   132  	// DROP is implicitly included.
   133  	states[DropTableGCState] = true
   134  	return states, nil
   135  }