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  }