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 }