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 }