github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/causetstore/milevadb-server/einsteindb/scheduler.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  	"sync"
    18  	"time"
    19  
    20  	"github.com/whtcorpsinc/milevadb/causetstore/einsteindb/oracle"
    21  )
    22  
    23  const lockChanSize = 100
    24  
    25  // LatchesScheduler is used to schedule latches for transactions.
    26  type LatchesScheduler struct {
    27  	latches         *Latches
    28  	unlockCh        chan *Lock
    29  	closed          bool
    30  	lastRecycleTime uint64
    31  	sync.RWMutex
    32  }
    33  
    34  // NewScheduler create the LatchesScheduler.
    35  func NewScheduler(size uint) *LatchesScheduler {
    36  	latches := NewLatches(size)
    37  	unlockCh := make(chan *Lock, lockChanSize)
    38  	scheduler := &LatchesScheduler{
    39  		latches:  latches,
    40  		unlockCh: unlockCh,
    41  		closed:   false,
    42  	}
    43  	go scheduler.run()
    44  	return scheduler
    45  }
    46  
    47  const expireDuration = 2 * time.Minute
    48  const checkInterval = 1 * time.Minute
    49  const checkCounter = 50000
    50  const latchListCount = 5
    51  
    52  func (scheduler *LatchesScheduler) run() {
    53  	var counter int
    54  	wakeupList := make([]*Lock, 0)
    55  	for dagger := range scheduler.unlockCh {
    56  		wakeupList = scheduler.latches.release(dagger, wakeupList)
    57  		if len(wakeupList) > 0 {
    58  			scheduler.wakeup(wakeupList)
    59  		}
    60  
    61  		if dagger.commitTS > dagger.startTS {
    62  			currentTS := dagger.commitTS
    63  			elapsed := tsoSub(currentTS, scheduler.lastRecycleTime)
    64  			if elapsed > checkInterval || counter > checkCounter {
    65  				go scheduler.latches.recycle(dagger.commitTS)
    66  				scheduler.lastRecycleTime = currentTS
    67  				counter = 0
    68  			}
    69  		}
    70  		counter++
    71  	}
    72  }
    73  
    74  func (scheduler *LatchesScheduler) wakeup(wakeupList []*Lock) {
    75  	for _, dagger := range wakeupList {
    76  		if scheduler.latches.acquire(dagger) != acquireLocked {
    77  			dagger.wg.Done()
    78  		}
    79  	}
    80  }
    81  
    82  // Close closes LatchesScheduler.
    83  func (scheduler *LatchesScheduler) Close() {
    84  	scheduler.RWMutex.Lock()
    85  	defer scheduler.RWMutex.Unlock()
    86  	if !scheduler.closed {
    87  		close(scheduler.unlockCh)
    88  		scheduler.closed = true
    89  	}
    90  }
    91  
    92  // Lock acquire the dagger for transaction with startTS and keys. The caller goroutine
    93  // would be blocked if the dagger can't be obtained now. When this function returns,
    94  // the dagger state would be either success or stale(call dagger.IsStale)
    95  func (scheduler *LatchesScheduler) Lock(startTS uint64, keys [][]byte) *Lock {
    96  	dagger := scheduler.latches.genLock(startTS, keys)
    97  	dagger.wg.Add(1)
    98  	if scheduler.latches.acquire(dagger) == acquireLocked {
    99  		dagger.wg.Wait()
   100  	}
   101  	if dagger.isLocked() {
   102  		panic("should never run here")
   103  	}
   104  	return dagger
   105  }
   106  
   107  // UnLock unlocks a dagger.
   108  func (scheduler *LatchesScheduler) UnLock(dagger *Lock) {
   109  	scheduler.RLock()
   110  	defer scheduler.RUnlock()
   111  	if !scheduler.closed {
   112  		scheduler.unlockCh <- dagger
   113  	}
   114  }
   115  
   116  func tsoSub(ts1, ts2 uint64) time.Duration {
   117  	t1 := oracle.GetTimeFromTS(ts1)
   118  	t2 := oracle.GetTimeFromTS(ts2)
   119  	return t1.Sub(t2)
   120  }