github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/pkg/shardddl/pessimism/ops.go (about)

     1  // Copyright 2020 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 pessimism
    15  
    16  import (
    17  	"context"
    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  // TODO(csuzhangxc): assign terror code before merged into the master branch.
    25  
    26  // PutOperationDeleteExistInfo puts an operation and deletes an info in one txn,
    27  // if the info exists in etcd before.
    28  // This function should often be called by DM-worker.
    29  func PutOperationDeleteExistInfo(cli *clientv3.Client, op Operation, info Info) (done bool, rev int64, err error) {
    30  	putOp, err := putOperationOp(op)
    31  	if err != nil {
    32  		return false, 0, nil
    33  	}
    34  	delOp := deleteInfoOp(info)
    35  
    36  	infoCmp := infoExistCmp(info)
    37  
    38  	getOp := getOperationOp(op)
    39  
    40  	ctx, cancel := context.WithTimeout(cli.Ctx(), etcdutil.DefaultRequestTimeout)
    41  	defer cancel()
    42  
    43  	resp, err := cli.Txn(ctx).If(infoCmp).Then(putOp, delOp).Else(getOp).Commit()
    44  	if err != nil {
    45  		return false, 0, err
    46  	}
    47  
    48  	if !resp.Succeeded && len(resp.Responses) == 1 {
    49  		if getResponse := resp.Responses[0].GetResponseRange(); getResponse.GetCount() == 1 {
    50  			// err must be nil here, or the last putOperationOp will return an error.
    51  			opBytes, _ := op.toJSON()
    52  			// we may successfully delete the info in the previous txn but fail to get the result
    53  			// before the connection is broken. If we check the operation is the same with the put
    54  			// operation, we can safely assume the done operation is okay.
    55  			return opBytes == string(getResponse.Kvs[0].Value), resp.Header.Revision, nil
    56  		}
    57  	}
    58  	return resp.Succeeded, resp.Header.Revision, nil
    59  }
    60  
    61  // DeleteInfosOperations deletes the shard DDL infos and operations in etcd.
    62  // This function should often be called by DM-master when calling UnlockDDL.
    63  func DeleteInfosOperations(cli *clientv3.Client, infos []Info, ops []Operation) (int64, error) {
    64  	opsDel := make([]clientv3.Op, 0, len(infos)+len(ops))
    65  	for _, info := range infos {
    66  		opsDel = append(opsDel, deleteInfoOp(info))
    67  	}
    68  	for _, op := range ops {
    69  		opsDel = append(opsDel, deleteOperationOp(op))
    70  	}
    71  	_, rev, err := etcdutil.DoTxnWithRepeatable(cli, etcdutil.ThenOpFunc(opsDel...))
    72  	return rev, err
    73  }
    74  
    75  // DeleteInfosOperationsByTask deletes the shard DDL infos and operations of a specified task in etcd.
    76  // This function should often be called by DM-master when deleting ddl meta data.
    77  func DeleteInfosOperationsByTask(cli *clientv3.Client, task string) (int64, error) {
    78  	opsDel := make([]clientv3.Op, 0, 2)
    79  	opsDel = append(opsDel, clientv3.OpDelete(common.ShardDDLPessimismInfoKeyAdapter.Encode(task), clientv3.WithPrefix()))
    80  	opsDel = append(opsDel, clientv3.OpDelete(common.ShardDDLPessimismOperationKeyAdapter.Encode(task), clientv3.WithPrefix()))
    81  	_, rev, err := etcdutil.DoTxnWithRepeatable(cli, etcdutil.ThenOpFunc(opsDel...))
    82  	return rev, err
    83  }