github.com/matrixorigin/matrixone@v1.2.0/pkg/vm/engine/tae/txn/txnbase/txn.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  	"context"
    19  	"fmt"
    20  	"runtime/trace"
    21  	"sync/atomic"
    22  
    23  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/logstore/entry"
    24  
    25  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    26  	"github.com/matrixorigin/matrixone/pkg/container/types"
    27  	"github.com/matrixorigin/matrixone/pkg/logutil"
    28  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/common"
    29  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/handle"
    30  	"github.com/matrixorigin/matrixone/pkg/vm/engine/tae/iface/txnif"
    31  )
    32  
    33  type OpType int8
    34  
    35  const (
    36  	OpCommit = iota
    37  	OpRollback
    38  	OpPrepare
    39  	OpCommitting
    40  	OpInvalid
    41  )
    42  
    43  const (
    44  	EventRollback = iota + 1
    45  	EventCommitting
    46  	EventCommit
    47  )
    48  
    49  type OpTxn struct {
    50  	ctx context.Context
    51  	Txn txnif.AsyncTxn
    52  	Op  OpType
    53  }
    54  
    55  func (txn *OpTxn) IsReplay() bool { return txn.Txn.IsReplay() }
    56  func (txn *OpTxn) Is2PC() bool    { return txn.Txn.Is2PC() }
    57  func (txn *OpTxn) IsTryCommitting() bool {
    58  	return txn.Op == OpCommit || txn.Op == OpPrepare
    59  }
    60  
    61  func (txn *OpTxn) Repr() string {
    62  	if txn.Op == OpCommit {
    63  		return fmt.Sprintf("[Commit][Txn-%X]", txn.Txn.GetID())
    64  	} else {
    65  		return fmt.Sprintf("[Rollback][Txn-%X]", txn.Txn.GetID())
    66  	}
    67  }
    68  
    69  var DefaultTxnFactory = func(
    70  	mgr *TxnManager,
    71  	store txnif.TxnStore,
    72  	id []byte,
    73  	startTS types.TS,
    74  	snapshotTS types.TS) txnif.AsyncTxn {
    75  	return NewTxn(mgr, store, id, startTS, snapshotTS)
    76  }
    77  
    78  type Txn struct {
    79  	*TxnCtx
    80  	Mgr                      *TxnManager
    81  	Store                    txnif.TxnStore
    82  	Err                      error
    83  	LSN                      uint64
    84  	TenantID, UserID, RoleID atomic.Uint32
    85  	isReplay                 bool
    86  	DedupType                txnif.DedupType
    87  
    88  	PrepareCommitFn   func(txnif.AsyncTxn) error
    89  	PrepareRollbackFn func(txnif.AsyncTxn) error
    90  	ApplyCommitFn     func(txnif.AsyncTxn) error
    91  	ApplyRollbackFn   func(txnif.AsyncTxn) error
    92  }
    93  
    94  func NewTxn(mgr *TxnManager, store txnif.TxnStore, txnId []byte, start, snapshot types.TS) *Txn {
    95  	txn := &Txn{
    96  		Mgr:   mgr,
    97  		Store: store,
    98  	}
    99  	txn.TxnCtx = NewTxnCtx(txnId, start, snapshot)
   100  	return txn
   101  }
   102  
   103  func MockTxnReaderWithStartTS(startTS types.TS) *Txn {
   104  	return &Txn{
   105  		TxnCtx: &TxnCtx{
   106  			StartTS: startTS,
   107  		},
   108  	}
   109  }
   110  
   111  func NewPersistedTxn(
   112  	mgr *TxnManager,
   113  	ctx *TxnCtx,
   114  	store txnif.TxnStore,
   115  	lsn uint64,
   116  	prepareCommitFn func(txnif.AsyncTxn) error,
   117  	prepareRollbackFn func(txnif.AsyncTxn) error,
   118  	applyCommitFn func(txnif.AsyncTxn) error,
   119  	applyRollbackFn func(txnif.AsyncTxn) error) *Txn {
   120  	return &Txn{
   121  		Mgr:               mgr,
   122  		TxnCtx:            ctx,
   123  		Store:             store,
   124  		isReplay:          true,
   125  		LSN:               lsn,
   126  		PrepareRollbackFn: prepareRollbackFn,
   127  		PrepareCommitFn:   prepareCommitFn,
   128  		ApplyRollbackFn:   applyRollbackFn,
   129  		ApplyCommitFn:     applyCommitFn,
   130  	}
   131  }
   132  func (txn *Txn) GetBase() txnif.BaseTxn {
   133  	return txn
   134  }
   135  func (txn *Txn) GetLsn() uint64              { return txn.LSN }
   136  func (txn *Txn) IsReplay() bool              { return txn.isReplay }
   137  func (txn *Txn) GetContext() context.Context { return txn.Store.GetContext() }
   138  func (txn *Txn) MockIncWriteCnt() int        { return txn.Store.IncreateWriteCnt() }
   139  
   140  func (txn *Txn) SetError(err error) { txn.Err = err }
   141  func (txn *Txn) GetError() error    { return txn.Err }
   142  
   143  func (txn *Txn) SetPrepareCommitFn(fn func(txnif.AsyncTxn) error)   { txn.PrepareCommitFn = fn }
   144  func (txn *Txn) SetPrepareRollbackFn(fn func(txnif.AsyncTxn) error) { txn.PrepareRollbackFn = fn }
   145  func (txn *Txn) SetApplyCommitFn(fn func(txnif.AsyncTxn) error)     { txn.ApplyCommitFn = fn }
   146  func (txn *Txn) SetApplyRollbackFn(fn func(txnif.AsyncTxn) error)   { txn.ApplyRollbackFn = fn }
   147  func (txn *Txn) SetDedupType(dedupType txnif.DedupType)             { txn.DedupType = dedupType }
   148  func (txn *Txn) GetDedupType() txnif.DedupType                      { return txn.DedupType }
   149  
   150  //The state transition of transaction is as follows:
   151  // 1PC: TxnStateActive--->TxnStatePreparing--->TxnStateCommitted/TxnStateRollbacked
   152  //		TxnStateActive--->TxnStatePreparing--->TxnStateRollbacking--->TxnStateRollbacked
   153  //      TxnStateActive--->TxnStateRollbacking--->TxnStateRollbacked
   154  // 2PC running on Coordinator: TxnStateActive--->TxnStatePreparing-->TxnStatePrepared
   155  //								-->TxnStateCommittingFinished--->TxnStateCommitted or
   156  //								TxnStateActive--->TxnStatePreparing-->TxnStatePrepared-->TxnStateRollbacked or
   157  //                             TxnStateActive--->TxnStateRollbacking--->TxnStateRollbacked.
   158  // 2PC running on Participant: TxnStateActive--->TxnStatePreparing-->TxnStatePrepared-->TxnStateCommitted or
   159  //                             TxnStateActive--->TxnStatePreparing-->TxnStatePrepared-->
   160  //                             TxnStateRollbacking-->TxnStateRollbacked or
   161  //                             TxnStateActive--->TxnStateRollbacking-->TxnStateRollbacked.
   162  
   163  // Prepare is used to pre-commit a 2PC distributed transaction.
   164  // Notice that once any error happened, we should rollback the txn.
   165  // TODO:
   166  //  1. How to handle the case in which log service timed out?
   167  //  2. For a 2pc transaction, Rollback message may arrive before Prepare message,
   168  //     should handle this case by TxnStorage?
   169  func (txn *Txn) Prepare(ctx context.Context) (pts types.TS, err error) {
   170  	if txn.Mgr.GetTxn(txn.GetID()) == nil {
   171  		logutil.Warn("tae : txn is not found in TxnManager")
   172  		//txn.Err = ErrTxnNotFound
   173  		return types.TS{}, moerr.NewTxnNotFoundNoCtx()
   174  	}
   175  	state := txn.GetTxnState(false)
   176  	if state != txnif.TxnStateActive {
   177  		logutil.Warnf("unexpected txn status : %s", txnif.TxnStrState(state))
   178  		txn.Err = moerr.NewTxnNotActiveNoCtx(txnif.TxnStrState(state))
   179  		return types.TS{}, txn.Err
   180  	}
   181  	txn.Add(1)
   182  	err = txn.Mgr.OnOpTxn(&OpTxn{
   183  		ctx: ctx,
   184  		Txn: txn,
   185  		Op:  OpPrepare,
   186  	})
   187  	// TxnManager is closed
   188  	if err != nil {
   189  		txn.SetError(err)
   190  		txn.ToRollbacking(txn.GetStartTS())
   191  		_ = txn.PrepareRollback()
   192  		_ = txn.ApplyRollback()
   193  		txn.DoneWithErr(err, true)
   194  	}
   195  	txn.Wait()
   196  
   197  	if txn.Err != nil {
   198  		txn.Mgr.DeleteTxn(txn.GetID())
   199  	}
   200  	return txn.GetPrepareTS(), txn.GetError()
   201  }
   202  
   203  // Rollback is used to roll back a 1PC or 2PC transaction.
   204  // Notice that there may be a such scenario in which a 2PC distributed transaction in ACTIVE
   205  // will be rollbacked, since Rollback message may arrive before the Prepare message.
   206  func (txn *Txn) Rollback(ctx context.Context) (err error) {
   207  	//idempotent check
   208  	if txn.Mgr.GetTxn(txn.GetID()) == nil {
   209  		logutil.Warnf("tae : txn %s is not found in TxnManager", txn.GetID())
   210  		err = moerr.NewTxnNotFoundNoCtx()
   211  		return
   212  	}
   213  	if txn.Store.IsReadonly() {
   214  		err = txn.Mgr.DeleteTxn(txn.GetID())
   215  		return
   216  	}
   217  
   218  	if txn.Is2PC() {
   219  		return txn.rollback2PC(ctx)
   220  	}
   221  
   222  	return txn.rollback1PC(ctx)
   223  }
   224  
   225  // Committing is used to record a "committing" status for coordinator.
   226  // Notice that txn must commit successfully once committing message arrives, since Preparing
   227  // had already succeeded.
   228  func (txn *Txn) Committing() (err error) {
   229  	return txn.doCommitting(false)
   230  }
   231  
   232  func (txn *Txn) CommittingInRecovery() (err error) {
   233  	return txn.doCommitting(true)
   234  }
   235  
   236  func (txn *Txn) doCommitting(inRecovery bool) (err error) {
   237  	if txn.Mgr.GetTxn(txn.GetID()) == nil {
   238  		err = moerr.NewTxnNotFoundNoCtx()
   239  		return
   240  	}
   241  	state := txn.GetTxnState(false)
   242  	if state != txnif.TxnStatePrepared {
   243  		return moerr.NewInternalErrorNoCtx(
   244  			"stat not prepared, unexpected txn status : %s",
   245  			txnif.TxnStrState(state),
   246  		)
   247  	}
   248  	if err = txn.ToCommittingFinished(); err != nil {
   249  		panic(err)
   250  	}
   251  
   252  	// Skip logging in recovery
   253  	if !inRecovery {
   254  		//Make a committing log entry, flush and wait it synced.
   255  		//A log entry's payload contains txn id , commit timestamp and txn's state.
   256  		_, err = txn.LogTxnState(true)
   257  		if err != nil {
   258  			panic(err)
   259  		}
   260  	}
   261  	err = txn.Err
   262  	return
   263  }
   264  
   265  // Commit is used to commit a 1PC or 2PC transaction running on Coordinator or running on Participant.
   266  // Notice that the Commit of a 2PC transaction must be success once the Commit message arrives,
   267  // since Preparing had already succeeded.
   268  func (txn *Txn) Commit(ctx context.Context) (err error) {
   269  	probe := trace.StartRegion(context.Background(), "Commit")
   270  	defer probe.End()
   271  
   272  	err = txn.doCommit(ctx, false)
   273  	return
   274  }
   275  
   276  // CommitInRecovery is called during recovery
   277  func (txn *Txn) CommitInRecovery(ctx context.Context) (err error) {
   278  	return txn.doCommit(ctx, true)
   279  }
   280  
   281  func (txn *Txn) doCommit(ctx context.Context, inRecovery bool) (err error) {
   282  	if txn.Mgr.GetTxn(txn.GetID()) == nil {
   283  		err = moerr.NewTxnNotFoundNoCtx()
   284  		return
   285  	}
   286  	// Skip readonly txn
   287  	if txn.Store.IsReadonly() {
   288  		txn.Mgr.DeleteTxn(txn.GetID())
   289  		return nil
   290  	}
   291  
   292  	if txn.Is2PC() {
   293  		return txn.commit2PC(inRecovery)
   294  	}
   295  
   296  	return txn.commit1PC(ctx, inRecovery)
   297  }
   298  
   299  func (txn *Txn) GetStore() txnif.TxnStore {
   300  	return txn.Store
   301  }
   302  
   303  func (txn *Txn) GetLSN() uint64 { return txn.LSN }
   304  
   305  func (txn *Txn) DoneWithErr(err error, isAbort bool) {
   306  	// Idempotent check
   307  	if moerr.IsMoErrCode(err, moerr.ErrTxnNotActive) {
   308  		// FIXME::??
   309  		txn.WaitGroup.Done()
   310  		return
   311  	}
   312  
   313  	if txn.Is2PC() {
   314  		txn.done2PCWithErr(err, isAbort)
   315  		return
   316  	}
   317  	txn.done1PCWithErr(err)
   318  	txn.GetStore().EndTrace()
   319  }
   320  
   321  func (txn *Txn) PrepareCommit() (err error) {
   322  	logutil.Debugf("Prepare Commite %X", txn.ID)
   323  	if txn.PrepareCommitFn != nil {
   324  		err = txn.PrepareCommitFn(txn)
   325  		return
   326  	}
   327  	err = txn.Store.PrepareCommit()
   328  	return err
   329  }
   330  
   331  func (txn *Txn) PreApplyCommit() (err error) {
   332  	err = txn.Store.PreApplyCommit()
   333  	return
   334  }
   335  
   336  func (txn *Txn) PrepareWAL() (err error) {
   337  	err = txn.Store.PrepareWAL()
   338  	return
   339  }
   340  
   341  func (txn *Txn) ApplyCommit() (err error) {
   342  	if txn.ApplyCommitFn != nil {
   343  		err = txn.ApplyCommitFn(txn)
   344  		return
   345  	}
   346  	defer func() {
   347  		//Get the lsn of ETTxnRecord entry in GroupC.
   348  		txn.LSN = txn.Store.GetLSN()
   349  		if err == nil {
   350  			err = txn.Store.Close()
   351  		} else {
   352  			txn.Store.Close()
   353  		}
   354  	}()
   355  	err = txn.Store.ApplyCommit()
   356  	return
   357  }
   358  
   359  func (txn *Txn) ApplyRollback() (err error) {
   360  	if txn.ApplyRollbackFn != nil {
   361  		err = txn.ApplyRollbackFn(txn)
   362  		return
   363  	}
   364  	defer func() {
   365  		txn.LSN = txn.Store.GetLSN()
   366  		if err == nil {
   367  			err = txn.Store.Close()
   368  		} else {
   369  			txn.Store.Close()
   370  		}
   371  	}()
   372  	err = txn.Store.ApplyRollback()
   373  	return
   374  }
   375  
   376  func (txn *Txn) PrePrepare(ctx context.Context) error {
   377  	return txn.Store.PrePrepare(ctx)
   378  }
   379  
   380  func (txn *Txn) Freeze() error {
   381  	return txn.Store.Freeze()
   382  }
   383  
   384  func (txn *Txn) PrepareRollback() (err error) {
   385  	logutil.Debugf("Prepare Rollbacking %X", txn.ID)
   386  	if txn.PrepareRollbackFn != nil {
   387  		err = txn.PrepareRollbackFn(txn)
   388  		return
   389  	}
   390  	err = txn.Store.PrepareRollback()
   391  	return
   392  }
   393  
   394  func (txn *Txn) String() string {
   395  	str := txn.TxnCtx.String()
   396  	return fmt.Sprintf("%s: %v", str, txn.GetError())
   397  }
   398  
   399  func (txn *Txn) WaitPrepared(ctx context.Context) error {
   400  	return txn.Store.WaitPrepared(ctx)
   401  }
   402  
   403  func (txn *Txn) WaitDone(err error, isAbort bool) error {
   404  	// logutil.Infof("Wait %s Done", txn.String())
   405  	txn.DoneWithErr(err, isAbort)
   406  	return txn.Err
   407  }
   408  
   409  func (txn *Txn) BindAccessInfo(tenantID, userID, roleID uint32) {
   410  	txn.TenantID.Store(tenantID)
   411  	txn.UserID.Store(userID)
   412  	txn.RoleID.Store(roleID)
   413  }
   414  
   415  func (txn *Txn) GetTenantID() uint32 {
   416  	return txn.TenantID.Load()
   417  }
   418  
   419  func (txn *Txn) GetUserAndRoleID() (uint32, uint32) {
   420  	return txn.UserID.Load(), txn.RoleID.Load()
   421  }
   422  
   423  func (txn *Txn) CreateDatabase(name, createSql, datTyp string) (db handle.Database, err error) {
   424  	return
   425  }
   426  
   427  func (txn *Txn) CreateDatabaseWithCtx(ctx context.Context,
   428  	name, createSql, datTyp string, id uint64) (db handle.Database, err error) {
   429  	return
   430  }
   431  
   432  func (txn *Txn) DropDatabase(name string) (db handle.Database, err error) {
   433  	return
   434  }
   435  
   436  func (txn *Txn) DropDatabaseByID(id uint64) (db handle.Database, err error) {
   437  	return
   438  }
   439  
   440  func (txn *Txn) UnsafeGetDatabase(id uint64) (db handle.Database, err error) {
   441  	return
   442  }
   443  
   444  func (txn *Txn) UnsafeGetRelation(dbId, id uint64) (db handle.Relation, err error) {
   445  	return
   446  }
   447  
   448  func (txn *Txn) GetDatabase(name string) (db handle.Database, err error) {
   449  	return
   450  }
   451  func (txn *Txn) GetDatabaseWithCtx(_ context.Context, _ string) (db handle.Database, err error) {
   452  	return
   453  }
   454  
   455  func (txn *Txn) GetDatabaseByID(id uint64) (db handle.Database, err error) {
   456  	return
   457  }
   458  
   459  func (txn *Txn) UseDatabase(name string) (err error) {
   460  	return
   461  }
   462  
   463  func (txn *Txn) CurrentDatabase() (db handle.Database) {
   464  	return
   465  }
   466  
   467  func (txn *Txn) DatabaseNames() (names []string) {
   468  	return
   469  }
   470  
   471  func (txn *Txn) LogTxnEntry(dbId, tableId uint64, entry txnif.TxnEntry, readed []*common.ID) (err error) {
   472  	return
   473  }
   474  
   475  func (txn *Txn) LogTxnState(sync bool) (logEntry entry.Entry, err error) {
   476  	return
   477  }