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  }