github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/pkg/shardddl/pessimism/lock.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 "sync" 18 19 "github.com/pingcap/tiflow/dm/master/metrics" 20 "github.com/pingcap/tiflow/dm/pkg/terror" 21 "github.com/pingcap/tiflow/dm/pkg/utils" 22 ) 23 24 // Lock represents the shard DDL lock in memory. 25 // This information does not need to be persistent, and can be re-constructed from the shard DDL info. 26 type Lock struct { 27 mu sync.RWMutex 28 29 ID string // lock's ID 30 Task string // lock's corresponding task name 31 Owner string // Owner's source ID (not DM-worker's name) 32 DDLs []string // DDL statements 33 remain int // remain count of sources needed to receive DDL info 34 35 // whether the DDL info received from the source. 36 // if all of them have been ready, then we call the lock `synced`. 37 ready map[string]bool 38 39 // whether the operations have done (exec/skip the shard DDL). 40 // if all of them have done, then we call the lock `resolved`. 41 done map[string]bool 42 } 43 44 // NewLock creates a new Lock instance. 45 func NewLock(id, task, owner string, ddls, sources []string) *Lock { 46 l := &Lock{ 47 ID: id, 48 Task: task, 49 Owner: owner, 50 DDLs: ddls, 51 remain: len(sources), 52 ready: make(map[string]bool), 53 done: make(map[string]bool), 54 } 55 for _, s := range sources { 56 l.ready[s] = false 57 l.done[s] = false 58 } 59 metrics.ReportDDLPending(task, metrics.DDLPendingNone, metrics.DDLPendingUnSynced) 60 61 return l 62 } 63 64 // TrySync tries to sync the lock, does decrease on remain, re-entrant. 65 // new upstream sources may join when the DDL lock is in syncing, 66 // so we need to merge these new sources. 67 func (l *Lock) TrySync(caller string, ddls, sources []string) (bool, int, error) { 68 l.mu.Lock() 69 defer l.mu.Unlock() 70 71 // check DDL statement first. 72 if !utils.CompareShardingDDLs(ddls, l.DDLs) { 73 return l.remain <= 0, l.remain, terror.ErrMasterShardingDDLDiff.Generate(l.DDLs, ddls) 74 } 75 76 // try to merge any newly joined sources. 77 for _, s := range sources { 78 if _, ok := l.ready[s]; !ok { 79 l.remain++ 80 l.ready[s] = false 81 l.done[s] = false // mark as not-done for newly joined sources. 82 } 83 } 84 85 // only `sync` once. 86 if synced, ok := l.ready[caller]; ok && !synced { 87 l.remain-- 88 l.ready[caller] = true 89 if l.remain == 0 { 90 metrics.ReportDDLPending(l.Task, metrics.DDLPendingUnSynced, metrics.DDLPendingSynced) 91 } 92 } 93 94 return l.remain <= 0, l.remain, nil 95 } 96 97 // ForceSynced forces to mark the lock as synced. 98 func (l *Lock) ForceSynced() { 99 l.mu.Lock() 100 defer l.mu.Unlock() 101 102 if l.remain > 0 { 103 metrics.ReportDDLPending(l.Task, metrics.DDLPendingUnSynced, metrics.DDLPendingSynced) 104 } 105 for source := range l.ready { 106 l.ready[source] = true 107 } 108 l.remain = 0 109 } 110 111 // RevertSynced reverts the synced stage of the sources. 112 func (l *Lock) RevertSynced(sources []string) { 113 l.mu.Lock() 114 defer l.mu.Unlock() 115 116 if l.remain == 0 && len(sources) > 0 { 117 metrics.ReportDDLPending(l.Task, metrics.DDLPendingSynced, metrics.DDLPendingUnSynced) 118 } 119 for _, source := range sources { 120 if synced, ok := l.ready[source]; ok && synced { 121 l.ready[source] = false 122 l.remain++ 123 } 124 } 125 } 126 127 // IsSynced returns whether the lock has synced. 128 func (l *Lock) IsSynced() (bool, int) { 129 l.mu.RLock() 130 defer l.mu.RUnlock() 131 return l.remain <= 0, l.remain 132 } 133 134 // Ready returns the sources sync status or whether they are ready. 135 func (l *Lock) Ready() map[string]bool { 136 l.mu.RLock() 137 defer l.mu.RUnlock() 138 139 ret := make(map[string]bool, len(l.ready)) 140 for k, v := range l.ready { 141 ret[k] = v 142 } 143 return ret 144 } 145 146 // MarkDone marks the operation of the source as done. 147 // NOTE: we do not support revert the `done` after marked now. 148 func (l *Lock) MarkDone(source string) { 149 l.mu.Lock() 150 defer l.mu.Unlock() 151 152 if _, ok := l.done[source]; !ok { 153 return // do not add it if not exists. 154 } 155 l.done[source] = true 156 } 157 158 // IsDone returns whether the operation has done. 159 func (l *Lock) IsDone(source string) bool { 160 l.mu.RLock() 161 defer l.mu.RUnlock() 162 163 return l.done[source] 164 } 165 166 // IsResolved returns whether the lock has resolved (all operations have done). 167 func (l *Lock) IsResolved() bool { 168 l.mu.RLock() 169 defer l.mu.RUnlock() 170 171 for _, done := range l.done { 172 if !done { 173 return false 174 } 175 } 176 return true 177 }