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  }