github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/pkg/ha/subtask.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 ha 15 16 import ( 17 "context" 18 19 "github.com/pingcap/tiflow/dm/common" 20 "github.com/pingcap/tiflow/dm/config" 21 "github.com/pingcap/tiflow/dm/pkg/etcdutil" 22 "github.com/pingcap/tiflow/dm/pkg/terror" 23 clientv3 "go.etcd.io/etcd/client/v3" 24 ) 25 26 // GetSubTaskCfg gets the subtask config of the specified source and task name. 27 // if the config for the source not exist, return with `err == nil` and `revision=0`. 28 // if task name is "", will return all the subtaskConfigs as a map{taskName: subtaskConfig} of the source 29 // if task name if given, will return a map{taskName: subtaskConfig} whose length is 1. 30 func GetSubTaskCfg(cli *clientv3.Client, source, task string, rev int64) (map[string]config.SubTaskConfig, int64, error) { 31 ctx, cancel := context.WithTimeout(cli.Ctx(), etcdutil.DefaultRequestTimeout) 32 defer cancel() 33 34 var ( 35 tsm = make(map[string]config.SubTaskConfig) 36 resp *clientv3.GetResponse 37 err error 38 ) 39 if task != "" { 40 resp, err = cli.Get(ctx, common.UpstreamSubTaskKeyAdapter.Encode(source, task), clientv3.WithRev(rev)) 41 } else { 42 resp, err = cli.Get(ctx, common.UpstreamSubTaskKeyAdapter.Encode(source), clientv3.WithPrefix(), clientv3.WithRev(rev)) 43 } 44 45 if err != nil { 46 return tsm, 0, terror.ErrHAFailTxnOperation.Delegate(err, "fail to get subtask config, source: %s, task: %s", source, task) 47 } 48 49 cfgs, err := subTaskCfgFromResp(source, task, resp) 50 if err != nil { 51 return tsm, 0, err 52 } 53 tsm = cfgs[source] 54 55 return tsm, resp.Header.Revision, nil 56 } 57 58 // GetAllSubTaskCfg gets all subtask configs. 59 // k/v: source ID -> task name -> subtask config. 60 func GetAllSubTaskCfg(cli *clientv3.Client) (map[string]map[string]config.SubTaskConfig, int64, error) { 61 ctx, cancel := context.WithTimeout(cli.Ctx(), etcdutil.DefaultRequestTimeout) 62 defer cancel() 63 64 resp, err := cli.Get(ctx, common.UpstreamSubTaskKeyAdapter.Path(), clientv3.WithPrefix()) 65 if err != nil { 66 return nil, 0, terror.ErrHAFailTxnOperation.Delegate(err, "fail to get all subtask configs") 67 } 68 69 cfgs, err := subTaskCfgFromResp("", "", resp) 70 if err != nil { 71 return nil, 0, err 72 } 73 74 return cfgs, resp.Header.Revision, nil 75 } 76 77 // putSubTaskCfgOp returns a PUT etcd operation for the subtask config. 78 func putSubTaskCfgOp(cfgs ...config.SubTaskConfig) ([]clientv3.Op, error) { 79 ops := make([]clientv3.Op, 0, len(cfgs)) 80 for _, cfg := range cfgs { 81 value, err := cfg.Toml() 82 if err != nil { 83 return ops, err 84 } 85 key := common.UpstreamSubTaskKeyAdapter.Encode(cfg.SourceID, cfg.Name) 86 ops = append(ops, clientv3.OpPut(key, value)) 87 } 88 return ops, nil 89 } 90 91 // deleteSubTaskCfgOp returns a DELETE etcd operation for the subtask config. 92 func deleteSubTaskCfgOp(cfgs ...config.SubTaskConfig) []clientv3.Op { 93 ops := make([]clientv3.Op, 0, len(cfgs)) 94 for _, cfg := range cfgs { 95 ops = append(ops, clientv3.OpDelete(common.UpstreamSubTaskKeyAdapter.Encode(cfg.SourceID, cfg.Name))) 96 } 97 return ops 98 } 99 100 func putValidatorStageOps(stages ...Stage) ([]clientv3.Op, error) { 101 ops := make([]clientv3.Op, 0, len(stages)) 102 for _, stage := range stages { 103 key := common.StageValidatorKeyAdapter.Encode(stage.Source, stage.Task) 104 value, err := stage.toJSON() 105 if err != nil { 106 return nil, err 107 } 108 ops = append(ops, clientv3.OpPut(key, value)) 109 } 110 return ops, nil 111 } 112 113 func deleteValidatorStageOps(stages ...Stage) []clientv3.Op { 114 ops := make([]clientv3.Op, 0, len(stages)) 115 for _, stage := range stages { 116 key := common.StageValidatorKeyAdapter.Encode(stage.Source, stage.Task) 117 ops = append(ops, clientv3.OpDelete(key)) 118 } 119 return ops 120 } 121 122 func subTaskCfgFromResp(source, task string, resp *clientv3.GetResponse) (map[string]map[string]config.SubTaskConfig, error) { 123 cfgs := make(map[string]map[string]config.SubTaskConfig) 124 if source != "" { 125 cfgs[source] = make(map[string]config.SubTaskConfig) // avoid cfgs[source] is nil 126 } 127 128 if resp.Count == 0 { 129 return cfgs, nil 130 } else if source != "" && task != "" && resp.Count > 1 { 131 // this should not happen. 132 return cfgs, terror.ErrConfigMoreThanOne.Generate(resp.Count, "config", "(source: "+source+", task: "+task+")") 133 } 134 135 for _, kvs := range resp.Kvs { 136 cfg := config.SubTaskConfig{} 137 err := cfg.Decode(string(kvs.Value), true) 138 if err != nil { 139 return cfgs, err 140 } 141 if _, ok := cfgs[cfg.SourceID]; !ok { 142 cfgs[cfg.SourceID] = make(map[string]config.SubTaskConfig) 143 } 144 cfgs[cfg.SourceID][cfg.Name] = cfg 145 } 146 147 return cfgs, nil 148 }