github.com/matrixorigin/matrixone@v1.2.0/pkg/txn/client/operator.go (about)

     1  // Copyright 2022 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 client
    16  
    17  import (
    18  	"bytes"
    19  	"context"
    20  	"encoding/hex"
    21  	"errors"
    22  	"fmt"
    23  	"sync"
    24  	"sync/atomic"
    25  	"time"
    26  
    27  	"go.uber.org/zap"
    28  
    29  	"github.com/matrixorigin/matrixone/pkg/common/moerr"
    30  	"github.com/matrixorigin/matrixone/pkg/common/runtime"
    31  	"github.com/matrixorigin/matrixone/pkg/lockservice"
    32  	"github.com/matrixorigin/matrixone/pkg/pb/lock"
    33  	"github.com/matrixorigin/matrixone/pkg/pb/metadata"
    34  	"github.com/matrixorigin/matrixone/pkg/pb/timestamp"
    35  	"github.com/matrixorigin/matrixone/pkg/pb/txn"
    36  	"github.com/matrixorigin/matrixone/pkg/txn/clock"
    37  	"github.com/matrixorigin/matrixone/pkg/txn/rpc"
    38  	"github.com/matrixorigin/matrixone/pkg/txn/util"
    39  	v2 "github.com/matrixorigin/matrixone/pkg/util/metric/v2"
    40  )
    41  
    42  var (
    43  	readTxnErrors = map[uint16]struct{}{
    44  		moerr.ErrTAERead:      {},
    45  		moerr.ErrRpcError:     {},
    46  		moerr.ErrWaitTxn:      {},
    47  		moerr.ErrTxnNotFound:  {},
    48  		moerr.ErrTxnNotActive: {},
    49  	}
    50  	writeTxnErrors = map[uint16]struct{}{
    51  		moerr.ErrTAEWrite:     {},
    52  		moerr.ErrRpcError:     {},
    53  		moerr.ErrTxnNotFound:  {},
    54  		moerr.ErrTxnNotActive: {},
    55  	}
    56  	commitTxnErrors = map[uint16]struct{}{
    57  		moerr.ErrTAECommit:            {},
    58  		moerr.ErrTAERollback:          {},
    59  		moerr.ErrTAEPrepare:           {},
    60  		moerr.ErrRpcError:             {},
    61  		moerr.ErrTxnNotFound:          {},
    62  		moerr.ErrTxnNotActive:         {},
    63  		moerr.ErrLockTableBindChanged: {},
    64  		moerr.ErrCannotCommitOrphan:   {},
    65  	}
    66  	rollbackTxnErrors = map[uint16]struct{}{
    67  		moerr.ErrTAERollback:  {},
    68  		moerr.ErrRpcError:     {},
    69  		moerr.ErrTxnNotFound:  {},
    70  		moerr.ErrTxnNotActive: {},
    71  	}
    72  )
    73  
    74  // WithUserTxn setup user transaction flag. Only user transactions need to be controlled for the maximum
    75  // number of active transactions.
    76  func WithUserTxn() TxnOption {
    77  	return func(tc *txnOperator) {
    78  		tc.options = tc.options.WithUserTxn()
    79  	}
    80  }
    81  
    82  // WithTxnReadyOnly setup readonly flag
    83  func WithTxnReadyOnly() TxnOption {
    84  	return func(tc *txnOperator) {
    85  		tc.options = tc.options.WithReadOnly()
    86  	}
    87  }
    88  
    89  // WithTxnDisable1PCOpt disable 1pc opt on distributed transaction. By default, mo enables 1pc
    90  // optimization for distributed transactions. For write operations, if all partitions' prepares are
    91  // executed successfully, then the transaction is considered committed and returned directly to the
    92  // client. Partitions' prepared data are committed asynchronously.
    93  func WithTxnDisable1PCOpt() TxnOption {
    94  	return func(tc *txnOperator) {
    95  		tc.options = tc.options.WithDisable1PC()
    96  	}
    97  }
    98  
    99  // WithTxnCNCoordinator set cn txn coordinator
   100  func WithTxnCNCoordinator() TxnOption {
   101  	return func(tc *txnOperator) {
   102  		tc.coordinator = true
   103  	}
   104  }
   105  
   106  // WithTxnLockService set txn lock service
   107  func WithTxnLockService(lockService lockservice.LockService) TxnOption {
   108  	return func(tc *txnOperator) {
   109  		tc.lockService = lockService
   110  	}
   111  }
   112  
   113  // WithTxnCreateBy set txn create by.
   114  func WithTxnCreateBy(
   115  	accountID uint32,
   116  	userName string,
   117  	sessionID string,
   118  	connectionID uint32) TxnOption {
   119  	return func(tc *txnOperator) {
   120  		tc.options.CN = runtime.ProcessLevelRuntime().ServiceUUID()
   121  		tc.options.SessionID = sessionID
   122  		tc.options.ConnectionID = connectionID
   123  		tc.options.AccountID = accountID
   124  		tc.options.UserName = userName
   125  	}
   126  }
   127  
   128  // WithTxnCacheWrite Set cache write requests, after each Write call, the request will not be sent
   129  // to the TN node immediately, but stored in the Coordinator's memory, and the Coordinator will
   130  // choose the right time to send the cached requests. The following scenarios trigger the sending
   131  // of requests to DN:
   132  //  1. Before read, because the Coordinator is not aware of the format and content of the written data,
   133  //     it is necessary to send the cached write requests to the corresponding TN node each time Read is
   134  //     called, used to implement "read your write".
   135  //  2. Before commit, obviously, the cached write requests needs to be sent to the corresponding TN node
   136  //     before commit.
   137  func WithTxnCacheWrite() TxnOption {
   138  	return func(tc *txnOperator) {
   139  		tc.options = tc.options.WithEnableCacheWrite()
   140  		tc.mu.cachedWrites = make(map[uint64][]txn.TxnRequest)
   141  	}
   142  }
   143  
   144  // WithSnapshotTS use a spec snapshot timestamp to build TxnOperator.
   145  func WithSnapshotTS(ts timestamp.Timestamp) TxnOption {
   146  	return func(tc *txnOperator) {
   147  		tc.mu.txn.SnapshotTS = ts
   148  	}
   149  }
   150  
   151  // WithTxnMode set txn mode
   152  func WithTxnMode(value txn.TxnMode) TxnOption {
   153  	return func(tc *txnOperator) {
   154  		tc.mu.txn.Mode = value
   155  	}
   156  }
   157  
   158  // WithTxnIsolation set txn isolation
   159  func WithTxnIsolation(value txn.TxnIsolation) TxnOption {
   160  	return func(tc *txnOperator) {
   161  		tc.mu.txn.Isolation = value
   162  	}
   163  }
   164  
   165  // WithTxnSkipLock skip txn lock on specified tables
   166  func WithTxnSkipLock(
   167  	tables []uint64,
   168  	modes []lock.LockMode) TxnOption {
   169  	return func(tc *txnOperator) {
   170  		tc.options.SkipLockTables = append(tc.options.SkipLockTables, tables...)
   171  		tc.options.SkipLockTableModes = append(tc.options.SkipLockTableModes, modes...)
   172  	}
   173  }
   174  
   175  // WithTxnEnableCheckDup enable check duplicate before commit to TN
   176  func WithTxnEnableCheckDup() TxnOption {
   177  	return func(tc *txnOperator) {
   178  		tc.options = tc.options.WithEnableCheckDup()
   179  	}
   180  }
   181  
   182  func WithDisableTrace(value bool) TxnOption {
   183  	return func(tc *txnOperator) {
   184  		if value {
   185  			tc.options = tc.options.WithDisableTrace()
   186  		}
   187  	}
   188  }
   189  
   190  func WithSessionInfo(info string) TxnOption {
   191  	return func(tc *txnOperator) {
   192  		tc.options.SessionInfo = info
   193  	}
   194  }
   195  
   196  type txnOperator struct {
   197  	sender               rpc.TxnSender
   198  	waiter               *waiter
   199  	txnID                []byte
   200  	coordinator          bool
   201  	options              txn.TxnOptions
   202  	cannotCleanWorkspace bool
   203  	workspace            Workspace
   204  	timestampWaiter      TimestampWaiter
   205  	clock                clock.Clock
   206  	createAt             time.Time
   207  	commitAt             time.Time
   208  	commitSeq            uint64
   209  	lockService          lockservice.LockService
   210  	sequence             atomic.Uint64
   211  	createTs             timestamp.Timestamp
   212  	//read-only txn operators for supporting snapshot read feature.
   213  	children []*txnOperator
   214  	parent   atomic.Pointer[txnOperator]
   215  
   216  	mu struct {
   217  		sync.RWMutex
   218  		waitActive   bool
   219  		closed       bool
   220  		txn          txn.TxnMeta
   221  		cachedWrites map[uint64][]txn.TxnRequest
   222  		lockTables   []lock.LockTable
   223  		callbacks    map[EventType][]func(TxnEvent)
   224  		retry        bool
   225  		lockSeq      uint64
   226  		waitLocks    map[uint64]Lock
   227  	}
   228  
   229  	commitCounter   counter
   230  	rollbackCounter counter
   231  	runSqlCounter   counter
   232  
   233  	waitActiveCost time.Duration
   234  }
   235  
   236  func newTxnOperator(
   237  	clock clock.Clock,
   238  	sender rpc.TxnSender,
   239  	txnMeta txn.TxnMeta,
   240  	options ...TxnOption) *txnOperator {
   241  	tc := &txnOperator{sender: sender}
   242  	tc.mu.txn = txnMeta
   243  	tc.txnID = txnMeta.ID
   244  	tc.clock = clock
   245  	tc.createAt = time.Now()
   246  	tc.createTs, _ = clock.Now()
   247  	for _, opt := range options {
   248  		opt(tc)
   249  	}
   250  	tc.adjust()
   251  	util.LogTxnCreated(tc.mu.txn)
   252  
   253  	if tc.options.UserTxn() {
   254  		v2.TxnUserCounter.Inc()
   255  	} else {
   256  		v2.TxnInternalCounter.Inc()
   257  	}
   258  	return tc
   259  }
   260  
   261  func (tc *txnOperator) IsSnapOp() bool {
   262  	return tc.parent.Load() != nil
   263  }
   264  
   265  func (tc *txnOperator) CloneSnapshotOp(snapshot timestamp.Timestamp) TxnOperator {
   266  	op := &txnOperator{}
   267  	op.mu.txn = txn.TxnMeta{
   268  		SnapshotTS: snapshot,
   269  		ID:         tc.mu.txn.ID,
   270  		TNShards:   tc.mu.txn.TNShards,
   271  	}
   272  	op.txnID = op.mu.txn.ID
   273  
   274  	op.workspace = tc.workspace.CloneSnapshotWS()
   275  	op.workspace.BindTxnOp(op)
   276  
   277  	tc.children = append(tc.children, op)
   278  	op.parent.Store(tc)
   279  	return op
   280  }
   281  
   282  func newTxnOperatorWithSnapshot(
   283  	sender rpc.TxnSender,
   284  	snapshot []byte) (*txnOperator, error) {
   285  	v := &txn.CNTxnSnapshot{}
   286  	if err := v.Unmarshal(snapshot); err != nil {
   287  		return nil, err
   288  	}
   289  
   290  	tc := &txnOperator{sender: sender}
   291  	tc.txnID = v.Txn.ID
   292  	tc.options = v.Options
   293  	tc.mu.txn = v.Txn
   294  	tc.mu.txn.Mirror = true
   295  	tc.mu.lockTables = v.LockTables
   296  
   297  	tc.adjust()
   298  	util.LogTxnCreated(tc.mu.txn)
   299  	return tc, nil
   300  }
   301  
   302  func (tc *txnOperator) setWaitActive(v bool) {
   303  	tc.mu.Lock()
   304  	defer tc.mu.Unlock()
   305  	tc.mu.waitActive = v
   306  }
   307  
   308  func (tc *txnOperator) waitActive(ctx context.Context) error {
   309  	if tc.waiter == nil {
   310  		return nil
   311  	}
   312  
   313  	tc.setWaitActive(true)
   314  	defer func() {
   315  		tc.waiter.close()
   316  		tc.setWaitActive(false)
   317  	}()
   318  
   319  	cost, err := tc.doCostAction(
   320  		time.Time{},
   321  		WaitActiveEvent,
   322  		func() error {
   323  			return tc.waiter.wait(ctx)
   324  		},
   325  		false)
   326  	tc.waitActiveCost = cost
   327  	v2.TxnWaitActiveDurationHistogram.Observe(cost.Seconds())
   328  	return err
   329  }
   330  
   331  func (tc *txnOperator) GetWaitActiveCost() time.Duration {
   332  	return tc.waitActiveCost
   333  }
   334  
   335  func (tc *txnOperator) notifyActive() {
   336  	if tc.waiter == nil {
   337  		panic("BUG: notify active on non-waiter txn operator")
   338  	}
   339  	defer tc.waiter.close()
   340  	tc.waiter.notify()
   341  }
   342  
   343  func (tc *txnOperator) AddWorkspace(workspace Workspace) {
   344  	tc.workspace = workspace
   345  }
   346  
   347  func (tc *txnOperator) GetWorkspace() Workspace {
   348  	return tc.workspace
   349  }
   350  
   351  func (tc *txnOperator) adjust() {
   352  	if tc.sender == nil {
   353  		util.GetLogger().Fatal("missing txn sender")
   354  	}
   355  	if len(tc.mu.txn.ID) == 0 {
   356  		util.GetLogger().Fatal("missing txn id")
   357  	}
   358  	if tc.options.ReadOnly() && tc.options.CacheWriteEnabled() {
   359  		util.GetLogger().Fatal("readyOnly and delayWrites cannot both be set")
   360  	}
   361  }
   362  
   363  func (tc *txnOperator) Txn() txn.TxnMeta {
   364  	return tc.getTxnMeta(false)
   365  }
   366  
   367  func (tc *txnOperator) TxnRef() *txn.TxnMeta {
   368  	tc.mu.RLock()
   369  	defer tc.mu.RUnlock()
   370  	return &tc.mu.txn
   371  }
   372  
   373  func (tc *txnOperator) SnapshotTS() timestamp.Timestamp {
   374  	tc.mu.RLock()
   375  	defer tc.mu.RUnlock()
   376  	return tc.mu.txn.SnapshotTS
   377  }
   378  
   379  func (tc *txnOperator) CreateTS() timestamp.Timestamp {
   380  	return tc.createTs
   381  }
   382  
   383  func (tc *txnOperator) Status() txn.TxnStatus {
   384  	tc.mu.RLock()
   385  	defer tc.mu.RUnlock()
   386  	return tc.mu.txn.Status
   387  }
   388  
   389  func (tc *txnOperator) Snapshot() ([]byte, error) {
   390  	tc.mu.Lock()
   391  	defer tc.mu.Unlock()
   392  
   393  	if err := tc.checkStatus(true); err != nil {
   394  		return nil, err
   395  	}
   396  	snapshot := &txn.CNTxnSnapshot{
   397  		Txn:        tc.mu.txn,
   398  		LockTables: tc.mu.lockTables,
   399  		Options:    tc.options,
   400  	}
   401  	return snapshot.Marshal()
   402  }
   403  
   404  func (tc *txnOperator) UpdateSnapshot(
   405  	ctx context.Context,
   406  	ts timestamp.Timestamp) error {
   407  	tc.mu.Lock()
   408  	defer tc.mu.Unlock()
   409  	if err := tc.checkStatus(true); err != nil {
   410  		return err
   411  	}
   412  
   413  	// ony push model support RC isolation
   414  	if tc.timestampWaiter == nil {
   415  		return nil
   416  	}
   417  
   418  	_, err := tc.doCostAction(
   419  		time.Time{},
   420  		UpdateSnapshotEvent,
   421  		func() error {
   422  			var err error
   423  			tc.mu.txn.SnapshotTS, err = tc.timestampWaiter.GetTimestamp(
   424  				ctx,
   425  				ts)
   426  			return err
   427  		},
   428  		true)
   429  	return err
   430  }
   431  
   432  func (tc *txnOperator) ApplySnapshot(data []byte) error {
   433  	if !tc.coordinator {
   434  		util.GetLogger().Fatal("apply snapshot on non-coordinator txn operator")
   435  	}
   436  
   437  	tc.mu.Lock()
   438  	defer tc.mu.Unlock()
   439  
   440  	if err := tc.checkStatus(true); err != nil {
   441  		return err
   442  	}
   443  
   444  	snapshot := &txn.CNTxnSnapshot{}
   445  	if err := snapshot.Unmarshal(data); err != nil {
   446  		return err
   447  	}
   448  
   449  	if !bytes.Equal(snapshot.Txn.ID, tc.mu.txn.ID) {
   450  		util.GetLogger().Fatal("apply snapshot with invalid txn id")
   451  	}
   452  
   453  	// apply locked tables in other cn
   454  	for _, v := range snapshot.LockTables {
   455  		if err := tc.doAddLockTableLocked(v); err != nil {
   456  			return err
   457  		}
   458  	}
   459  
   460  	for _, tn := range snapshot.Txn.TNShards {
   461  		has := false
   462  		for _, v := range tc.mu.txn.TNShards {
   463  			if v.ShardID == tn.ShardID {
   464  				has = true
   465  				break
   466  			}
   467  		}
   468  
   469  		if !has {
   470  			tc.mu.txn.TNShards = append(tc.mu.txn.TNShards, tn)
   471  		}
   472  	}
   473  	if tc.mu.txn.SnapshotTS.Less(snapshot.Txn.SnapshotTS) {
   474  		tc.mu.txn.SnapshotTS = snapshot.Txn.SnapshotTS
   475  	}
   476  	util.LogTxnUpdated(tc.mu.txn)
   477  	return nil
   478  }
   479  
   480  func (tc *txnOperator) Read(ctx context.Context, requests []txn.TxnRequest) (*rpc.SendResult, error) {
   481  	util.LogTxnRead(tc.getTxnMeta(false))
   482  
   483  	for idx := range requests {
   484  		requests[idx].Method = txn.TxnMethod_Read
   485  	}
   486  
   487  	if err := tc.validate(ctx, false); err != nil {
   488  		return nil, err
   489  	}
   490  
   491  	requests = tc.maybeInsertCachedWrites(requests, false)
   492  	return tc.trimResponses(tc.handleError(tc.doSend(ctx, requests, false)))
   493  }
   494  
   495  func (tc *txnOperator) Write(ctx context.Context, requests []txn.TxnRequest) (*rpc.SendResult, error) {
   496  	util.LogTxnWrite(tc.getTxnMeta(false))
   497  	return tc.doWrite(ctx, requests, false)
   498  }
   499  
   500  func (tc *txnOperator) WriteAndCommit(ctx context.Context, requests []txn.TxnRequest) (*rpc.SendResult, error) {
   501  	util.LogTxnWrite(tc.getTxnMeta(false))
   502  	util.LogTxnCommit(tc.getTxnMeta(false))
   503  	return tc.doWrite(ctx, requests, true)
   504  }
   505  
   506  func (tc *txnOperator) Commit(ctx context.Context) (err error) {
   507  	tc.commitCounter.addEnter()
   508  	defer tc.commitCounter.addExit()
   509  	txn := tc.getTxnMeta(false)
   510  	util.LogTxnCommit(txn)
   511  
   512  	tc.commitSeq = tc.NextSequence()
   513  	tc.commitAt = time.Now()
   514  
   515  	tc.triggerEvent(newEvent(CommitEvent, txn, tc.commitSeq, nil))
   516  	defer func() {
   517  		cost := time.Since(tc.commitAt)
   518  		v2.TxnCNCommitDurationHistogram.Observe(cost.Seconds())
   519  		tc.triggerEvent(newCostEvent(CommitEvent, tc.getTxnMeta(false), tc.commitSeq, err, cost))
   520  	}()
   521  
   522  	if tc.options.ReadOnly() {
   523  		tc.mu.Lock()
   524  		defer tc.mu.Unlock()
   525  		tc.closeLocked()
   526  		return
   527  	}
   528  
   529  	result, e := tc.doWrite(ctx, nil, true)
   530  	if e != nil {
   531  		err = e
   532  		return
   533  	}
   534  
   535  	if result != nil {
   536  		result.Release()
   537  	}
   538  	return
   539  }
   540  
   541  func (tc *txnOperator) Rollback(ctx context.Context) (err error) {
   542  	tc.rollbackCounter.addEnter()
   543  	defer tc.rollbackCounter.addExit()
   544  	v2.TxnRollbackCounter.Inc()
   545  	txnMeta := tc.getTxnMeta(false)
   546  	util.LogTxnRollback(txnMeta)
   547  
   548  	if tc.workspace != nil && !tc.cannotCleanWorkspace {
   549  		if err = tc.workspace.Rollback(ctx); err != nil {
   550  			util.GetLogger().Error("rollback workspace failed",
   551  				util.TxnIDField(txnMeta), zap.Error(err))
   552  		}
   553  	}
   554  
   555  	tc.mu.Lock()
   556  	defer tc.mu.Unlock()
   557  
   558  	if tc.mu.closed {
   559  		return nil
   560  	}
   561  
   562  	seq := tc.NextSequence()
   563  	start := time.Now()
   564  	tc.triggerEventLocked(newEvent(RollbackEvent, txnMeta, seq, nil))
   565  	defer func() {
   566  		cost := time.Since(start)
   567  		tc.triggerEventLocked(newCostEvent(RollbackEvent, txnMeta, seq, err, cost))
   568  	}()
   569  
   570  	defer func() {
   571  		tc.mu.txn.Status = txn.TxnStatus_Aborted
   572  		tc.closeLocked()
   573  	}()
   574  
   575  	if tc.needUnlockLocked() {
   576  		defer tc.unlock(ctx)
   577  	}
   578  
   579  	if len(tc.mu.txn.TNShards) == 0 {
   580  		return nil
   581  	}
   582  
   583  	result, err := tc.handleError(tc.doSend(ctx, []txn.TxnRequest{{
   584  		Method:          txn.TxnMethod_Rollback,
   585  		RollbackRequest: &txn.TxnRollbackRequest{},
   586  	}}, true))
   587  	if err != nil {
   588  		if moerr.IsMoErrCode(err, moerr.ErrTxnClosed) {
   589  			return nil
   590  		}
   591  		return err
   592  	}
   593  	if result != nil {
   594  		result.Release()
   595  	}
   596  	return nil
   597  }
   598  
   599  func (tc *txnOperator) AddLockTable(value lock.LockTable) error {
   600  	tc.mu.Lock()
   601  	defer tc.mu.Unlock()
   602  	if tc.mu.txn.Mode != txn.TxnMode_Pessimistic {
   603  		panic("lock in optimistic mode")
   604  	}
   605  
   606  	// mirror txn can not check status, and the txn's status is on the creation cn of the txn.
   607  	if !tc.mu.txn.Mirror {
   608  		if err := tc.checkStatus(true); err != nil {
   609  			return err
   610  		}
   611  	}
   612  
   613  	return tc.doAddLockTableLocked(value)
   614  }
   615  
   616  func (tc *txnOperator) ResetRetry(retry bool) {
   617  	tc.mu.Lock()
   618  	defer tc.mu.Unlock()
   619  	tc.mu.retry = retry
   620  }
   621  
   622  func (tc *txnOperator) IsRetry() bool {
   623  	tc.mu.RLock()
   624  	defer tc.mu.RUnlock()
   625  	return tc.mu.retry
   626  }
   627  
   628  func (tc *txnOperator) doAddLockTableLocked(value lock.LockTable) error {
   629  	for _, l := range tc.mu.lockTables {
   630  		if l.Group == value.Group &&
   631  			l.Table == value.Table {
   632  			if l.Changed(value) {
   633  				return moerr.NewLockTableBindChangedNoCtx()
   634  			}
   635  			return nil
   636  		}
   637  	}
   638  	tc.mu.lockTables = append(tc.mu.lockTables, value)
   639  	return nil
   640  }
   641  
   642  func (tc *txnOperator) Debug(ctx context.Context, requests []txn.TxnRequest) (*rpc.SendResult, error) {
   643  	for idx := range requests {
   644  		requests[idx].Method = txn.TxnMethod_DEBUG
   645  	}
   646  
   647  	if err := tc.validate(ctx, false); err != nil {
   648  		return nil, err
   649  	}
   650  
   651  	requests = tc.maybeInsertCachedWrites(requests, false)
   652  	return tc.trimResponses(tc.handleError(tc.doSend(ctx, requests, false)))
   653  }
   654  
   655  func (tc *txnOperator) doWrite(ctx context.Context, requests []txn.TxnRequest, commit bool) (*rpc.SendResult, error) {
   656  	for idx := range requests {
   657  		requests[idx].Method = txn.TxnMethod_Write
   658  	}
   659  
   660  	if tc.options.ReadOnly() {
   661  		util.GetLogger().Fatal("can not write on ready only transaction")
   662  	}
   663  	var payload []txn.TxnRequest
   664  	if commit {
   665  		if tc.workspace != nil {
   666  			reqs, err := tc.workspace.Commit(ctx)
   667  			if err != nil {
   668  				return nil, errors.Join(err, tc.Rollback(ctx))
   669  			}
   670  			payload = reqs
   671  		}
   672  		tc.mu.Lock()
   673  		defer func() {
   674  			tc.closeLocked()
   675  			tc.mu.Unlock()
   676  		}()
   677  		if tc.mu.closed {
   678  			return nil, moerr.NewTxnClosedNoCtx(tc.txnID)
   679  		}
   680  
   681  		if tc.needUnlockLocked() {
   682  			tc.mu.txn.LockTables = tc.mu.lockTables
   683  			defer tc.unlock(ctx)
   684  		}
   685  	}
   686  
   687  	if err := tc.validate(ctx, commit); err != nil {
   688  		return nil, err
   689  	}
   690  
   691  	var txnReqs []*txn.TxnRequest
   692  	if payload != nil {
   693  		v2.TxnCNCommitCounter.Inc()
   694  		for i := range payload {
   695  			payload[i].Txn = tc.getTxnMeta(true)
   696  			txnReqs = append(txnReqs, &payload[i])
   697  		}
   698  		tc.updateWritePartitions(payload, commit)
   699  	}
   700  
   701  	tc.updateWritePartitions(requests, commit)
   702  
   703  	// delayWrites enabled, no responses
   704  	if !commit && tc.maybeCacheWrites(requests, commit) {
   705  		return nil, nil
   706  	}
   707  
   708  	if commit {
   709  		if len(tc.mu.txn.TNShards) == 0 { // commit no write handled txn
   710  			tc.mu.txn.Status = txn.TxnStatus_Committed
   711  			return nil, nil
   712  		}
   713  
   714  		requests = tc.maybeInsertCachedWrites(requests, true)
   715  		requests = append(requests, txn.TxnRequest{
   716  			Method: txn.TxnMethod_Commit,
   717  			Flag:   txn.SkipResponseFlag,
   718  			CommitRequest: &txn.TxnCommitRequest{
   719  				Payload:       txnReqs,
   720  				Disable1PCOpt: tc.options.Is1PCDisabled(),
   721  			}})
   722  	}
   723  	return tc.trimResponses(tc.handleError(tc.doSend(ctx, requests, commit)))
   724  }
   725  
   726  func (tc *txnOperator) updateWritePartitions(requests []txn.TxnRequest, locked bool) {
   727  	if len(requests) == 0 {
   728  		return
   729  	}
   730  
   731  	if !locked {
   732  		tc.mu.Lock()
   733  		defer tc.mu.Unlock()
   734  	}
   735  
   736  	for _, req := range requests {
   737  		tc.addPartitionLocked(req.CNRequest.Target)
   738  	}
   739  }
   740  
   741  func (tc *txnOperator) addPartitionLocked(tn metadata.TNShard) {
   742  	for idx := range tc.mu.txn.TNShards {
   743  		if tc.mu.txn.TNShards[idx].ShardID == tn.ShardID {
   744  			return
   745  		}
   746  	}
   747  	tc.mu.txn.TNShards = append(tc.mu.txn.TNShards, tn)
   748  	util.LogTxnUpdated(tc.mu.txn)
   749  }
   750  
   751  func (tc *txnOperator) validate(ctx context.Context, locked bool) error {
   752  	if _, ok := ctx.Deadline(); !ok {
   753  		util.GetLogger().Fatal("context deadline set")
   754  	}
   755  
   756  	return tc.checkStatus(locked)
   757  }
   758  
   759  func (tc *txnOperator) checkStatus(locked bool) error {
   760  	if !locked {
   761  		tc.mu.RLock()
   762  		defer tc.mu.RUnlock()
   763  	}
   764  
   765  	if tc.mu.closed {
   766  		return moerr.NewTxnClosedNoCtx(tc.txnID)
   767  	}
   768  	return nil
   769  }
   770  
   771  func (tc *txnOperator) maybeCacheWrites(requests []txn.TxnRequest, locked bool) bool {
   772  	if tc.options.CacheWriteEnabled() {
   773  		if !locked {
   774  			tc.mu.Lock()
   775  			defer tc.mu.Unlock()
   776  		}
   777  
   778  		for idx := range requests {
   779  			requests[idx].Flag |= txn.SkipResponseFlag
   780  			tn := requests[idx].CNRequest.Target.ShardID
   781  			tc.mu.cachedWrites[tn] = append(tc.mu.cachedWrites[tn], requests[idx])
   782  		}
   783  		return true
   784  	}
   785  	return false
   786  }
   787  
   788  func (tc *txnOperator) maybeInsertCachedWrites(
   789  	requests []txn.TxnRequest,
   790  	locked bool,
   791  ) []txn.TxnRequest {
   792  	if len(requests) == 0 ||
   793  		!tc.options.CacheWriteEnabled() {
   794  		return requests
   795  	}
   796  
   797  	if !locked {
   798  		tc.mu.Lock()
   799  		defer tc.mu.Unlock()
   800  	}
   801  
   802  	if len(tc.mu.cachedWrites) == 0 {
   803  		return requests
   804  	}
   805  
   806  	newRequests := requests
   807  	hasCachedWrites := false
   808  	insertCount := 0
   809  	for idx := range requests {
   810  		tn := requests[idx].CNRequest.Target.ShardID
   811  		if writes, ok := tc.getCachedWritesLocked(tn); ok {
   812  			if !hasCachedWrites {
   813  				// copy all requests into newRequests if cached writes encountered
   814  				newRequests = append([]txn.TxnRequest(nil), requests[:idx]...)
   815  			}
   816  			newRequests = append(newRequests, writes...)
   817  			tc.clearCachedWritesLocked(tn)
   818  			hasCachedWrites = true
   819  			insertCount += len(writes)
   820  		}
   821  		if hasCachedWrites {
   822  			newRequests = append(newRequests, requests[idx])
   823  		}
   824  	}
   825  	return newRequests
   826  }
   827  
   828  func (tc *txnOperator) getCachedWritesLocked(tn uint64) ([]txn.TxnRequest, bool) {
   829  	writes, ok := tc.mu.cachedWrites[tn]
   830  	if !ok || len(writes) == 0 {
   831  		return nil, false
   832  	}
   833  	return writes, true
   834  }
   835  
   836  func (tc *txnOperator) clearCachedWritesLocked(tn uint64) {
   837  	delete(tc.mu.cachedWrites, tn)
   838  }
   839  
   840  func (tc *txnOperator) getTxnMeta(locked bool) txn.TxnMeta {
   841  	if !locked {
   842  		tc.mu.RLock()
   843  		defer tc.mu.RUnlock()
   844  	}
   845  	return tc.mu.txn
   846  }
   847  
   848  func (tc *txnOperator) doSend(ctx context.Context, requests []txn.TxnRequest, locked bool) (*rpc.SendResult, error) {
   849  	txnMeta := tc.getTxnMeta(locked)
   850  	for idx := range requests {
   851  		requests[idx].Txn = txnMeta
   852  	}
   853  
   854  	util.LogTxnSendRequests(requests)
   855  	result, err := tc.sender.Send(ctx, requests)
   856  	if err != nil {
   857  		util.LogTxnSendRequestsFailed(requests, err)
   858  		return nil, err
   859  	}
   860  	util.LogTxnReceivedResponses(result.Responses)
   861  
   862  	if len(result.Responses) == 0 {
   863  		return result, nil
   864  	}
   865  
   866  	// update commit timestamp
   867  	resp := result.Responses[len(result.Responses)-1]
   868  	if resp.Txn == nil {
   869  		return result, nil
   870  	}
   871  	if !locked {
   872  		tc.mu.Lock()
   873  		defer tc.mu.Unlock()
   874  	}
   875  	tc.mu.txn.CommitTS = resp.Txn.CommitTS
   876  	tc.mu.txn.Status = resp.Txn.Status
   877  	return result, nil
   878  }
   879  
   880  func (tc *txnOperator) handleError(result *rpc.SendResult, err error) (*rpc.SendResult, error) {
   881  	if err != nil {
   882  		return nil, err
   883  	}
   884  
   885  	for _, resp := range result.Responses {
   886  		if err := tc.handleErrorResponse(resp); err != nil {
   887  			result.Release()
   888  			return nil, err
   889  		}
   890  	}
   891  	return result, nil
   892  }
   893  
   894  func (tc *txnOperator) handleErrorResponse(resp txn.TxnResponse) error {
   895  	switch resp.Method {
   896  	case txn.TxnMethod_Read:
   897  		if err := tc.checkResponseTxnStatusForReadWrite(resp); err != nil {
   898  			return err
   899  		}
   900  		return tc.checkTxnError(resp.TxnError, readTxnErrors)
   901  	case txn.TxnMethod_Write:
   902  		if err := tc.checkResponseTxnStatusForReadWrite(resp); err != nil {
   903  			return err
   904  		}
   905  		return tc.checkTxnError(resp.TxnError, writeTxnErrors)
   906  	case txn.TxnMethod_Commit:
   907  		tc.triggerEventLocked(
   908  			newCostEvent(
   909  				CommitResponseEvent,
   910  				tc.mu.txn,
   911  				tc.commitSeq,
   912  				nil,
   913  				time.Since(tc.commitAt)))
   914  
   915  		if err := tc.checkResponseTxnStatusForCommit(resp); err != nil {
   916  			return err
   917  		}
   918  
   919  		err := tc.checkTxnError(resp.TxnError, commitTxnErrors)
   920  		if err == nil || !tc.mu.txn.IsPessimistic() {
   921  			return err
   922  		}
   923  
   924  		// commit failed, refresh invalid lock tables
   925  		if moerr.IsMoErrCode(err, moerr.ErrLockTableBindChanged) {
   926  			tc.lockService.ForceRefreshLockTableBinds(
   927  				resp.CommitResponse.InvalidLockTables,
   928  				func(bind lock.LockTable) bool {
   929  					for _, hold := range tc.mu.lockTables {
   930  						if hold.Table == bind.Table && !hold.Changed(bind) {
   931  							return true
   932  						}
   933  					}
   934  					return false
   935  				})
   936  		}
   937  
   938  		if moerr.IsMoErrCode(err, moerr.ErrTxnWWConflict) ||
   939  			moerr.IsMoErrCode(err, moerr.ErrDuplicateEntry) {
   940  			v, ok := runtime.ProcessLevelRuntime().GetGlobalVariables(runtime.EnableCheckInvalidRCErrors)
   941  			if ok && v.(bool) {
   942  				util.GetLogger().Fatal("failed",
   943  					zap.Error(err),
   944  					zap.String("txn", hex.EncodeToString(tc.txnID)))
   945  			}
   946  		}
   947  		return err
   948  	case txn.TxnMethod_Rollback:
   949  		if err := tc.checkResponseTxnStatusForRollback(resp); err != nil {
   950  			return err
   951  		}
   952  		return tc.checkTxnError(resp.TxnError, rollbackTxnErrors)
   953  	case txn.TxnMethod_DEBUG:
   954  		if resp.TxnError != nil {
   955  			return resp.TxnError.UnwrapError()
   956  		}
   957  		return nil
   958  	default:
   959  		return moerr.NewNotSupportedNoCtx("unknown txn response method: %s", resp.DebugString())
   960  	}
   961  }
   962  
   963  func (tc *txnOperator) checkResponseTxnStatusForReadWrite(resp txn.TxnResponse) error {
   964  	if resp.TxnError != nil {
   965  		return nil
   966  	}
   967  
   968  	txnMeta := resp.Txn
   969  	if txnMeta == nil {
   970  		return moerr.NewTxnClosedNoCtx(tc.txnID)
   971  	}
   972  
   973  	switch txnMeta.Status {
   974  	case txn.TxnStatus_Active:
   975  		return nil
   976  	case txn.TxnStatus_Aborted, txn.TxnStatus_Aborting,
   977  		txn.TxnStatus_Committed, txn.TxnStatus_Committing:
   978  		return moerr.NewTxnClosedNoCtx(tc.txnID)
   979  	default:
   980  		util.GetLogger().Fatal("invalid response status for read or write",
   981  			util.TxnField(*txnMeta))
   982  	}
   983  	return nil
   984  }
   985  
   986  func (tc *txnOperator) checkTxnError(txnError *txn.TxnError, possibleErrorMap map[uint16]struct{}) error {
   987  	if txnError == nil {
   988  		return nil
   989  	}
   990  
   991  	// use txn internal error code to check error
   992  	txnCode := uint16(txnError.TxnErrCode)
   993  	if txnCode == moerr.ErrTNShardNotFound {
   994  		// do we still have the uuid and shard id?
   995  		return moerr.NewTNShardNotFoundNoCtx("", 0xDEADBEAF)
   996  	}
   997  
   998  	if _, ok := possibleErrorMap[txnCode]; ok {
   999  		return txnError.UnwrapError()
  1000  	}
  1001  
  1002  	panic(moerr.NewInternalErrorNoCtx("invalid txn error, code %d, msg %s", txnCode, txnError.DebugString()))
  1003  }
  1004  
  1005  func (tc *txnOperator) checkResponseTxnStatusForCommit(resp txn.TxnResponse) error {
  1006  	if resp.TxnError != nil {
  1007  		return nil
  1008  	}
  1009  
  1010  	txnMeta := resp.Txn
  1011  	if txnMeta == nil {
  1012  		return moerr.NewTxnClosedNoCtx(tc.txnID)
  1013  	}
  1014  
  1015  	switch txnMeta.Status {
  1016  	case txn.TxnStatus_Committed, txn.TxnStatus_Aborted:
  1017  		return nil
  1018  	default:
  1019  		panic(moerr.NewInternalErrorNoCtx("invalid response status for commit, %v", txnMeta.Status))
  1020  	}
  1021  }
  1022  
  1023  func (tc *txnOperator) checkResponseTxnStatusForRollback(resp txn.TxnResponse) error {
  1024  	if resp.TxnError != nil {
  1025  		return nil
  1026  	}
  1027  
  1028  	txnMeta := resp.Txn
  1029  	if txnMeta == nil {
  1030  		return moerr.NewTxnClosedNoCtx(tc.txnID)
  1031  	}
  1032  
  1033  	switch txnMeta.Status {
  1034  	case txn.TxnStatus_Aborted:
  1035  		return nil
  1036  	default:
  1037  		panic(moerr.NewInternalErrorNoCtx("invalid response status for rollback %v", txnMeta.Status))
  1038  	}
  1039  }
  1040  
  1041  func (tc *txnOperator) trimResponses(result *rpc.SendResult, err error) (*rpc.SendResult, error) {
  1042  	if err != nil {
  1043  		return nil, err
  1044  	}
  1045  
  1046  	values := result.Responses[:0]
  1047  	for _, resp := range result.Responses {
  1048  		if !resp.HasFlag(txn.SkipResponseFlag) {
  1049  			values = append(values, resp)
  1050  		}
  1051  	}
  1052  	result.Responses = values
  1053  	return result, nil
  1054  }
  1055  
  1056  func (tc *txnOperator) unlock(ctx context.Context) {
  1057  	if !tc.commitAt.IsZero() {
  1058  		v2.TxnCNCommitResponseDurationHistogram.Observe(float64(time.Since(tc.commitAt).Seconds()))
  1059  	}
  1060  
  1061  	// rc mode need to see the committed value, so wait logtail applied
  1062  	if tc.mu.txn.IsRCIsolation() &&
  1063  		tc.timestampWaiter != nil {
  1064  		cost, err := tc.doCostAction(
  1065  			time.Time{},
  1066  			CommitWaitApplyEvent,
  1067  			func() error {
  1068  				_, err := tc.timestampWaiter.GetTimestamp(ctx, tc.mu.txn.CommitTS)
  1069  				return err
  1070  			},
  1071  			true)
  1072  		v2.TxnCNCommitWaitLogtailDurationHistogram.Observe(cost.Seconds())
  1073  
  1074  		if err != nil {
  1075  			util.GetLogger().Error("txn wait committed log applied failed in rc mode",
  1076  				util.TxnField(tc.mu.txn),
  1077  				zap.Error(err))
  1078  		}
  1079  	}
  1080  
  1081  	_, err := tc.doCostAction(
  1082  		time.Time{},
  1083  		UnlockEvent,
  1084  		func() error {
  1085  			return tc.lockService.Unlock(
  1086  				ctx,
  1087  				tc.mu.txn.ID,
  1088  				tc.mu.txn.CommitTS)
  1089  		},
  1090  		true)
  1091  	if err != nil {
  1092  		util.GetLogger().Error("failed to unlock txn",
  1093  			util.TxnField(tc.mu.txn),
  1094  			zap.Error(err))
  1095  	}
  1096  }
  1097  
  1098  func (tc *txnOperator) needUnlockLocked() bool {
  1099  	if tc.mu.txn.Mode ==
  1100  		txn.TxnMode_Optimistic {
  1101  		return false
  1102  	}
  1103  	return tc.lockService != nil
  1104  }
  1105  
  1106  func (tc *txnOperator) closeLocked() {
  1107  	if !tc.mu.closed {
  1108  		tc.mu.closed = true
  1109  		tc.triggerEventLocked(
  1110  			TxnEvent{
  1111  				Event: ClosedEvent,
  1112  				Txn:   tc.mu.txn,
  1113  			})
  1114  	}
  1115  }
  1116  
  1117  func (tc *txnOperator) AddWaitLock(tableID uint64, rows [][]byte, opt lock.LockOptions) uint64 {
  1118  	tc.mu.Lock()
  1119  	defer tc.mu.Unlock()
  1120  	if tc.mu.waitLocks == nil {
  1121  		tc.mu.waitLocks = make(map[uint64]Lock)
  1122  	}
  1123  
  1124  	seq := tc.mu.lockSeq
  1125  	tc.mu.lockSeq++
  1126  
  1127  	tc.mu.waitLocks[seq] = Lock{
  1128  		TableID: tableID,
  1129  		Rows:    rows,
  1130  		Options: opt,
  1131  	}
  1132  	return seq
  1133  }
  1134  
  1135  func (tc *txnOperator) RemoveWaitLock(key uint64) {
  1136  	tc.mu.Lock()
  1137  	defer tc.mu.Unlock()
  1138  
  1139  	delete(tc.mu.waitLocks, key)
  1140  }
  1141  
  1142  func (tc *txnOperator) LockTableCount() int32 {
  1143  	tc.mu.RLock()
  1144  	defer tc.mu.RUnlock()
  1145  	if tc.mu.txn.Mode != txn.TxnMode_Pessimistic {
  1146  		panic("lock in optimistic mode")
  1147  	}
  1148  	return int32(len(tc.mu.lockTables))
  1149  }
  1150  
  1151  func (tc *txnOperator) GetOverview() TxnOverview {
  1152  	tc.mu.RLock()
  1153  	defer tc.mu.RUnlock()
  1154  
  1155  	return TxnOverview{
  1156  		CreateAt:  tc.createAt,
  1157  		Meta:      tc.mu.txn,
  1158  		UserTxn:   tc.options.UserTxn(),
  1159  		WaitLocks: tc.getWaitLocksLocked(),
  1160  	}
  1161  }
  1162  
  1163  func (tc *txnOperator) getWaitLocksLocked() []Lock {
  1164  	if tc.mu.waitLocks == nil {
  1165  		return nil
  1166  	}
  1167  
  1168  	values := make([]Lock, 0, len(tc.mu.waitLocks))
  1169  	for _, l := range tc.mu.waitLocks {
  1170  		values = append(values, l)
  1171  	}
  1172  	return values
  1173  }
  1174  
  1175  func (tc *txnOperator) LockSkipped(
  1176  	tableID uint64,
  1177  	mode lock.LockMode) bool {
  1178  	if len(tc.options.SkipLockTables) == 0 {
  1179  		return false
  1180  	}
  1181  	for i, id := range tc.options.SkipLockTables {
  1182  		if id == tableID &&
  1183  			mode == tc.options.SkipLockTableModes[i] {
  1184  			return true
  1185  		}
  1186  	}
  1187  	return false
  1188  }
  1189  
  1190  func (tc *txnOperator) TxnOptions() txn.TxnOptions {
  1191  	return tc.options
  1192  }
  1193  
  1194  func (tc *txnOperator) NextSequence() uint64 {
  1195  	return tc.sequence.Add(1)
  1196  }
  1197  
  1198  func (tc *txnOperator) doCostAction(
  1199  	startAt time.Time,
  1200  	event EventType,
  1201  	action func() error,
  1202  	locked bool) (time.Duration, error) {
  1203  	if !locked {
  1204  		tc.mu.RLock()
  1205  		defer tc.mu.RUnlock()
  1206  	}
  1207  
  1208  	seq := tc.NextSequence()
  1209  	if startAt == (time.Time{}) {
  1210  		startAt = time.Now()
  1211  	}
  1212  
  1213  	tc.triggerEventLocked(
  1214  		newEvent(
  1215  			event,
  1216  			tc.mu.txn,
  1217  			seq,
  1218  			nil))
  1219  
  1220  	err := action()
  1221  	cost := time.Since(startAt)
  1222  	tc.triggerEventLocked(
  1223  		newCostEvent(
  1224  			event,
  1225  			tc.mu.txn,
  1226  			seq,
  1227  			err,
  1228  			time.Since(startAt)))
  1229  	return cost, err
  1230  }
  1231  
  1232  func (tc *txnOperator) EnterRunSql() {
  1233  	tc.runSqlCounter.addEnter()
  1234  }
  1235  
  1236  func (tc *txnOperator) ExitRunSql() {
  1237  	tc.runSqlCounter.addExit()
  1238  }
  1239  
  1240  func (tc *txnOperator) inRunSql() bool {
  1241  	return tc.runSqlCounter.more()
  1242  }
  1243  
  1244  func (tc *txnOperator) inCommit() bool {
  1245  	return tc.commitCounter.more()
  1246  }
  1247  
  1248  func (tc *txnOperator) inRollback() bool {
  1249  	return tc.rollbackCounter.more()
  1250  }
  1251  
  1252  func (tc *txnOperator) counter() string {
  1253  	return fmt.Sprintf("commit: %s rollback: %s runSql: %s",
  1254  		tc.commitCounter.String(),
  1255  		tc.rollbackCounter.String(),
  1256  		tc.runSqlCounter.String())
  1257  }