github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/pkg/shardddl/optimism/column.go (about) 1 // Copyright 2021 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package optimism 15 16 import ( 17 "encoding/json" 18 19 "github.com/pingcap/tiflow/dm/common" 20 "github.com/pingcap/tiflow/dm/pkg/etcdutil" 21 clientv3 "go.etcd.io/etcd/client/v3" 22 ) 23 24 // GetAllDroppedColumns gets the all partially dropped columns. 25 // return lockID -> column-name -> source-id -> upstream-schema-name -> upstream-table-name. 26 func GetAllDroppedColumns(cli *clientv3.Client) (map[string]map[string]map[string]map[string]map[string]DropColumnStage, int64, error) { 27 var done DropColumnStage 28 colm := make(map[string]map[string]map[string]map[string]map[string]DropColumnStage) 29 op := clientv3.OpGet(common.ShardDDLOptimismDroppedColumnsKeyAdapter.Path(), clientv3.WithPrefix()) 30 respTxn, rev, err := etcdutil.DoTxnWithRepeatable(cli, etcdutil.ThenOpFunc(op)) 31 if err != nil { 32 return colm, 0, err 33 } 34 resp := respTxn.Responses[0].GetResponseRange() 35 36 if resp.Count > 0 { 37 for _, kv := range resp.Kvs { 38 keys, err := common.ShardDDLOptimismDroppedColumnsKeyAdapter.Decode(string(kv.Key)) 39 if err != nil { 40 return colm, 0, err 41 } 42 err = json.Unmarshal(kv.Value, &done) 43 if err != nil { 44 return colm, 0, err 45 } 46 lockID := keys[0] 47 column := keys[1] 48 source := keys[2] 49 upSchema := keys[3] 50 upTable := keys[4] 51 if _, ok := colm[lockID]; !ok { 52 colm[lockID] = make(map[string]map[string]map[string]map[string]DropColumnStage) 53 } 54 if _, ok := colm[lockID][column]; !ok { 55 colm[lockID][column] = make(map[string]map[string]map[string]DropColumnStage) 56 } 57 if _, ok := colm[lockID][column][source]; !ok { 58 colm[lockID][column][source] = make(map[string]map[string]DropColumnStage) 59 } 60 if _, ok := colm[lockID][column][source][upSchema]; !ok { 61 colm[lockID][column][source][upSchema] = make(map[string]DropColumnStage) 62 } 63 colm[lockID][column][source][upSchema][upTable] = done 64 } 65 } 66 return colm, rev, nil 67 } 68 69 // PutDroppedColumn puts the partially dropped column names into ectd. 70 // When we drop a column, we save this column's name in etcd. 71 func PutDroppedColumns(cli *clientv3.Client, lockID, source, upSchema, upTable string, cols []string, done DropColumnStage) (int64, bool, error) { 72 ops := make([]clientv3.Op, 0, len(cols)) 73 for _, column := range cols { 74 key := common.ShardDDLOptimismDroppedColumnsKeyAdapter.Encode(lockID, column, source, upSchema, upTable) 75 val, err := json.Marshal(done) 76 if err != nil { 77 return 0, false, err 78 } 79 op := clientv3.OpPut(key, string(val)) 80 ops = append(ops, op) 81 } 82 83 resp, rev, err := etcdutil.DoTxnWithRepeatable(cli, etcdutil.ThenOpFunc(ops...)) 84 if err != nil { 85 return 0, false, err 86 } 87 return rev, resp.Succeeded, nil 88 } 89 90 // DeleteDroppedColumns tries to delete the partially dropped columns for the specified lock ID. 91 // Only when this column is fully dropped in downstream database, 92 // in other words, **we receive all `Done` operation from dm-worker**, 93 // we can delete this column's name from the etcd. 94 func DeleteDroppedColumns(cli *clientv3.Client, lockID string, columns ...string) (rev int64, deleted bool, err error) { 95 ops := make([]clientv3.Op, 0, len(columns)) 96 for _, col := range columns { 97 ops = append(ops, deleteDroppedColumnByColumnOp(lockID, col)) 98 } 99 resp, rev, err := etcdutil.DoTxnWithRepeatable(cli, etcdutil.ThenOpFunc(ops...)) 100 if err != nil { 101 return 0, false, err 102 } 103 return rev, resp.Succeeded, nil 104 } 105 106 // deleteDroppedColumnOp returns a DELETE etcd operation for the specified task and column name. 107 func deleteDroppedColumnByColumnOp(lockID, column string) clientv3.Op { 108 return clientv3.OpDelete(common.ShardDDLOptimismDroppedColumnsKeyAdapter.Encode(lockID, column), clientv3.WithPrefix()) 109 } 110 111 // deleteDroppedColumnsByLockOp returns a DELETE etcd operation for the specified lock. 112 func deleteDroppedColumnsByLockOp(lockID string) clientv3.Op { 113 return clientv3.OpDelete(common.ShardDDLOptimismDroppedColumnsKeyAdapter.Encode(lockID), clientv3.WithPrefix()) 114 } 115 116 // deleteSourceDroppedColumnsOp return a DELETE etcd operation for the specified lock relate to a upstream table. 117 func deleteSourceDroppedColumnsOp(lockID, column, source, upSchema, upTable string) clientv3.Op { 118 return clientv3.OpDelete(common.ShardDDLOptimismDroppedColumnsKeyAdapter.Encode(lockID, column, source, upSchema, upTable)) 119 } 120 121 // CheckColumns try to check and fix all the schema and table names for delete columns infos. 122 func CheckColumns(cli *clientv3.Client, source string, schemaMap map[string]string, tablesMap map[string]map[string]string) error { 123 allColInfos, _, err := GetAllDroppedColumns(cli) 124 if err != nil { 125 return err 126 } 127 128 for lockID, colDropInfo := range allColInfos { 129 for columnName, sourceDropInfo := range colDropInfo { 130 tableInfos, ok := sourceDropInfo[source] 131 if !ok { 132 continue 133 } 134 for schema, tableDropInfo := range tableInfos { 135 realSchema, hasChange := schemaMap[schema] 136 if !hasChange { 137 realSchema = schema 138 } 139 tableMap := tablesMap[schema] 140 for table, stage := range tableDropInfo { 141 realTable, tblChange := tableMap[table] 142 if !tblChange { 143 realTable = table 144 tblChange = hasChange 145 } 146 if tblChange { 147 key := common.ShardDDLOptimismDroppedColumnsKeyAdapter.Encode(lockID, columnName, source, realSchema, realTable) 148 val, err := json.Marshal(stage) 149 if err != nil { 150 return err 151 } 152 opPut := clientv3.OpPut(key, string(val)) 153 opDel := deleteSourceDroppedColumnsOp(lockID, columnName, source, schema, table) 154 155 _, _, err = etcdutil.DoTxnWithRepeatable(cli, etcdutil.ThenOpFunc(opPut, opDel)) 156 if err != nil { 157 return err 158 } 159 } 160 } 161 } 162 } 163 } 164 165 return nil 166 }