github.com/matrixorigin/matrixone@v1.2.0/pkg/vm/engine/tae/txn/txnbase/mvccchain.go (about) 1 // Copyright 2021 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 txnbase 16 17 import ( 18 "bytes" 19 "sync" 20 21 "github.com/matrixorigin/matrixone/pkg/container/types" 22 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/common" 23 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/txnif" 24 ) 25 26 type MVCCChain[T txnif.MVCCNode[T]] struct { 27 *sync.RWMutex 28 MVCC *common.GenericSortedDList[T] 29 comparefn func(T, T) int 30 newnodefn func() T 31 zero T 32 } 33 34 func NewMVCCChain[T txnif.MVCCNode[T]](comparefn func(T, T) int, newnodefn func() T, rwlocker *sync.RWMutex) *MVCCChain[T] { 35 if rwlocker == nil { 36 rwlocker = new(sync.RWMutex) 37 } 38 return &MVCCChain[T]{ 39 MVCC: common.NewGenericSortedDList(comparefn), 40 RWMutex: rwlocker, 41 comparefn: comparefn, 42 newnodefn: newnodefn, 43 } 44 } 45 func (be *MVCCChain[T]) Depth() int { 46 return be.MVCC.Depth() 47 } 48 func (be *MVCCChain[T]) StringLocked() string { 49 var w bytes.Buffer 50 it := common.NewGenericSortedDListIt(nil, be.MVCC, false) 51 for it.Valid() { 52 version := it.Get().GetPayload() 53 _, _ = w.WriteString(" -> \n") 54 _, _ = w.WriteString(version.String()) 55 it.Next() 56 } 57 return w.String() 58 } 59 60 // for replay 61 func (be *MVCCChain[T]) GetPrepareTs() types.TS { 62 return be.GetLatestNodeLocked().GetPrepare() 63 } 64 65 func (be *MVCCChain[T]) GetTxn() txnif.TxnReader { return be.GetLatestNodeLocked().GetTxn() } 66 67 func (be *MVCCChain[T]) Insert(vun T) (node *common.GenericDLNode[T]) { 68 un := vun 69 node = be.MVCC.Insert(un) 70 return 71 } 72 73 // [start, end] 74 // Check whether there is any committed node in between [start, end] 75 // -----+------+-------+--------+----------+---------> 76 // 77 // | | | | | Time 78 // | start | end | 79 // commitTs <----- commitTs <--------- commitTs|uncommitted <= MVCCChain Header 80 // (1) (2) (3) 81 func (be *MVCCChain[T]) HasCommittedNodeInRange(start, end types.TS) (ok bool) { 82 be.MVCC.Loop(func(n *common.GenericDLNode[T]) bool { 83 un := n.GetPayload() 84 in, before := un.CommittedIn(start, end) 85 86 // case (2) 87 // A committed node is found. Stop the loop. 88 // Found 89 if in { 90 ok = true 91 return false 92 } 93 94 // case (3) 95 // Committed after the end ts or uncommitted. 96 // Go to prev node 97 // Not found 98 if !before { 99 return true 100 } 101 102 // case (1) 103 // Committed before the start ts. Stop the loop. 104 // Not found 105 return false 106 }, false) 107 return 108 } 109 110 func (be *MVCCChain[T]) MustOneNodeLocked() (T, bool) { 111 var nilT T 112 if be.MVCC.Depth() != 1 { 113 return nilT, be.MVCC.Depth() == 0 114 } 115 return be.MVCC.GetHead().GetPayload(), false 116 } 117 118 // GetLatestNodeLocked gets the latest mvcc node. 119 // It is useful in making command, apply state(e.g. ApplyCommit), 120 // check confilct. 121 func (be *MVCCChain[T]) GetLatestNodeLocked() T { 122 head := be.MVCC.GetHead() 123 if head == nil { 124 return be.zero 125 } 126 payload := head.GetPayload() 127 if payload.IsNil() { 128 return be.zero 129 } 130 entry := payload 131 return entry 132 } 133 134 // GetLatestCommittedNode gets the latest committed mvcc node. 135 // It's useful when check whether the catalog/metadata entry is deleted. 136 func (be *MVCCChain[T]) GetLatestCommittedNodeLocked() (node T) { 137 be.MVCC.Loop(func(n *common.GenericDLNode[T]) bool { 138 un := n.GetPayload() 139 if !un.IsActive() && !un.IsCommitting() { 140 node = un 141 return false 142 } 143 return true 144 }, false) 145 return 146 } 147 148 // GetVisibleNode gets mvcc node according to the txnReader. 149 // It returns the mvcc node in the same txn as the read txn 150 // or returns the latest mvcc node with commitTS less than the timestamp. 151 func (be *MVCCChain[T]) GetVisibleNodeLocked(txn txnif.TxnReader) (node T) { 152 be.MVCC.Loop(func(n *common.GenericDLNode[T]) (goNext bool) { 153 un := n.GetPayload() 154 var visible bool 155 if visible = un.IsVisible(txn); visible { 156 node = un 157 } 158 goNext = !visible 159 return 160 }, false) 161 return 162 } 163 164 // It's only used in replay 165 func (be *MVCCChain[T]) SearchNodeLocked(o T) (node T) { 166 be.MVCC.Loop(func(n *common.GenericDLNode[T]) bool { 167 un := n.GetPayload() 168 compare := be.comparefn(un, o) 169 if compare == 0 { 170 node = un 171 return false 172 } 173 // return compare > 0 174 return true 175 }, false) 176 return 177 } 178 179 func (be *MVCCChain[T]) LoopChainLocked(fn func(T) bool) { 180 be.MVCC.Loop(func(n *common.GenericDLNode[T]) bool { 181 un := n.GetPayload() 182 return fn(un) 183 }, false) 184 } 185 186 func (be *MVCCChain[T]) NeedWaitCommittingLocked(ts types.TS) (bool, txnif.TxnReader) { 187 un := be.GetLatestNodeLocked() 188 if un.IsNil() { 189 return false, nil 190 } 191 return un.NeedWaitCommitting(ts) 192 } 193 194 func (be *MVCCChain[T]) HasUncommittedNodeLocked() bool { 195 var found bool 196 be.LoopChainLocked(func(n T) bool { 197 if n.IsCommitted() { 198 return false 199 } else { 200 if !n.IsAborted() { 201 found = true 202 } 203 } 204 return !found 205 }) 206 return found 207 } 208 209 func (be *MVCCChain[T]) HasCommittedNodeLocked() bool { 210 var found bool 211 be.LoopChainLocked(func(n T) bool { 212 if n.IsCommitted() { 213 found = true 214 return false 215 } 216 return true 217 }) 218 return found 219 } 220 221 func (be *MVCCChain[T]) IsCreatingOrAborted() bool { 222 un, empty := be.MustOneNodeLocked() 223 if empty { 224 return true 225 } 226 if un.IsNil() { 227 return false 228 } 229 return un.IsActive() || un.IsAborted() 230 } 231 232 func (be *MVCCChain[T]) CheckConflictLocked(txn txnif.TxnReader) (err error) { 233 if be.IsEmptyLocked() { 234 return 235 } 236 node := be.GetLatestNodeLocked() 237 err = node.CheckConflict(txn) 238 return 239 } 240 241 func (be *MVCCChain[T]) IsEmptyLocked() bool { 242 head := be.MVCC.GetHead() 243 return head == nil 244 } 245 246 func (be *MVCCChain[T]) ApplyRollback() error { 247 be.Lock() 248 defer be.Unlock() 249 return be.GetLatestNodeLocked().ApplyRollback() 250 251 } 252 253 func (be *MVCCChain[T]) ApplyCommit() error { 254 be.Lock() 255 defer be.Unlock() 256 return be.GetLatestNodeLocked().ApplyCommit() 257 } 258 259 func (be *MVCCChain[T]) Apply1PCCommit() error { 260 be.Lock() 261 defer be.Unlock() 262 return be.GetLatestNodeLocked().ApplyCommit() 263 } 264 265 func (be *MVCCChain[T]) PrepareCommit() error { 266 be.Lock() 267 defer be.Unlock() 268 return be.GetLatestNodeLocked().PrepareCommit() 269 } 270 271 func (be *MVCCChain[T]) PrepareRollback() (bool, error) { 272 be.Lock() 273 defer be.Unlock() 274 node := be.MVCC.GetHead() 275 _ = node.GetPayload().PrepareRollback() 276 be.MVCC.Delete(node) 277 isEmpty := be.IsEmptyLocked() 278 return isEmpty, nil 279 } 280 281 func (be *MVCCChain[T]) IsCommittedLocked() bool { 282 un := be.GetLatestNodeLocked() 283 if un.IsNil() { 284 return false 285 } 286 return un.IsCommitted() 287 } 288 289 func (be *MVCCChain[T]) IsCommitted() bool { 290 be.RLock() 291 defer be.RUnlock() 292 un := be.GetLatestNodeLocked() 293 if un.IsNil() { 294 return false 295 } 296 return un.IsCommitted() 297 } 298 299 func (be *MVCCChain[T]) ClonePreparedInRange(start, end types.TS) (ret []T) { 300 be.RLock() 301 defer be.RUnlock() 302 return be.ClonePreparedInRangeLocked(start, end) 303 } 304 305 // ClonePreparedInRange will collect all txn node prepared in the time window. 306 // Wait txn to complete committing if it didn't. 307 func (be *MVCCChain[T]) ClonePreparedInRangeLocked(start, end types.TS) (ret []T) { 308 needWait, txn := be.NeedWaitCommittingLocked(end.Next()) 309 if needWait { 310 be.RUnlock() 311 txn.GetTxnState(true) 312 be.RLock() 313 } 314 be.MVCC.Loop(func(n *common.GenericDLNode[T]) bool { 315 un := n.GetPayload() 316 in, before := un.PreparedIn(start, end) 317 if in { 318 if ret == nil { 319 ret = make([]T, 0) 320 } 321 ret = append(ret, un.CloneAll()) 322 } else if !before { 323 return false 324 } 325 return true 326 }, true) 327 return 328 }