github.com/matrixorigin/matrixone@v0.7.0/pkg/lockservice/service.go (about)

     1  // Copyright 2022 Matrix Origin
     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  package lockservice
    16  
    17  import (
    18  	"context"
    19  	"sync"
    20  	"unsafe"
    21  )
    22  
    23  type activeTxnHolder interface {
    24  	getActiveTxn(txnID []byte, create bool) *activeTxn
    25  	deleteActiveTxn(txnID []byte) *activeTxn
    26  }
    27  
    28  type mapBasedTxnHolder struct {
    29  	fsp *fixedSlicePool
    30  	mu  struct {
    31  		sync.RWMutex
    32  		activeTxns map[string]*activeTxn
    33  	}
    34  }
    35  
    36  func newMapBasedTxnHandler(fsp *fixedSlicePool) activeTxnHolder {
    37  	h := &mapBasedTxnHolder{}
    38  	h.fsp = fsp
    39  	h.mu.activeTxns = make(map[string]*activeTxn)
    40  	return h
    41  }
    42  
    43  func (h *mapBasedTxnHolder) getActiveTxn(
    44  	txnID []byte,
    45  	create bool) *activeTxn {
    46  	txnKey := unsafeByteSliceToString(txnID)
    47  	h.mu.RLock()
    48  	if v, ok := h.mu.activeTxns[txnKey]; ok {
    49  		h.mu.RUnlock()
    50  		return v
    51  	}
    52  	h.mu.RUnlock()
    53  	if !create {
    54  		return nil
    55  	}
    56  
    57  	h.mu.Lock()
    58  	defer h.mu.Unlock()
    59  	txn := newActiveTxn(txnID, txnKey, h.fsp)
    60  	h.mu.activeTxns[txnKey] = txn
    61  	return txn
    62  }
    63  
    64  func (h *mapBasedTxnHolder) deleteActiveTxn(txnID []byte) *activeTxn {
    65  	txnKey := unsafeByteSliceToString(txnID)
    66  	h.mu.Lock()
    67  	defer h.mu.Unlock()
    68  	v, ok := h.mu.activeTxns[txnKey]
    69  	if ok {
    70  		delete(h.mu.activeTxns, txnKey)
    71  	}
    72  	return v
    73  }
    74  
    75  type service struct {
    76  	tables sync.Map // tableid -> *locktable
    77  	// activeTxn        sync.Map // txnid -> *activeTxns
    78  	activeTxns       activeTxnHolder
    79  	fsp              *fixedSlicePool
    80  	deadlockDetector *detector
    81  }
    82  
    83  func NewLockService() LockService {
    84  	l := &service{
    85  		// FIXME: config
    86  		fsp: newFixedSlicePool(1024 * 1024),
    87  	}
    88  	l.activeTxns = newMapBasedTxnHandler(l.fsp)
    89  	l.deadlockDetector = newDeadlockDetector(l.fetchTxnWaitingList,
    90  		l.abortDeadlockTxn)
    91  	return l
    92  }
    93  
    94  func (s *service) Lock(
    95  	ctx context.Context,
    96  	tableID uint64,
    97  	rows [][]byte,
    98  	txnID []byte,
    99  	options LockOptions) error {
   100  	txn := s.activeTxns.getActiveTxn(txnID, true)
   101  	l := s.getLockTable(tableID)
   102  	if err := l.lock(ctx, txn, rows, options); err != nil {
   103  		return err
   104  	}
   105  	return nil
   106  }
   107  
   108  func (s *service) Unlock(txnID []byte) error {
   109  	txn := s.activeTxns.deleteActiveTxn(txnID)
   110  	if txn == nil {
   111  		return nil
   112  	}
   113  	txn.close(s.getLockTable)
   114  	// The deadlock detector will hold the deadlocked transaction that is aborted
   115  	// to avoid the situation where the deadlock detection is interfered with by
   116  	// the abort transaction. When a transaction is unlocked, the deadlock detector
   117  	// needs to be notified to release memory.
   118  	s.deadlockDetector.txnClosed(txnID)
   119  	return nil
   120  }
   121  
   122  func (s *service) Close() error {
   123  	s.deadlockDetector.close()
   124  	return nil
   125  }
   126  
   127  func (s *service) fetchTxnWaitingList(txnID []byte, waiters *waiters) bool {
   128  	txn := s.activeTxns.getActiveTxn(txnID, false)
   129  	// the active txn closed
   130  	if txn == nil {
   131  		return true
   132  	}
   133  
   134  	return txn.fetchWhoWaitingMe(txnID, waiters, s.getLockTable)
   135  }
   136  
   137  func (s *service) abortDeadlockTxn(txnID []byte) {
   138  	txn := s.activeTxns.getActiveTxn(txnID, false)
   139  	// the active txn closed
   140  	if txn == nil {
   141  		return
   142  	}
   143  	txn.abort(txnID)
   144  }
   145  
   146  func (s *service) getLockTable(tableID uint64) *lockTable {
   147  	if v, ok := s.tables.Load(tableID); ok {
   148  		return v.(*lockTable)
   149  	}
   150  
   151  	l := newLockTable(tableID, s.deadlockDetector)
   152  	if v, loaded := s.tables.LoadOrStore(tableID, l); loaded {
   153  		return v.(*lockTable)
   154  	}
   155  	return l
   156  }
   157  
   158  func unsafeByteSliceToString(key []byte) string {
   159  	return *(*string)(unsafe.Pointer(&key))
   160  }