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