github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/pkg/ha/lightning_coordinate.go (about) 1 // Copyright 2022 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 ha 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 // Simple status transition to make all lightning synchronized in physical import 25 // mode. 26 // When DM-master creates the task, it will write LightningNotReady for all subtasks. 27 // When DM-worker enters load unit, it will mark itself as LightningReady. When 28 // all DM-workers are ready, lightning can be started so its parallel import can 29 // work. 30 // When lightning is finished and its returned error is also resolved, it will mark 31 // itself as LightningFinished. When all DM-workers are finished, sync unit can 32 // be continued. 33 const ( 34 LightningNotReady = "not-ready" 35 LightningReady = "ready" 36 LightningFinished = "finished" 37 ) 38 39 // PutLightningStatus puts the status for the source of the subtask. 40 // k/v: (task, sourceID) -> status. 41 // This function should be called by DM-worker. 42 func PutLightningStatus(cli *clientv3.Client, task, sourceID, status string) (int64, error) { 43 data, err := json.Marshal(status) 44 if err != nil { 45 return 0, err 46 } 47 key := common.LightningCoordinationKeyAdapter.Encode(task, sourceID) 48 49 _, rev, err := etcdutil.DoTxnWithRepeatable(cli, etcdutil.ThenOpFunc(clientv3.OpPut(key, string(data)))) 50 if err != nil { 51 return 0, err 52 } 53 return rev, nil 54 } 55 56 // PutLightningNotReadyForAllSources puts LightningNotReady for all sources of the subtask. 57 // This function should be called by DM-master. 58 func PutLightningNotReadyForAllSources(cli *clientv3.Client, task string, sources []string) (int64, error) { 59 data, err := json.Marshal(LightningNotReady) 60 if err != nil { 61 return 0, err 62 } 63 ops := make([]clientv3.Op, 0, len(sources)) 64 for _, source := range sources { 65 key := common.LightningCoordinationKeyAdapter.Encode(task, source) 66 ops = append(ops, clientv3.OpPut(key, string(data))) 67 } 68 69 _, rev, err := etcdutil.DoTxnWithRepeatable(cli, etcdutil.ThenOpFunc(ops...)) 70 if err != nil { 71 return 0, err 72 } 73 return rev, nil 74 } 75 76 // DeleteLightningStatusForTask deletes the status for all sources of the task. 77 func DeleteLightningStatusForTask(cli *clientv3.Client, task string) (int64, error) { 78 key := common.LightningCoordinationKeyAdapter.Encode(task) 79 _, rev, err := etcdutil.DoTxnWithRepeatable(cli, etcdutil.ThenOpFunc(clientv3.OpDelete(key, clientv3.WithPrefix()))) 80 if err != nil { 81 return 0, err 82 } 83 return rev, nil 84 } 85 86 // GetAllLightningStatus gets the status for all source of the task. 87 func GetAllLightningStatus(cli *clientv3.Client, task string) ([]string, error) { 88 key := common.LightningCoordinationKeyAdapter.Encode(task) 89 resp, err := cli.Get(cli.Ctx(), key, clientv3.WithPrefix()) 90 if err != nil { 91 return nil, err 92 } 93 ret := make([]string, 0, len(resp.Kvs)) 94 for _, kv := range resp.Kvs { 95 var s string 96 if err2 := json.Unmarshal(kv.Value, &s); err2 != nil { 97 return nil, err2 98 } 99 ret = append(ret, s) 100 } 101 return ret, nil 102 }