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  }