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  }