github.com/KinWaiYuen/client-go/v2@v2.5.4/internal/latch/scheduler.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/scheduler.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 "sync" 39 "time" 40 41 "github.com/KinWaiYuen/client-go/v2/oracle" 42 ) 43 44 const lockChanSize = 100 45 46 // LatchesScheduler is used to schedule latches for transactions. 47 type LatchesScheduler struct { 48 latches *Latches 49 unlockCh chan *Lock 50 closed bool 51 lastRecycleTime uint64 52 sync.RWMutex 53 } 54 55 // NewScheduler create the LatchesScheduler. 56 func NewScheduler(size uint) *LatchesScheduler { 57 latches := NewLatches(size) 58 unlockCh := make(chan *Lock, lockChanSize) 59 scheduler := &LatchesScheduler{ 60 latches: latches, 61 unlockCh: unlockCh, 62 closed: false, 63 } 64 go scheduler.run() 65 return scheduler 66 } 67 68 const expireDuration = 2 * time.Minute 69 const checkInterval = 1 * time.Minute 70 const checkCounter = 50000 71 const latchListCount = 5 72 73 func (scheduler *LatchesScheduler) run() { 74 var counter int 75 wakeupList := make([]*Lock, 0) 76 for lock := range scheduler.unlockCh { 77 wakeupList = scheduler.latches.release(lock, wakeupList) 78 if len(wakeupList) > 0 { 79 scheduler.wakeup(wakeupList) 80 } 81 82 if lock.commitTS > lock.startTS { 83 currentTS := lock.commitTS 84 elapsed := tsoSub(currentTS, scheduler.lastRecycleTime) 85 if elapsed > checkInterval || counter > checkCounter { 86 go scheduler.latches.recycle(lock.commitTS) 87 scheduler.lastRecycleTime = currentTS 88 counter = 0 89 } 90 } 91 counter++ 92 } 93 } 94 95 func (scheduler *LatchesScheduler) wakeup(wakeupList []*Lock) { 96 for _, lock := range wakeupList { 97 if scheduler.latches.acquire(lock) != acquireLocked { 98 lock.wg.Done() 99 } 100 } 101 } 102 103 // Close closes LatchesScheduler. 104 func (scheduler *LatchesScheduler) Close() { 105 scheduler.RWMutex.Lock() 106 defer scheduler.RWMutex.Unlock() 107 if !scheduler.closed { 108 close(scheduler.unlockCh) 109 scheduler.closed = true 110 } 111 } 112 113 // Lock acquire the lock for transaction with startTS and keys. The caller goroutine 114 // would be blocked if the lock can't be obtained now. When this function returns, 115 // the lock state would be either success or stale(call lock.IsStale) 116 func (scheduler *LatchesScheduler) Lock(startTS uint64, keys [][]byte) *Lock { 117 lock := scheduler.latches.genLock(startTS, keys) 118 lock.wg.Add(1) 119 if scheduler.latches.acquire(lock) == acquireLocked { 120 lock.wg.Wait() 121 } 122 if lock.isLocked() { 123 panic("should never run here") 124 } 125 return lock 126 } 127 128 // UnLock unlocks a lock. 129 func (scheduler *LatchesScheduler) UnLock(lock *Lock) { 130 scheduler.RLock() 131 defer scheduler.RUnlock() 132 if !scheduler.closed { 133 scheduler.unlockCh <- lock 134 } 135 } 136 137 func tsoSub(ts1, ts2 uint64) time.Duration { 138 t1 := oracle.GetTimeFromTS(ts1) 139 t2 := oracle.GetTimeFromTS(ts2) 140 return t1.Sub(t2) 141 }