github.com/matrixorigin/matrixone@v0.7.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 "encoding/binary" 20 "io" 21 "sync" 22 23 "github.com/matrixorigin/matrixone/pkg/container/types" 24 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/common" 25 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/txnif" 26 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/wal" 27 ) 28 29 type MVCCChain struct { 30 *sync.RWMutex 31 MVCC *common.GenericSortedDList[txnif.MVCCNode] 32 comparefn func(txnif.MVCCNode, txnif.MVCCNode) int 33 newnodefn func() txnif.MVCCNode 34 } 35 36 func NewMVCCChain(comparefn func(txnif.MVCCNode, txnif.MVCCNode) int, newnodefn func() txnif.MVCCNode) *MVCCChain { 37 return &MVCCChain{ 38 MVCC: common.NewGenericSortedDList(comparefn), 39 RWMutex: &sync.RWMutex{}, 40 comparefn: comparefn, 41 newnodefn: newnodefn, 42 } 43 } 44 func (be *MVCCChain) Depth() int { 45 return be.MVCC.Depth() 46 } 47 func (be *MVCCChain) StringLocked() string { 48 var w bytes.Buffer 49 it := common.NewGenericSortedDListIt(nil, be.MVCC, false) 50 for it.Valid() { 51 version := it.Get().GetPayload() 52 _, _ = w.WriteString(" -> ") 53 _, _ = w.WriteString(version.String()) 54 it.Next() 55 } 56 return w.String() 57 } 58 59 // for replay 60 func (be *MVCCChain) GetPrepareTs() types.TS { 61 return be.GetLatestNodeLocked().GetPrepare() 62 } 63 64 func (be *MVCCChain) GetTxn() txnif.TxnReader { return be.GetLatestNodeLocked().GetTxn() } 65 66 func (be *MVCCChain) GetIndexes() []*wal.Index { 67 ret := make([]*wal.Index, 0) 68 be.MVCC.Loop(func(n *common.GenericDLNode[txnif.MVCCNode]) bool { 69 un := n.GetPayload() 70 ret = append(ret, un.GetLogIndex()) 71 return true 72 }, true) 73 return ret 74 } 75 76 func (be *MVCCChain) Insert(vun txnif.MVCCNode) (node *common.GenericDLNode[txnif.MVCCNode]) { 77 un := vun 78 node = be.MVCC.Insert(un) 79 return 80 } 81 82 // [start, end] 83 // Check whether there is any committed node in between [start, end] 84 // -----+------+-------+--------+----------+---------> 85 // 86 // | | | | | Time 87 // | start | end | 88 // commitTs <----- commitTs <--------- commitTs|uncommitted <= MVCCChain Header 89 // (1) (2) (3) 90 func (be *MVCCChain) HasCommittedNodeInRange(start, end types.TS) (ok bool) { 91 be.MVCC.Loop(func(n *common.GenericDLNode[txnif.MVCCNode]) bool { 92 un := n.GetPayload() 93 in, before := un.CommittedIn(start, end) 94 95 // case (2) 96 // A committed node is found. Stop the loop. 97 // Found 98 if in { 99 ok = true 100 return false 101 } 102 103 // case (3) 104 // Committed after the end ts or uncommitted. 105 // Go to prev node 106 // Not found 107 if !before { 108 return true 109 } 110 111 // case (1) 112 // Committed before the start ts. Stop the loop. 113 // Not found 114 return false 115 }, false) 116 return 117 } 118 119 func (be *MVCCChain) MustOneNodeLocked() (txnif.MVCCNode, bool) { 120 if be.MVCC.Depth() != 1 { 121 return nil, be.MVCC.Depth() == 0 122 } 123 return be.MVCC.GetHead().GetPayload(), false 124 } 125 126 // GetLatestNodeLocked gets the latest mvcc node. 127 // It is useful in making command, apply state(e.g. ApplyCommit), 128 // check confilct. 129 func (be *MVCCChain) GetLatestNodeLocked() txnif.MVCCNode { 130 head := be.MVCC.GetHead() 131 if head == nil { 132 return nil 133 } 134 payload := head.GetPayload() 135 if payload == nil { 136 return nil 137 } 138 entry := payload 139 return entry 140 } 141 142 // GetLatestCommittedNode gets the latest committed mvcc node. 143 // It's useful when check whether the catalog/metadata entry is deleted. 144 func (be *MVCCChain) GetLatestCommittedNode() (node txnif.MVCCNode) { 145 be.MVCC.Loop(func(n *common.GenericDLNode[txnif.MVCCNode]) bool { 146 un := n.GetPayload() 147 if !un.IsActive() && !un.IsCommitting() { 148 node = un 149 return false 150 } 151 return true 152 }, false) 153 return 154 } 155 156 // GetVisibleNode gets mvcc node according to the timestamp. 157 // It returns the mvcc node in the same txn as the read txn 158 // or returns the latest mvcc node with commitTS less than the timestamp. 159 func (be *MVCCChain) GetVisibleNode(ts types.TS) (node txnif.MVCCNode) { 160 be.MVCC.Loop(func(n *common.GenericDLNode[txnif.MVCCNode]) (goNext bool) { 161 un := n.GetPayload() 162 var visible bool 163 if visible = un.IsVisible(ts); visible { 164 node = un 165 } 166 goNext = !visible 167 return 168 }, false) 169 return 170 } 171 172 // It's only used in replay 173 func (be *MVCCChain) SearchNode(o txnif.MVCCNode) (node txnif.MVCCNode) { 174 be.MVCC.Loop(func(n *common.GenericDLNode[txnif.MVCCNode]) bool { 175 un := n.GetPayload() 176 compare := be.comparefn(un, o) 177 if compare == 0 { 178 node = un 179 return false 180 } 181 // return compare > 0 182 return true 183 }, false) 184 return 185 } 186 187 func (be *MVCCChain) LoopChain(fn func(txnif.MVCCNode) bool) { 188 be.MVCC.Loop(func(n *common.GenericDLNode[txnif.MVCCNode]) bool { 189 un := n.GetPayload() 190 return fn(un) 191 }, false) 192 } 193 194 func (be *MVCCChain) NeedWaitCommitting(ts types.TS) (bool, txnif.TxnReader) { 195 un := be.GetLatestNodeLocked() 196 if un == nil { 197 return false, nil 198 } 199 return un.NeedWaitCommitting(ts) 200 } 201 202 func (be *MVCCChain) HasUncommittedNode() bool { 203 var found bool 204 be.LoopChain(func(n txnif.MVCCNode) bool { 205 if n.IsCommitted() { 206 return false 207 } else { 208 if !n.IsAborted() { 209 found = true 210 } 211 } 212 return !found 213 }) 214 return found 215 } 216 217 func (be *MVCCChain) HasCommittedNode() bool { 218 var found bool 219 be.LoopChain(func(n txnif.MVCCNode) bool { 220 if n.IsCommitted() { 221 found = true 222 return false 223 } 224 return true 225 }) 226 return found 227 } 228 229 func (be *MVCCChain) IsCreatingOrAborted() bool { 230 un, empty := be.MustOneNodeLocked() 231 if empty { 232 return true 233 } 234 if un == nil { 235 return false 236 } 237 return un.IsActive() || un.IsAborted() 238 } 239 240 func (be *MVCCChain) CheckConflict(txn txnif.TxnReader) (err error) { 241 node := be.GetLatestNodeLocked() 242 err = node.CheckConflict(txn.GetStartTS()) 243 return 244 } 245 246 func (be *MVCCChain) WriteOneNodeTo(w io.Writer) (n int64, err error) { 247 var n2 int64 248 n2, err = be.GetLatestNodeLocked().WriteTo(w) 249 if err != nil { 250 return 251 } 252 n += n2 253 return 254 } 255 256 func (be *MVCCChain) WriteAllTo(w io.Writer) (n int64, err error) { 257 n += 8 258 if err = binary.Write(w, binary.BigEndian, uint64(be.MVCC.Depth())); err != nil { 259 return 260 } 261 n += 8 262 be.MVCC.Loop(func(node *common.GenericDLNode[txnif.MVCCNode]) bool { 263 var n2 int64 264 n2, err = node.GetPayload().WriteTo(w) 265 if err != nil { 266 return false 267 } 268 n += n2 269 return true 270 }, true) 271 return 272 } 273 274 func (be *MVCCChain) ReadOneNodeFrom(r io.Reader) (n int64, err error) { 275 var n2 int64 276 un := be.newnodefn() 277 n2, err = un.ReadFrom(r) 278 if err != nil { 279 return 280 } 281 be.Insert(un) 282 n += n2 283 return 284 } 285 286 func (be *MVCCChain) ReadAllFrom(r io.Reader) (n int64, err error) { 287 var depth uint64 288 if err = binary.Read(r, binary.BigEndian, &depth); err != nil { 289 return 290 } 291 n += 8 292 for i := 0; i < int(depth); i++ { 293 var n2 int64 294 un := be.newnodefn() 295 n2, err = un.ReadFrom(r) 296 if err != nil { 297 return 298 } 299 be.MVCC.Insert(un) 300 n += n2 301 } 302 return 303 } 304 305 func (be *MVCCChain) IsEmpty() bool { 306 head := be.MVCC.GetHead() 307 return head == nil 308 } 309 310 func (be *MVCCChain) ApplyRollback(index *wal.Index) error { 311 be.Lock() 312 defer be.Unlock() 313 return be.GetLatestNodeLocked().ApplyRollback(index) 314 315 } 316 317 func (be *MVCCChain) ApplyCommit(index *wal.Index) error { 318 be.Lock() 319 defer be.Unlock() 320 return be.GetLatestNodeLocked().ApplyCommit(index) 321 } 322 323 func (be *MVCCChain) Apply1PCCommit(index *wal.Index) error { 324 be.Lock() 325 defer be.Unlock() 326 return be.GetLatestNodeLocked().ApplyCommit(index) 327 } 328 329 func (be *MVCCChain) GetLogIndex() *wal.Index { 330 node := be.GetLatestNodeLocked() 331 if node == nil { 332 return nil 333 } 334 return node.GetLogIndex() 335 } 336 337 func (be *MVCCChain) CloneLatestNode() (*MVCCChain, txnif.MVCCNode) { 338 cloned := &MVCCChain{ 339 MVCC: common.NewGenericSortedDList(be.comparefn), 340 RWMutex: &sync.RWMutex{}, 341 } 342 un := be.GetLatestNodeLocked() 343 uncloned := un.CloneData() 344 cloned.Insert(uncloned) 345 return cloned, uncloned 346 } 347 348 // In /Catalog, there're three states: Active, Committing and Committed. 349 // A txn is Active before its CommitTs is allocated. 350 // It's Committed when its state will never change, i.e. TxnStateCommitted and TxnStateRollbacked. 351 // It's Committing when it's in any other state, including TxnStateCommitting, TxnStateRollbacking, TxnStatePrepared and so on. When read or write an entry, if the last txn of the entry is Committing, we wait for it. When write on an Entry, if there's an Active txn, we report w-w conflict. 352 func (be *MVCCChain) IsCommitting() bool { 353 node := be.GetLatestNodeLocked() 354 if node == nil { 355 return false 356 } 357 return node.IsCommitting() 358 } 359 360 func (be *MVCCChain) PrepareCommit() error { 361 be.Lock() 362 defer be.Unlock() 363 return be.GetLatestNodeLocked().PrepareCommit() 364 } 365 366 func (be *MVCCChain) PrepareRollback() (bool, error) { 367 be.Lock() 368 defer be.Unlock() 369 node := be.MVCC.GetHead() 370 _ = node.GetPayload().PrepareRollback() 371 be.MVCC.Delete(node) 372 isEmpty := be.IsEmpty() 373 return isEmpty, nil 374 } 375 376 func (be *MVCCChain) IsCommitted() bool { 377 un := be.GetLatestNodeLocked() 378 if un == nil { 379 return false 380 } 381 return un.IsCommitted() 382 } 383 384 func (be *MVCCChain) CloneCommittedInRange(start, end types.TS) (ret *MVCCChain) { 385 needWait, txn := be.NeedWaitCommitting(end.Next()) 386 if needWait { 387 be.RUnlock() 388 txn.GetTxnState(true) 389 be.RLock() 390 } 391 be.MVCC.Loop(func(n *common.GenericDLNode[txnif.MVCCNode]) bool { 392 un := n.GetPayload() 393 in, before := un.CommittedIn(start, end) 394 if in { 395 if ret == nil { 396 ret = NewMVCCChain(be.comparefn, be.newnodefn) 397 } 398 ret.Insert(un.CloneAll()) 399 } else if !before { 400 return false 401 } 402 return true 403 }, true) 404 return 405 } 406 407 func (be *MVCCChain) ClonePreparedInRange(start, end types.TS) (ret []txnif.MVCCNode) { 408 needWait, txn := be.NeedWaitCommitting(end.Next()) 409 if needWait { 410 be.RUnlock() 411 txn.GetTxnState(true) 412 be.RLock() 413 } 414 be.MVCC.Loop(func(n *common.GenericDLNode[txnif.MVCCNode]) bool { 415 un := n.GetPayload() 416 in, before := un.PreparedIn(start, end) 417 if in { 418 if ret == nil { 419 ret = make([]txnif.MVCCNode, 0) 420 } 421 ret = append(ret, un.CloneAll()) 422 } else if !before { 423 return false 424 } 425 return true 426 }, true) 427 return 428 }