github.com/KinWaiYuen/client-go/v2@v2.5.4/internal/latch/latch.go (about) 1 // Copyright 2021 TiKV Authors 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 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // NOTE: The code in this file is based on code from the 16 // TiDB project, licensed under the Apache License v 2.0 17 // 18 // https://github.com/pingcap/tidb/tree/cc5e161ac06827589c4966674597c137cc9e809c/store/tikv/latch/latch.go 19 // 20 21 // Copyright 2018 PingCAP, Inc. 22 // 23 // Licensed under the Apache License, Version 2.0 (the "License"); 24 // you may not use this file except in compliance with the License. 25 // You may obtain a copy of the License at 26 // 27 // http://www.apache.org/licenses/LICENSE-2.0 28 // 29 // Unless required by applicable law or agreed to in writing, software 30 // distributed under the License is distributed on an "AS IS" BASIS, 31 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 32 // See the License for the specific language governing permissions and 33 // limitations under the License. 34 35 package latch 36 37 import ( 38 "bytes" 39 "math/bits" 40 "sort" 41 "sync" 42 "time" 43 44 "github.com/KinWaiYuen/client-go/v2/internal/logutil" 45 "github.com/cznic/mathutil" 46 "github.com/twmb/murmur3" 47 "go.uber.org/zap" 48 ) 49 50 type node struct { 51 slotID int 52 key []byte 53 maxCommitTS uint64 54 value *Lock 55 56 next *node 57 } 58 59 // latch stores a key's waiting transactions information. 60 type latch struct { 61 queue *node 62 count int 63 waiting []*Lock 64 sync.Mutex 65 } 66 67 // Lock is the locks' information required for a transaction. 68 type Lock struct { 69 keys [][]byte 70 // requiredSlots represents required slots. 71 // The slot IDs of the latches(keys) that a startTS must acquire before being able to processed. 72 requiredSlots []int 73 // acquiredCount represents the number of latches that the transaction has acquired. 74 // For status is stale, it include the latch whose front is current lock already. 75 acquiredCount int 76 // startTS represents current transaction's. 77 startTS uint64 78 // commitTS represents current transaction's. 79 commitTS uint64 80 81 wg sync.WaitGroup 82 isStale bool 83 } 84 85 // acquireResult is the result type for acquire() 86 type acquireResult int32 87 88 const ( 89 // acquireSuccess is a type constant for acquireResult. 90 // which means acquired success 91 acquireSuccess acquireResult = iota 92 // acquireLocked is a type constant for acquireResult 93 // which means still locked by other Lock. 94 acquireLocked 95 // acquireStale is a type constant for acquireResult 96 // which means current Lock's startTS is stale. 97 acquireStale 98 ) 99 100 // IsStale returns whether the status is stale. 101 func (l *Lock) IsStale() bool { 102 return l.isStale 103 } 104 105 func (l *Lock) isLocked() bool { 106 return !l.isStale && l.acquiredCount != len(l.requiredSlots) 107 } 108 109 // SetCommitTS sets the lock's commitTS. 110 func (l *Lock) SetCommitTS(commitTS uint64) { 111 l.commitTS = commitTS 112 } 113 114 // Latches which are used for concurrency control. 115 // Each latch is indexed by a slot's ID, hence the term latch and slot are used in interchangeable, 116 // but conceptually a latch is a queue, and a slot is an index to the queue 117 type Latches struct { 118 slots []latch 119 } 120 121 type bytesSlice [][]byte 122 123 func (s bytesSlice) Len() int { 124 return len(s) 125 } 126 127 func (s bytesSlice) Swap(i, j int) { 128 s[i], s[j] = s[j], s[i] 129 } 130 131 func (s bytesSlice) Less(i, j int) bool { 132 return bytes.Compare(s[i], s[j]) < 0 133 } 134 135 // NewLatches create a Latches with fixed length, 136 // the size will be rounded up to the power of 2. 137 func NewLatches(size uint) *Latches { 138 powerOfTwoSize := 1 << uint32(bits.Len32(uint32(size-1))) 139 slots := make([]latch, powerOfTwoSize) 140 return &Latches{ 141 slots: slots, 142 } 143 } 144 145 // genLock generates Lock for the transaction with startTS and keys. 146 func (latches *Latches) genLock(startTS uint64, keys [][]byte) *Lock { 147 sort.Sort(bytesSlice(keys)) 148 return &Lock{ 149 keys: keys, 150 requiredSlots: latches.genSlotIDs(keys), 151 acquiredCount: 0, 152 startTS: startTS, 153 } 154 } 155 156 func (latches *Latches) genSlotIDs(keys [][]byte) []int { 157 slots := make([]int, 0, len(keys)) 158 for _, key := range keys { 159 slots = append(slots, latches.slotID(key)) 160 } 161 return slots 162 } 163 164 // slotID return slotID for current key. 165 func (latches *Latches) slotID(key []byte) int { 166 return int(murmur3.Sum32(key)) & (len(latches.slots) - 1) 167 } 168 169 // acquire tries to acquire the lock for a transaction. 170 func (latches *Latches) acquire(lock *Lock) acquireResult { 171 if lock.IsStale() { 172 return acquireStale 173 } 174 for lock.acquiredCount < len(lock.requiredSlots) { 175 status := latches.acquireSlot(lock) 176 if status != acquireSuccess { 177 return status 178 } 179 } 180 return acquireSuccess 181 } 182 183 // release releases all latches owned by the `lock` and returns the wakeup list. 184 // Preconditions: the caller must ensure the transaction's status is not locked. 185 func (latches *Latches) release(lock *Lock, wakeupList []*Lock) []*Lock { 186 wakeupList = wakeupList[:0] 187 for lock.acquiredCount > 0 { 188 if nextLock := latches.releaseSlot(lock); nextLock != nil { 189 wakeupList = append(wakeupList, nextLock) 190 } 191 } 192 return wakeupList 193 } 194 195 func (latches *Latches) releaseSlot(lock *Lock) (nextLock *Lock) { 196 key := lock.keys[lock.acquiredCount-1] 197 slotID := lock.requiredSlots[lock.acquiredCount-1] 198 latch := &latches.slots[slotID] 199 lock.acquiredCount-- 200 latch.Lock() 201 defer latch.Unlock() 202 203 find := findNode(latch.queue, key) 204 if find.value != lock { 205 panic("releaseSlot wrong") 206 } 207 find.maxCommitTS = mathutil.MaxUint64(find.maxCommitTS, lock.commitTS) 208 find.value = nil 209 // Make a copy of the key, so latch does not reference the transaction's memory. 210 // If we do not do it, transaction memory can't be recycle by GC and there will 211 // be a leak. 212 copyKey := make([]byte, len(find.key)) 213 copy(copyKey, find.key) 214 find.key = copyKey 215 if len(latch.waiting) == 0 { 216 return nil 217 } 218 219 var idx int 220 for idx = 0; idx < len(latch.waiting); idx++ { 221 waiting := latch.waiting[idx] 222 if bytes.Equal(waiting.keys[waiting.acquiredCount], key) { 223 break 224 } 225 } 226 // Wake up the first one in waiting queue. 227 if idx < len(latch.waiting) { 228 nextLock = latch.waiting[idx] 229 // Delete element latch.waiting[idx] from the array. 230 copy(latch.waiting[idx:], latch.waiting[idx+1:]) 231 latch.waiting[len(latch.waiting)-1] = nil 232 latch.waiting = latch.waiting[:len(latch.waiting)-1] 233 234 if find.maxCommitTS > nextLock.startTS { 235 find.value = nextLock 236 nextLock.acquiredCount++ 237 nextLock.isStale = true 238 } 239 } 240 241 return 242 } 243 244 func (latches *Latches) acquireSlot(lock *Lock) acquireResult { 245 key := lock.keys[lock.acquiredCount] 246 slotID := lock.requiredSlots[lock.acquiredCount] 247 latch := &latches.slots[slotID] 248 latch.Lock() 249 defer latch.Unlock() 250 251 // Try to recycle to limit the memory usage. 252 if latch.count >= latchListCount { 253 latch.recycle(lock.startTS) 254 } 255 256 find := findNode(latch.queue, key) 257 if find == nil { 258 tmp := &node{ 259 slotID: slotID, 260 key: key, 261 value: lock, 262 } 263 tmp.next = latch.queue 264 latch.queue = tmp 265 latch.count++ 266 267 lock.acquiredCount++ 268 return acquireSuccess 269 } 270 271 if find.maxCommitTS > lock.startTS { 272 lock.isStale = true 273 return acquireStale 274 } 275 276 if find.value == nil { 277 find.value = lock 278 lock.acquiredCount++ 279 return acquireSuccess 280 } 281 282 // Push the current transaction into waitingQueue. 283 latch.waiting = append(latch.waiting, lock) 284 return acquireLocked 285 } 286 287 // recycle is not thread safe, the latch should acquire its lock before executing this function. 288 func (l *latch) recycle(currentTS uint64) int { 289 total := 0 290 fakeHead := node{next: l.queue} 291 prev := &fakeHead 292 for curr := prev.next; curr != nil; curr = curr.next { 293 if tsoSub(currentTS, curr.maxCommitTS) >= expireDuration && curr.value == nil { 294 l.count-- 295 prev.next = curr.next 296 total++ 297 } else { 298 prev = curr 299 } 300 } 301 l.queue = fakeHead.next 302 return total 303 } 304 305 func (latches *Latches) recycle(currentTS uint64) { 306 total := 0 307 for i := 0; i < len(latches.slots); i++ { 308 latch := &latches.slots[i] 309 latch.Lock() 310 total += latch.recycle(currentTS) 311 latch.Unlock() 312 } 313 logutil.BgLogger().Debug("recycle", 314 zap.Time("start at", time.Now()), 315 zap.Int("count", total)) 316 } 317 318 func findNode(list *node, key []byte) *node { 319 for n := list; n != nil; n = n.next { 320 if bytes.Equal(n.key, key) { 321 return n 322 } 323 } 324 return nil 325 }