github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/syncer/shardddl/pessimist.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 shardddl 15 16 import ( 17 "context" 18 "sync" 19 20 tcontext "github.com/pingcap/tiflow/dm/pkg/context" 21 "github.com/pingcap/tiflow/dm/pkg/etcdutil" 22 "github.com/pingcap/tiflow/dm/pkg/log" 23 "github.com/pingcap/tiflow/dm/pkg/shardddl/pessimism" 24 "github.com/pingcap/tiflow/dm/pkg/terror" 25 clientv3 "go.etcd.io/etcd/client/v3" 26 "go.uber.org/zap" 27 ) 28 29 // Pessimist used to coordinate the shard DDL migration in pessimism mode. 30 type Pessimist struct { 31 mu sync.RWMutex 32 33 logger log.Logger 34 cli *clientv3.Client 35 task string 36 source string 37 38 // the shard DDL info which is pending to handle. 39 pendingInfo *pessimism.Info 40 // the shard DDL lock operation which is pending to handle. 41 pendingOp *pessimism.Operation 42 } 43 44 // NewPessimist creates a new Pessimist instance. 45 func NewPessimist(pLogger *log.Logger, cli *clientv3.Client, task, source string) *Pessimist { 46 return &Pessimist{ 47 logger: pLogger.WithFields(zap.String("component", "shard DDL pessimist")), 48 cli: cli, 49 task: task, 50 source: source, 51 } 52 } 53 54 // Reset resets the internal state of the pessimist. 55 func (p *Pessimist) Reset() { 56 p.mu.Lock() 57 defer p.mu.Unlock() 58 59 p.pendingInfo = nil 60 p.pendingOp = nil 61 } 62 63 // ConstructInfo constructs a shard DDL info. 64 func (p *Pessimist) ConstructInfo(schema, table string, ddls []string) pessimism.Info { 65 return pessimism.NewInfo(p.task, p.source, schema, table, ddls) 66 } 67 68 // PutInfo puts the shard DDL info into etcd and returns the revision. 69 func (p *Pessimist) PutInfo(ctx context.Context, info pessimism.Info) (int64, error) { 70 // put info only no previous operation exists or not done. 71 rev, putted, err := pessimism.PutInfoIfOpNotDone(p.cli, info) 72 if err != nil { 73 return 0, err 74 } else if putted { 75 p.mu.Lock() 76 p.pendingInfo = &info 77 p.mu.Unlock() 78 79 return rev, nil 80 } 81 82 p.logger.Warn("the previous shard DDL operation still exists, waiting for it to be deleted", zap.Stringer("info", info)) 83 84 // wait for the operation to be deleted. 85 ctx2, cancel2 := context.WithCancel(ctx) 86 defer cancel2() 87 ch := make(chan pessimism.Operation, 1) 88 errCh := make(chan error, 1) 89 go pessimism.WatchOperationDelete(ctx2, p.cli, info.Task, info.Source, rev, ch, errCh) 90 91 select { 92 case <-ch: // deleted. 93 case err = <-errCh: 94 return 0, err 95 case <-ctx.Done(): 96 return 0, ctx.Err() 97 } 98 99 rev, err = pessimism.PutInfo(p.cli, info) 100 if err != nil { 101 return 0, err 102 } 103 104 p.mu.Lock() 105 p.pendingInfo = &info 106 p.mu.Unlock() 107 108 return rev, nil 109 } 110 111 // GetOperation gets the shard DDL lock operation relative to the shard DDL info. 112 func (p *Pessimist) GetOperation(ctx context.Context, info pessimism.Info, rev int64) (pessimism.Operation, error) { 113 ctx2, cancel2 := context.WithCancel(ctx) 114 defer cancel2() 115 116 ch := make(chan pessimism.Operation, 1) 117 errCh := make(chan error, 1) 118 go pessimism.WatchOperationPut(ctx2, p.cli, info.Task, info.Source, rev, ch, errCh) 119 120 select { 121 case op := <-ch: 122 p.mu.Lock() 123 p.pendingOp = &op 124 p.mu.Unlock() 125 return op, nil 126 case err := <-errCh: 127 return pessimism.Operation{}, err 128 case <-ctx.Done(): 129 return pessimism.Operation{}, ctx.Err() 130 } 131 } 132 133 // DoneOperationDeleteInfo marks the shard DDL lock operation as done and delete the shard DDL info. 134 func (p *Pessimist) DoneOperationDeleteInfo(op pessimism.Operation, info pessimism.Info) error { 135 op.Done = true // mark the operation as `done`. 136 137 _, _, err := etcdutil.DoTxnWithRepeatable(p.cli, func(_ *tcontext.Context, cli *clientv3.Client) (interface{}, error) { 138 done, _, err := pessimism.PutOperationDeleteExistInfo(cli, op, info) 139 if err != nil { 140 return nil, err 141 } else if !done { 142 return nil, terror.ErrWorkerDDLLockInfoNotFound.Generatef("DDL info for (%s, %s) not found", info.Task, info.Source) 143 } 144 return nil, nil 145 }) 146 if err != nil { 147 return err 148 } 149 150 p.mu.Lock() 151 p.pendingInfo = nil 152 p.pendingOp = nil 153 p.mu.Unlock() 154 155 return nil 156 } 157 158 // PendingInfo returns the shard DDL info which is pending to handle. 159 func (p *Pessimist) PendingInfo() *pessimism.Info { 160 p.mu.RLock() 161 defer p.mu.RUnlock() 162 163 if p.pendingInfo == nil { 164 return nil 165 } 166 info := *p.pendingInfo 167 return &info 168 } 169 170 // PendingOperation returns the shard DDL lock operation which is pending to handle. 171 func (p *Pessimist) PendingOperation() *pessimism.Operation { 172 p.mu.RLock() 173 defer p.mu.RUnlock() 174 175 if p.pendingOp == nil { 176 return nil 177 } 178 op := *p.pendingOp 179 return &op 180 }