github.com/matrixorigin/matrixone@v1.2.0/pkg/vm/engine/tae/txn/txnbase/txnctx.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  	"fmt"
    19  	"sync"
    20  
    21  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    22  	"github.com/matrixorigin/matrixone/pkg/container/types"
    23  
    24  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/txnif"
    25  )
    26  
    27  func IDToIDCtx(id uint64) []byte {
    28  	ctx := make([]byte, 8)
    29  	copy(ctx, types.EncodeUint64(&id))
    30  	return ctx
    31  }
    32  
    33  func IDCtxToID(buf []byte) string {
    34  	return string(buf)
    35  }
    36  
    37  type TxnCtx struct {
    38  	sync.RWMutex
    39  	sync.WaitGroup
    40  	DoneCond                     sync.Cond
    41  	ID                           string
    42  	IDCtx                        []byte
    43  	StartTS, CommitTS, PrepareTS types.TS
    44  
    45  	// SnapshotTS is the specified snapshot timestamp used by this txn
    46  	SnapshotTS types.TS
    47  
    48  	State        txnif.TxnState
    49  	Participants []uint64
    50  
    51  	// Memo is not thread-safe
    52  	// It will be readonly when txn state is not txnif.TxnStateActive
    53  	Memo *txnif.TxnMemo
    54  }
    55  
    56  func NewTxnCtx(id []byte, start, snapshot types.TS) *TxnCtx {
    57  	if snapshot.IsEmpty() {
    58  		snapshot = start
    59  	}
    60  	ctx := &TxnCtx{
    61  		ID:         string(id),
    62  		IDCtx:      id,
    63  		StartTS:    start,
    64  		PrepareTS:  txnif.UncommitTS,
    65  		CommitTS:   txnif.UncommitTS,
    66  		SnapshotTS: snapshot,
    67  		Memo:       txnif.NewTxnMemo(),
    68  	}
    69  	ctx.DoneCond = *sync.NewCond(ctx)
    70  	return ctx
    71  }
    72  
    73  func NewEmptyTxnCtx() *TxnCtx {
    74  	ctx := &TxnCtx{
    75  		Memo: txnif.NewTxnMemo(),
    76  	}
    77  	ctx.DoneCond = *sync.NewCond(ctx)
    78  	return ctx
    79  }
    80  
    81  func (ctx *TxnCtx) IsReplay() bool { return false }
    82  func (ctx *TxnCtx) GetMemo() *txnif.TxnMemo {
    83  	return ctx.Memo
    84  }
    85  
    86  func (ctx *TxnCtx) Is2PC() bool { return len(ctx.Participants) > 1 }
    87  
    88  func (ctx *TxnCtx) GetCtx() []byte {
    89  	return ctx.IDCtx
    90  }
    91  
    92  func (ctx *TxnCtx) Repr() string {
    93  	ctx.RLock()
    94  	defer ctx.RUnlock()
    95  	if ctx.HasSnapshotLag() {
    96  		return fmt.Sprintf(
    97  			"ctx[%X][%s->%s->%s][%s]",
    98  			ctx.ID,
    99  			ctx.SnapshotTS.ToString(),
   100  			ctx.StartTS.ToString(),
   101  			ctx.PrepareTS.ToString(),
   102  			txnif.TxnStrState(ctx.State),
   103  		)
   104  	}
   105  	return fmt.Sprintf(
   106  		"ctx[%X][%s->%s][%s]",
   107  		ctx.ID,
   108  		ctx.StartTS.ToString(),
   109  		ctx.PrepareTS.ToString(),
   110  		txnif.TxnStrState(ctx.State),
   111  	)
   112  }
   113  
   114  func (ctx *TxnCtx) SameTxn(txn txnif.TxnReader) bool { return ctx.ID == txn.GetID() }
   115  func (ctx *TxnCtx) CommitBefore(startTs types.TS) bool {
   116  	commitTS := ctx.GetCommitTS()
   117  	return commitTS.Less(&startTs)
   118  }
   119  func (ctx *TxnCtx) CommitAfter(startTs types.TS) bool {
   120  	commitTS := ctx.GetCommitTS()
   121  	return commitTS.Greater(&startTs)
   122  }
   123  
   124  func (ctx *TxnCtx) String() string            { return ctx.Repr() }
   125  func (ctx *TxnCtx) GetID() string             { return ctx.ID }
   126  func (ctx *TxnCtx) HasSnapshotLag() bool      { return ctx.SnapshotTS.Less(&ctx.StartTS) }
   127  func (ctx *TxnCtx) GetSnapshotTS() types.TS   { return ctx.SnapshotTS }
   128  func (ctx *TxnCtx) SetSnapshotTS(ts types.TS) { ctx.SnapshotTS = ts }
   129  func (ctx *TxnCtx) GetStartTS() types.TS      { return ctx.StartTS }
   130  func (ctx *TxnCtx) GetCommitTS() types.TS {
   131  	ctx.RLock()
   132  	defer ctx.RUnlock()
   133  	return ctx.CommitTS
   134  }
   135  
   136  // test only
   137  // Note: unsafe
   138  func (ctx *TxnCtx) MockStartTS(ts types.TS) {
   139  	ctx.StartTS = ts
   140  }
   141  
   142  func (ctx *TxnCtx) SetCommitTS(cts types.TS) (err error) {
   143  	ctx.RLock()
   144  	defer ctx.RUnlock()
   145  	ctx.CommitTS = cts
   146  	return
   147  }
   148  
   149  func (ctx *TxnCtx) GetParticipants() []uint64 {
   150  	ctx.RLock()
   151  	defer ctx.RUnlock()
   152  	return ctx.Participants
   153  }
   154  
   155  func (ctx *TxnCtx) SetParticipants(ids []uint64) (err error) {
   156  	ctx.RLock()
   157  	defer ctx.RUnlock()
   158  	ctx.Participants = ids
   159  	return
   160  }
   161  
   162  func (ctx *TxnCtx) GetPrepareTS() types.TS {
   163  	ctx.RLock()
   164  	defer ctx.RUnlock()
   165  	return ctx.PrepareTS
   166  }
   167  
   168  // Atomically returns the current txn state
   169  func (ctx *TxnCtx) getTxnState() txnif.TxnState {
   170  	ctx.RLock()
   171  	defer ctx.RUnlock()
   172  	return ctx.State
   173  }
   174  
   175  // Wait txn state to be rollbacked or committed
   176  func (ctx *TxnCtx) resolveTxnState() txnif.TxnState {
   177  	ctx.DoneCond.L.Lock()
   178  	defer ctx.DoneCond.L.Unlock()
   179  	state := ctx.State
   180  	//if state != txnif.TxnStatePreparing {
   181  	if state == txnif.TxnStateActive ||
   182  		state == txnif.TxnStateRollbacked ||
   183  		state == txnif.TxnStateCommitted {
   184  		return state
   185  	}
   186  	ctx.DoneCond.Wait()
   187  	return ctx.State
   188  }
   189  
   190  // False when atomically get the current txn state
   191  //
   192  // True when the txn state is committing, wait it to be committed or rollbacked. It
   193  // is used during snapshot reads. If TxnStateActive is currently returned, this value will
   194  // definitely not be used, because even if it becomes TxnStatePreparing later, the timestamp
   195  // would be larger than the current read timestamp.
   196  func (ctx *TxnCtx) GetTxnState(waitIfCommitting bool) (state txnif.TxnState) {
   197  	// Quick get the current txn state
   198  	// If waitIfCommitting is false, return the state
   199  	// If state is not txnif.TxnStatePreparing, return the state
   200  
   201  	//if state = ctx.getTxnState(); !waitIfCommitting || state != txnif.TxnStatePreparing {
   202  	if state = ctx.getTxnState(); !waitIfCommitting ||
   203  		state == txnif.TxnStateActive ||
   204  		state == txnif.TxnStateCommitted ||
   205  		state == txnif.TxnStateRollbacked {
   206  		return state
   207  	}
   208  
   209  	// Wait the committing txn to be committed or rollbacked
   210  	state = ctx.resolveTxnState()
   211  	return
   212  }
   213  
   214  func (ctx *TxnCtx) IsVisible(o txnif.TxnReader) bool {
   215  	ostart := o.GetStartTS()
   216  	ctx.RLock()
   217  	defer ctx.RUnlock()
   218  	return ostart.LessEq(&ctx.StartTS)
   219  }
   220  
   221  func (ctx *TxnCtx) IsActiveLocked() bool {
   222  	return ctx.CommitTS == txnif.UncommitTS
   223  }
   224  
   225  func (ctx *TxnCtx) ToPreparingLocked(ts types.TS) error {
   226  	if ts.LessEq(&ctx.StartTS) {
   227  		panic(fmt.Sprintf("start ts %d should be less than commit ts %d", ctx.StartTS, ts))
   228  	}
   229  	// if !ctx.CommitTS.Equal(txnif.UncommitTS) {
   230  	// 	return moerr.NewTxnNotActive("")
   231  	// }
   232  	ctx.PrepareTS = ts
   233  	ctx.CommitTS = ts
   234  	ctx.State = txnif.TxnStatePreparing
   235  	return nil
   236  }
   237  
   238  func (ctx *TxnCtx) ToPrepared() (err error) {
   239  	ctx.Lock()
   240  	defer ctx.Unlock()
   241  	return ctx.ToPreparedLocked()
   242  }
   243  
   244  func (ctx *TxnCtx) ToPreparedLocked() (err error) {
   245  	if ctx.State != txnif.TxnStatePreparing {
   246  		err = moerr.NewTAEPrepareNoCtx("ToPreparedLocked: state is not preparing")
   247  		return
   248  	}
   249  	ctx.State = txnif.TxnStatePrepared
   250  	return
   251  }
   252  
   253  func (ctx *TxnCtx) ToCommittingFinished() (err error) {
   254  	ctx.Lock()
   255  	defer ctx.Unlock()
   256  	return ctx.ToCommittingFinishedLocked()
   257  }
   258  
   259  func (ctx *TxnCtx) ToCommittingFinishedLocked() (err error) {
   260  	if ctx.State != txnif.TxnStatePrepared {
   261  		err = moerr.NewTAECommitNoCtx("ToCommittingFinishedLocked: state is not prepared")
   262  		return
   263  	}
   264  	ctx.State = txnif.TxnStateCommittingFinished
   265  	return
   266  }
   267  
   268  func (ctx *TxnCtx) ToCommittedLocked() error {
   269  	if ctx.Is2PC() {
   270  		if ctx.State != txnif.TxnStateCommittingFinished &&
   271  			ctx.State != txnif.TxnStatePrepared {
   272  			return moerr.NewTAECommitNoCtx("ToCommittedLocked: 2PC txn's state " +
   273  				"is not Prepared or CommittingFinished")
   274  		}
   275  		ctx.State = txnif.TxnStateCommitted
   276  		return nil
   277  	}
   278  	if ctx.State != txnif.TxnStatePreparing {
   279  		return moerr.NewTAECommitNoCtx("ToCommittedLocked: 1PC txn's state is not preparing")
   280  	}
   281  	ctx.State = txnif.TxnStateCommitted
   282  	return nil
   283  }
   284  
   285  func (ctx *TxnCtx) ToRollbacking(ts types.TS) error {
   286  	ctx.Lock()
   287  	defer ctx.Unlock()
   288  	return ctx.ToRollbackingLocked(ts)
   289  }
   290  
   291  func (ctx *TxnCtx) ToRollbackingLocked(ts types.TS) error {
   292  	if ts.Less(&ctx.StartTS) {
   293  		panic(fmt.Sprintf("commit ts %d should not be less than start ts %d", ts, ctx.StartTS))
   294  	}
   295  	if (ctx.State != txnif.TxnStateActive) && (ctx.State != txnif.TxnStatePreparing) {
   296  		return moerr.NewTAERollbackNoCtx("ToRollbackingLocked: state is not active or preparing")
   297  	}
   298  	ctx.CommitTS = ts
   299  	ctx.PrepareTS = ts
   300  	ctx.State = txnif.TxnStateRollbacking
   301  	return nil
   302  }
   303  
   304  func (ctx *TxnCtx) ToRollbackedLocked() error {
   305  	if ctx.Is2PC() {
   306  		if ctx.State != txnif.TxnStatePrepared &&
   307  			ctx.State != txnif.TxnStateRollbacking {
   308  			return moerr.NewTAERollbackNoCtx("state %s", txnif.TxnStrState(ctx.State))
   309  		}
   310  		ctx.State = txnif.TxnStateRollbacked
   311  		return nil
   312  	}
   313  	if ctx.State != txnif.TxnStateRollbacking {
   314  		return moerr.NewTAERollbackNoCtx("state %s", txnif.TxnStrState(ctx.State))
   315  	}
   316  	ctx.State = txnif.TxnStateRollbacked
   317  	return nil
   318  }
   319  
   320  func (ctx *TxnCtx) ToUnknownLocked() {
   321  	ctx.State = txnif.TxnStateUnknown
   322  }