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