github.com/KinWaiYuen/client-go/v2@v2.5.4/txnkv/transaction/txn.go (about)

     1  // Copyright 2021 TiKV Authors
     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  // NOTE: The code in this file is based on code from the
    16  // TiDB project, licensed under the Apache License v 2.0
    17  //
    18  // https://github.com/pingcap/tidb/tree/cc5e161ac06827589c4966674597c137cc9e809c/store/tikv/txn.go
    19  //
    20  
    21  // Copyright 2016 PingCAP, Inc.
    22  //
    23  // Licensed under the Apache License, Version 2.0 (the "License");
    24  // you may not use this file except in compliance with the License.
    25  // You may obtain a copy of the License at
    26  //
    27  //     http://www.apache.org/licenses/LICENSE-2.0
    28  //
    29  // Unless required by applicable law or agreed to in writing, software
    30  // distributed under the License is distributed on an "AS IS" BASIS,
    31  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    32  // See the License for the specific language governing permissions and
    33  // limitations under the License.
    34  
    35  package transaction
    36  
    37  import (
    38  	"bytes"
    39  	"context"
    40  	"encoding/json"
    41  	"fmt"
    42  	"math/rand"
    43  	"runtime/trace"
    44  	"sort"
    45  	"sync"
    46  	"sync/atomic"
    47  	"time"
    48  
    49  	"github.com/KinWaiYuen/client-go/v2/config"
    50  	tikverr "github.com/KinWaiYuen/client-go/v2/error"
    51  	"github.com/KinWaiYuen/client-go/v2/internal/logutil"
    52  	"github.com/KinWaiYuen/client-go/v2/internal/retry"
    53  	"github.com/KinWaiYuen/client-go/v2/internal/unionstore"
    54  	tikv "github.com/KinWaiYuen/client-go/v2/kv"
    55  	"github.com/KinWaiYuen/client-go/v2/metrics"
    56  	"github.com/KinWaiYuen/client-go/v2/txnkv/txnsnapshot"
    57  	"github.com/KinWaiYuen/client-go/v2/txnkv/txnutil"
    58  	"github.com/KinWaiYuen/client-go/v2/util"
    59  	"github.com/dgryski/go-farm"
    60  	"github.com/opentracing/opentracing-go"
    61  	"github.com/pingcap/errors"
    62  	"github.com/pingcap/failpoint"
    63  	"github.com/pingcap/kvproto/pkg/kvrpcpb"
    64  	"go.uber.org/zap"
    65  )
    66  
    67  // MaxTxnTimeUse is the max time a Txn may use (in ms) from its begin to commit.
    68  // We use it to abort the transaction to guarantee GC worker will not influence it.
    69  const MaxTxnTimeUse = 24 * 60 * 60 * 1000
    70  
    71  // SchemaAmender is used by pessimistic transactions to amend commit mutations for schema change during 2pc.
    72  type SchemaAmender interface {
    73  	// AmendTxn is the amend entry, new mutations will be generated based on input mutations using schema change info.
    74  	// The returned results are mutations need to prewrite and mutations need to cleanup.
    75  	AmendTxn(ctx context.Context, startInfoSchema SchemaVer, change *RelatedSchemaChange, mutations CommitterMutations) (CommitterMutations, error)
    76  }
    77  
    78  // KVTxn contains methods to interact with a TiKV transaction.
    79  type KVTxn struct {
    80  	snapshot  *txnsnapshot.KVSnapshot
    81  	us        *unionstore.KVUnionStore
    82  	store     kvstore // for connection to region.
    83  	startTS   uint64
    84  	startTime time.Time // Monotonic timestamp for recording txn time consuming.
    85  	commitTS  uint64
    86  	mu        sync.Mutex // For thread-safe LockKeys function.
    87  	setCnt    int64
    88  	vars      *tikv.Variables
    89  	committer *twoPhaseCommitter
    90  	lockedCnt int
    91  
    92  	valid bool
    93  
    94  	// schemaVer is the infoSchema fetched at startTS.
    95  	schemaVer SchemaVer
    96  	// SchemaAmender is used amend pessimistic txn commit mutations for schema change
    97  	schemaAmender SchemaAmender
    98  	// commitCallback is called after current transaction gets committed
    99  	commitCallback func(info string, err error)
   100  
   101  	binlog             BinlogExecutor
   102  	schemaLeaseChecker SchemaLeaseChecker
   103  	syncLog            bool
   104  	priority           txnutil.Priority
   105  	isPessimistic      bool
   106  	enableAsyncCommit  bool
   107  	enable1PC          bool
   108  	causalConsistency  bool
   109  	scope              string
   110  	kvFilter           KVFilter
   111  	resourceGroupTag   []byte
   112  	diskFullOpt        kvrpcpb.DiskFullOpt
   113  }
   114  
   115  // NewTiKVTxn creates a new KVTxn.
   116  func NewTiKVTxn(store kvstore, snapshot *txnsnapshot.KVSnapshot, startTS uint64, scope string) (*KVTxn, error) {
   117  	cfg := config.GetGlobalConfig()
   118  	newTiKVTxn := &KVTxn{
   119  		snapshot:          snapshot,
   120  		us:                unionstore.NewUnionStore(snapshot),
   121  		store:             store,
   122  		startTS:           startTS,
   123  		startTime:         time.Now(),
   124  		valid:             true,
   125  		vars:              tikv.DefaultVars,
   126  		scope:             scope,
   127  		enableAsyncCommit: cfg.EnableAsyncCommit,
   128  		enable1PC:         cfg.Enable1PC,
   129  		diskFullOpt:       kvrpcpb.DiskFullOpt_NotAllowedOnFull,
   130  	}
   131  	return newTiKVTxn, nil
   132  }
   133  
   134  // SetSuccess is used to probe if kv variables are set or not. It is ONLY used in test cases.
   135  var SetSuccess = false
   136  
   137  // SetVars sets variables to the transaction.
   138  func (txn *KVTxn) SetVars(vars *tikv.Variables) {
   139  	txn.vars = vars
   140  	txn.snapshot.SetVars(vars)
   141  	if val, err := util.EvalFailpoint("probeSetVars"); err == nil {
   142  		if val.(bool) {
   143  			SetSuccess = true
   144  		}
   145  	}
   146  }
   147  
   148  // GetVars gets variables from the transaction.
   149  func (txn *KVTxn) GetVars() *tikv.Variables {
   150  	return txn.vars
   151  }
   152  
   153  // Get implements transaction interface.
   154  func (txn *KVTxn) Get(ctx context.Context, k []byte) ([]byte, error) {
   155  	ret, err := txn.us.Get(ctx, k)
   156  	if tikverr.IsErrNotFound(err) {
   157  		return nil, err
   158  	}
   159  	if err != nil {
   160  		return nil, errors.Trace(err)
   161  	}
   162  
   163  	return ret, nil
   164  }
   165  
   166  // BatchGet gets kv from the memory buffer of statement and transaction, and the kv storage.
   167  // Do not use len(value) == 0 or value == nil to represent non-exist.
   168  // If a key doesn't exist, there shouldn't be any corresponding entry in the result map.
   169  func (txn *KVTxn) BatchGet(ctx context.Context, keys [][]byte) (map[string][]byte, error) {
   170  	return NewBufferBatchGetter(txn.GetMemBuffer(), txn.GetSnapshot()).BatchGet(ctx, keys)
   171  }
   172  
   173  // Set sets the value for key k as v into kv store.
   174  // v must NOT be nil or empty, otherwise it returns ErrCannotSetNilValue.
   175  func (txn *KVTxn) Set(k []byte, v []byte) error {
   176  	txn.setCnt++
   177  	return txn.us.GetMemBuffer().Set(k, v)
   178  }
   179  
   180  // String implements fmt.Stringer interface.
   181  func (txn *KVTxn) String() string {
   182  	return fmt.Sprintf("%d", txn.StartTS())
   183  }
   184  
   185  // Iter creates an Iterator positioned on the first entry that k <= entry's key.
   186  // If such entry is not found, it returns an invalid Iterator with no error.
   187  // It yields only keys that < upperBound. If upperBound is nil, it means the upperBound is unbounded.
   188  // The Iterator must be Closed after use.
   189  func (txn *KVTxn) Iter(k []byte, upperBound []byte) (unionstore.Iterator, error) {
   190  	return txn.us.Iter(k, upperBound)
   191  }
   192  
   193  // IterReverse creates a reversed Iterator positioned on the first entry which key is less than k.
   194  func (txn *KVTxn) IterReverse(k []byte) (unionstore.Iterator, error) {
   195  	return txn.us.IterReverse(k)
   196  }
   197  
   198  // Delete removes the entry for key k from kv store.
   199  func (txn *KVTxn) Delete(k []byte) error {
   200  	return txn.us.GetMemBuffer().Delete(k)
   201  }
   202  
   203  // SetSchemaLeaseChecker sets a hook to check schema version.
   204  func (txn *KVTxn) SetSchemaLeaseChecker(checker SchemaLeaseChecker) {
   205  	txn.schemaLeaseChecker = checker
   206  }
   207  
   208  // EnableForceSyncLog indicates tikv to always sync log for the transaction.
   209  func (txn *KVTxn) EnableForceSyncLog() {
   210  	txn.syncLog = true
   211  }
   212  
   213  // SetPessimistic indicates if the transaction should use pessimictic lock.
   214  func (txn *KVTxn) SetPessimistic(b bool) {
   215  	txn.isPessimistic = b
   216  }
   217  
   218  // SetSchemaVer updates schema version to validate transaction.
   219  func (txn *KVTxn) SetSchemaVer(schemaVer SchemaVer) {
   220  	txn.schemaVer = schemaVer
   221  }
   222  
   223  // SetPriority sets the priority for both write and read.
   224  func (txn *KVTxn) SetPriority(pri txnutil.Priority) {
   225  	txn.priority = pri
   226  	txn.GetSnapshot().SetPriority(pri)
   227  }
   228  
   229  // SetResourceGroupTag sets the resource tag for both write and read.
   230  func (txn *KVTxn) SetResourceGroupTag(tag []byte) {
   231  	txn.resourceGroupTag = tag
   232  	txn.GetSnapshot().SetResourceGroupTag(tag)
   233  }
   234  
   235  // SetSchemaAmender sets an amender to update mutations after schema change.
   236  func (txn *KVTxn) SetSchemaAmender(sa SchemaAmender) {
   237  	txn.schemaAmender = sa
   238  }
   239  
   240  // SetCommitCallback sets up a function that will be called when the transaction
   241  // is finished.
   242  func (txn *KVTxn) SetCommitCallback(f func(string, error)) {
   243  	txn.commitCallback = f
   244  }
   245  
   246  // SetEnableAsyncCommit indicates if the transaction will try to use async commit.
   247  func (txn *KVTxn) SetEnableAsyncCommit(b bool) {
   248  	txn.enableAsyncCommit = b
   249  }
   250  
   251  // SetEnable1PC indicates that the transaction will try to use 1 phase commit(which should be faster).
   252  // 1PC does not work if the keys to update in the current txn are in multiple regions.
   253  func (txn *KVTxn) SetEnable1PC(b bool) {
   254  	txn.enable1PC = b
   255  }
   256  
   257  // SetCausalConsistency indicates if the transaction does not need to
   258  // guarantee linearizability. Default value is false which means
   259  // linearizability is guaranteed.
   260  func (txn *KVTxn) SetCausalConsistency(b bool) {
   261  	txn.causalConsistency = b
   262  }
   263  
   264  // SetScope sets the geographical scope of the transaction.
   265  func (txn *KVTxn) SetScope(scope string) {
   266  	txn.scope = scope
   267  }
   268  
   269  // SetKVFilter sets the filter to ignore key-values in memory buffer.
   270  func (txn *KVTxn) SetKVFilter(filter KVFilter) {
   271  	txn.kvFilter = filter
   272  }
   273  
   274  // SetDiskFullOpt sets whether current operation is allowed in each TiKV disk usage level.
   275  func (txn *KVTxn) SetDiskFullOpt(level kvrpcpb.DiskFullOpt) {
   276  	txn.diskFullOpt = level
   277  }
   278  
   279  // GetDiskFullOpt gets the options of current operation in each TiKV disk usage level.
   280  func (txn *KVTxn) GetDiskFullOpt() kvrpcpb.DiskFullOpt {
   281  	return txn.diskFullOpt
   282  }
   283  
   284  // ClearDiskFullOpt clears the options of current operation in each tikv disk usage level.
   285  func (txn *KVTxn) ClearDiskFullOpt() {
   286  	txn.diskFullOpt = kvrpcpb.DiskFullOpt_NotAllowedOnFull
   287  }
   288  
   289  // IsPessimistic returns true if it is pessimistic.
   290  func (txn *KVTxn) IsPessimistic() bool {
   291  	return txn.isPessimistic
   292  }
   293  
   294  // IsCasualConsistency returns if the transaction allows linearizability
   295  // inconsistency.
   296  func (txn *KVTxn) IsCasualConsistency() bool {
   297  	return txn.causalConsistency
   298  }
   299  
   300  // GetScope returns the geographical scope of the transaction.
   301  func (txn *KVTxn) GetScope() string {
   302  	return txn.scope
   303  }
   304  
   305  // Commit commits the transaction operations to KV store.
   306  func (txn *KVTxn) Commit(ctx context.Context) error {
   307  	if span := opentracing.SpanFromContext(ctx); span != nil && span.Tracer() != nil {
   308  		span1 := span.Tracer().StartSpan("tikvTxn.Commit", opentracing.ChildOf(span.Context()))
   309  		defer span1.Finish()
   310  		ctx = opentracing.ContextWithSpan(ctx, span1)
   311  	}
   312  	defer trace.StartRegion(ctx, "CommitTxn").End()
   313  
   314  	if !txn.valid {
   315  		return tikverr.ErrInvalidTxn
   316  	}
   317  	defer txn.close()
   318  
   319  	if val, err := util.EvalFailpoint("mockCommitError"); err == nil && val.(bool) {
   320  		if _, err := util.EvalFailpoint("mockCommitErrorOpt"); err == nil {
   321  			failpoint.Disable("tikvclient/mockCommitErrorOpt")
   322  			return errors.New("mock commit error")
   323  		}
   324  	}
   325  
   326  	start := time.Now()
   327  	defer func() { metrics.TxnCmdHistogramWithCommit.Observe(time.Since(start).Seconds()) }()
   328  
   329  	// sessionID is used for log.
   330  	var sessionID uint64
   331  	val := ctx.Value(util.SessionID)
   332  	if val != nil {
   333  		sessionID = val.(uint64)
   334  	}
   335  
   336  	var err error
   337  	// If the txn use pessimistic lock, committer is initialized.
   338  	committer := txn.committer
   339  	if committer == nil {
   340  		committer, err = newTwoPhaseCommitter(txn, sessionID)
   341  		if err != nil {
   342  			return errors.Trace(err)
   343  		}
   344  		txn.committer = committer
   345  	}
   346  
   347  	txn.committer.SetDiskFullOpt(txn.diskFullOpt)
   348  
   349  	defer committer.ttlManager.close()
   350  
   351  	initRegion := trace.StartRegion(ctx, "InitKeys")
   352  	err = committer.initKeysAndMutations()
   353  	initRegion.End()
   354  	if err != nil {
   355  		return errors.Trace(err)
   356  	}
   357  	if committer.mutations.Len() == 0 {
   358  		return nil
   359  	}
   360  
   361  	defer func() {
   362  		detail := committer.getDetail()
   363  		detail.Mu.Lock()
   364  		metrics.TiKVTxnCommitBackoffSeconds.Observe(float64(detail.Mu.CommitBackoffTime) / float64(time.Second))
   365  		metrics.TiKVTxnCommitBackoffCount.Observe(float64(len(detail.Mu.BackoffTypes)))
   366  		detail.Mu.Unlock()
   367  
   368  		ctxValue := ctx.Value(util.CommitDetailCtxKey)
   369  		if ctxValue != nil {
   370  			commitDetail := ctxValue.(**util.CommitDetails)
   371  			if *commitDetail != nil {
   372  				(*commitDetail).TxnRetry++
   373  			} else {
   374  				*commitDetail = detail
   375  			}
   376  		}
   377  	}()
   378  	// latches disabled
   379  	// pessimistic transaction should also bypass latch.
   380  	if txn.store.TxnLatches() == nil || txn.IsPessimistic() {
   381  		err = committer.execute(ctx)
   382  		if val == nil || sessionID > 0 {
   383  			txn.onCommitted(err)
   384  		}
   385  		logutil.Logger(ctx).Debug("[kv] txnLatches disabled, 2pc directly", zap.Error(err))
   386  		return errors.Trace(err)
   387  	}
   388  
   389  	// latches enabled
   390  	// for transactions which need to acquire latches
   391  	start = time.Now()
   392  	lock := txn.store.TxnLatches().Lock(committer.startTS, committer.mutations.GetKeys())
   393  	commitDetail := committer.getDetail()
   394  	commitDetail.LocalLatchTime = time.Since(start)
   395  	if commitDetail.LocalLatchTime > 0 {
   396  		metrics.TiKVLocalLatchWaitTimeHistogram.Observe(commitDetail.LocalLatchTime.Seconds())
   397  	}
   398  	defer txn.store.TxnLatches().UnLock(lock)
   399  	if lock.IsStale() {
   400  		return &tikverr.ErrWriteConflictInLatch{StartTS: txn.startTS}
   401  	}
   402  	err = committer.execute(ctx)
   403  	if val == nil || sessionID > 0 {
   404  		txn.onCommitted(err)
   405  	}
   406  	if err == nil {
   407  		lock.SetCommitTS(committer.commitTS)
   408  	}
   409  	logutil.Logger(ctx).Debug("[kv] txnLatches enabled while txn retryable", zap.Error(err))
   410  	return errors.Trace(err)
   411  }
   412  
   413  func (txn *KVTxn) close() {
   414  	txn.valid = false
   415  	txn.ClearDiskFullOpt()
   416  }
   417  
   418  // Rollback undoes the transaction operations to KV store.
   419  func (txn *KVTxn) Rollback() error {
   420  	if !txn.valid {
   421  		return tikverr.ErrInvalidTxn
   422  	}
   423  	start := time.Now()
   424  	// Clean up pessimistic lock.
   425  	if txn.IsPessimistic() && txn.committer != nil {
   426  		err := txn.rollbackPessimisticLocks()
   427  		txn.committer.ttlManager.close()
   428  		if err != nil {
   429  			logutil.BgLogger().Error(err.Error())
   430  		}
   431  	}
   432  	txn.close()
   433  	logutil.BgLogger().Debug("[kv] rollback txn", zap.Uint64("txnStartTS", txn.StartTS()))
   434  	metrics.TxnCmdHistogramWithRollback.Observe(time.Since(start).Seconds())
   435  	return nil
   436  }
   437  
   438  func (txn *KVTxn) rollbackPessimisticLocks() error {
   439  	if txn.lockedCnt == 0 {
   440  		return nil
   441  	}
   442  	bo := retry.NewBackofferWithVars(context.Background(), cleanupMaxBackoff, txn.vars)
   443  	keys := txn.collectLockedKeys()
   444  	return txn.committer.pessimisticRollbackMutations(bo, &PlainMutations{keys: keys})
   445  }
   446  
   447  func (txn *KVTxn) collectLockedKeys() [][]byte {
   448  	keys := make([][]byte, 0, txn.lockedCnt)
   449  	buf := txn.GetMemBuffer()
   450  	var err error
   451  	for it := buf.IterWithFlags(nil, nil); it.Valid(); err = it.Next() {
   452  		_ = err
   453  		if it.Flags().HasLocked() {
   454  			keys = append(keys, it.Key())
   455  		}
   456  	}
   457  	return keys
   458  }
   459  
   460  // TxnInfo is used to keep track the info of a committed transaction (mainly for diagnosis and testing)
   461  type TxnInfo struct {
   462  	TxnScope            string `json:"txn_scope"`
   463  	StartTS             uint64 `json:"start_ts"`
   464  	CommitTS            uint64 `json:"commit_ts"`
   465  	TxnCommitMode       string `json:"txn_commit_mode"`
   466  	AsyncCommitFallback bool   `json:"async_commit_fallback"`
   467  	OnePCFallback       bool   `json:"one_pc_fallback"`
   468  	ErrMsg              string `json:"error,omitempty"`
   469  }
   470  
   471  func (txn *KVTxn) onCommitted(err error) {
   472  	if txn.commitCallback != nil {
   473  		isAsyncCommit := txn.committer.isAsyncCommit()
   474  		isOnePC := txn.committer.isOnePC()
   475  
   476  		commitMode := "2pc"
   477  		if isOnePC {
   478  			commitMode = "1pc"
   479  		} else if isAsyncCommit {
   480  			commitMode = "async_commit"
   481  		}
   482  
   483  		info := TxnInfo{
   484  			TxnScope:            txn.GetScope(),
   485  			StartTS:             txn.startTS,
   486  			CommitTS:            txn.commitTS,
   487  			TxnCommitMode:       commitMode,
   488  			AsyncCommitFallback: txn.committer.hasTriedAsyncCommit && !isAsyncCommit,
   489  			OnePCFallback:       txn.committer.hasTriedOnePC && !isOnePC,
   490  		}
   491  		if err != nil {
   492  			info.ErrMsg = err.Error()
   493  		}
   494  		infoStr, err2 := json.Marshal(info)
   495  		_ = err2
   496  		txn.commitCallback(string(infoStr), err)
   497  	}
   498  }
   499  
   500  // LockKeysWithWaitTime tries to lock the entries with the keys in KV store.
   501  // lockWaitTime in ms, 0 means nowait lock.
   502  func (txn *KVTxn) LockKeysWithWaitTime(ctx context.Context, lockWaitTime int64, keysInput ...[]byte) (err error) {
   503  	forUpdateTs := txn.startTS
   504  	if txn.IsPessimistic() {
   505  		bo := retry.NewBackofferWithVars(context.Background(), TsoMaxBackoff, nil)
   506  		forUpdateTs, err = txn.store.GetTimestampWithRetry(bo, txn.scope)
   507  		if err != nil {
   508  			return err
   509  		}
   510  	}
   511  	lockCtx := tikv.NewLockCtx(forUpdateTs, lockWaitTime, time.Now())
   512  
   513  	return txn.LockKeys(ctx, lockCtx, keysInput...)
   514  }
   515  
   516  // LockKeys tries to lock the entries with the keys in KV store.
   517  // lockCtx is the context for lock, lockCtx.lockWaitTime in ms
   518  func (txn *KVTxn) LockKeys(ctx context.Context, lockCtx *tikv.LockCtx, keysInput ...[]byte) error {
   519  	// Exclude keys that are already locked.
   520  	var err error
   521  	keys := make([][]byte, 0, len(keysInput))
   522  	startTime := time.Now()
   523  	txn.mu.Lock()
   524  	defer txn.mu.Unlock()
   525  	defer func() {
   526  		metrics.TxnCmdHistogramWithLockKeys.Observe(time.Since(startTime).Seconds())
   527  		if err == nil {
   528  			if lockCtx.PessimisticLockWaited != nil {
   529  				if atomic.LoadInt32(lockCtx.PessimisticLockWaited) > 0 {
   530  					timeWaited := time.Since(lockCtx.WaitStartTime)
   531  					atomic.StoreInt64(lockCtx.LockKeysDuration, int64(timeWaited))
   532  					metrics.TiKVPessimisticLockKeysDuration.Observe(timeWaited.Seconds())
   533  				}
   534  			}
   535  		}
   536  		if lockCtx.LockKeysCount != nil {
   537  			*lockCtx.LockKeysCount += int32(len(keys))
   538  		}
   539  		if lockCtx.Stats != nil {
   540  			lockCtx.Stats.TotalTime = time.Since(startTime)
   541  			ctxValue := ctx.Value(util.LockKeysDetailCtxKey)
   542  			if ctxValue != nil {
   543  				lockKeysDetail := ctxValue.(**util.LockKeysDetails)
   544  				*lockKeysDetail = lockCtx.Stats
   545  			}
   546  		}
   547  	}()
   548  	memBuf := txn.us.GetMemBuffer()
   549  	for _, key := range keysInput {
   550  		// The value of lockedMap is only used by pessimistic transactions.
   551  		var valueExist, locked, checkKeyExists bool
   552  		if flags, err := memBuf.GetFlags(key); err == nil {
   553  			locked = flags.HasLocked()
   554  			valueExist = flags.HasLockedValueExists()
   555  			checkKeyExists = flags.HasNeedCheckExists()
   556  		}
   557  		if !locked {
   558  			keys = append(keys, key)
   559  		} else if txn.IsPessimistic() {
   560  			if checkKeyExists && valueExist {
   561  				alreadyExist := kvrpcpb.AlreadyExist{Key: key}
   562  				e := &tikverr.ErrKeyExist{AlreadyExist: &alreadyExist}
   563  				return txn.committer.extractKeyExistsErr(e)
   564  			}
   565  		}
   566  		if lockCtx.ReturnValues && locked {
   567  			// An already locked key can not return values, we add an entry to let the caller get the value
   568  			// in other ways.
   569  			lockCtx.Values[string(key)] = tikv.ReturnedValue{AlreadyLocked: true}
   570  		}
   571  	}
   572  	if len(keys) == 0 {
   573  		return nil
   574  	}
   575  	keys = deduplicateKeys(keys)
   576  	if txn.IsPessimistic() && lockCtx.ForUpdateTS > 0 {
   577  		if txn.committer == nil {
   578  			// sessionID is used for log.
   579  			var sessionID uint64
   580  			var err error
   581  			val := ctx.Value(util.SessionID)
   582  			if val != nil {
   583  				sessionID = val.(uint64)
   584  			}
   585  			txn.committer, err = newTwoPhaseCommitter(txn, sessionID)
   586  			if err != nil {
   587  				return err
   588  			}
   589  		}
   590  		var assignedPrimaryKey bool
   591  		if txn.committer.primaryKey == nil {
   592  			txn.committer.primaryKey = keys[0]
   593  			assignedPrimaryKey = true
   594  		}
   595  
   596  		lockCtx.Stats = &util.LockKeysDetails{
   597  			LockKeys: int32(len(keys)),
   598  		}
   599  		bo := retry.NewBackofferWithVars(ctx, pessimisticLockMaxBackoff, txn.vars)
   600  		txn.committer.forUpdateTS = lockCtx.ForUpdateTS
   601  		// If the number of keys greater than 1, it can be on different region,
   602  		// concurrently execute on multiple regions may lead to deadlock.
   603  		txn.committer.isFirstLock = txn.lockedCnt == 0 && len(keys) == 1
   604  		err = txn.committer.pessimisticLockMutations(bo, lockCtx, &PlainMutations{keys: keys})
   605  		if bo.GetTotalSleep() > 0 {
   606  			atomic.AddInt64(&lockCtx.Stats.BackoffTime, int64(bo.GetTotalSleep())*int64(time.Millisecond))
   607  			lockCtx.Stats.Mu.Lock()
   608  			lockCtx.Stats.Mu.BackoffTypes = append(lockCtx.Stats.Mu.BackoffTypes, bo.GetTypes()...)
   609  			lockCtx.Stats.Mu.Unlock()
   610  		}
   611  		if lockCtx.Killed != nil {
   612  			// If the kill signal is received during waiting for pessimisticLock,
   613  			// pessimisticLockKeys would handle the error but it doesn't reset the flag.
   614  			// We need to reset the killed flag here.
   615  			atomic.CompareAndSwapUint32(lockCtx.Killed, 1, 0)
   616  		}
   617  		if err != nil {
   618  			for _, key := range keys {
   619  				if txn.us.HasPresumeKeyNotExists(key) {
   620  					txn.us.UnmarkPresumeKeyNotExists(key)
   621  				}
   622  			}
   623  			keyMayBeLocked := !(tikverr.IsErrWriteConflict(err) || tikverr.IsErrKeyExist(err))
   624  			// If there is only 1 key and lock fails, no need to do pessimistic rollback.
   625  			if len(keys) > 1 || keyMayBeLocked {
   626  				dl, isDeadlock := errors.Cause(err).(*tikverr.ErrDeadlock)
   627  				if isDeadlock {
   628  					if hashInKeys(dl.DeadlockKeyHash, keys) {
   629  						dl.IsRetryable = true
   630  					}
   631  					if lockCtx.OnDeadlock != nil {
   632  						// Call OnDeadlock before pessimistic rollback.
   633  						lockCtx.OnDeadlock(dl)
   634  					}
   635  				}
   636  
   637  				wg := txn.asyncPessimisticRollback(ctx, keys)
   638  
   639  				if isDeadlock {
   640  					logutil.Logger(ctx).Debug("deadlock error received", zap.Uint64("startTS", txn.startTS), zap.Stringer("deadlockInfo", dl))
   641  					if dl.IsRetryable {
   642  						// Wait for the pessimistic rollback to finish before we retry the statement.
   643  						wg.Wait()
   644  						// Sleep a little, wait for the other transaction that blocked by this transaction to acquire the lock.
   645  						time.Sleep(time.Millisecond * 5)
   646  						if _, err := util.EvalFailpoint("SingleStmtDeadLockRetrySleep"); err == nil {
   647  							time.Sleep(300 * time.Millisecond)
   648  						}
   649  					}
   650  				}
   651  			}
   652  			if assignedPrimaryKey {
   653  				// unset the primary key and stop heartbeat if we assigned primary key when failed to lock it.
   654  				txn.committer.primaryKey = nil
   655  				txn.committer.ttlManager.reset()
   656  			}
   657  			return err
   658  		}
   659  	}
   660  	for _, key := range keys {
   661  		valExists := tikv.SetKeyLockedValueExists
   662  		// PointGet and BatchPointGet will return value in pessimistic lock response, the value may not exist.
   663  		// For other lock modes, the locked key values always exist.
   664  		if lockCtx.ReturnValues {
   665  			val := lockCtx.Values[string(key)]
   666  			if len(val.Value) == 0 {
   667  				valExists = tikv.SetKeyLockedValueNotExists
   668  			}
   669  		}
   670  		memBuf.UpdateFlags(key, tikv.SetKeyLocked, tikv.DelNeedCheckExists, valExists)
   671  	}
   672  	txn.lockedCnt += len(keys)
   673  	return nil
   674  }
   675  
   676  // deduplicateKeys deduplicate the keys, it use sort instead of map to avoid memory allocation.
   677  func deduplicateKeys(keys [][]byte) [][]byte {
   678  	sort.Slice(keys, func(i, j int) bool {
   679  		return bytes.Compare(keys[i], keys[j]) < 0
   680  	})
   681  	deduped := keys[:1]
   682  	for i := 1; i < len(keys); i++ {
   683  		if !bytes.Equal(deduped[len(deduped)-1], keys[i]) {
   684  			deduped = append(deduped, keys[i])
   685  		}
   686  	}
   687  	return deduped
   688  }
   689  
   690  const pessimisticRollbackMaxBackoff = 20000
   691  
   692  func (txn *KVTxn) asyncPessimisticRollback(ctx context.Context, keys [][]byte) *sync.WaitGroup {
   693  	// Clone a new committer for execute in background.
   694  	committer := &twoPhaseCommitter{
   695  		store:       txn.committer.store,
   696  		sessionID:   txn.committer.sessionID,
   697  		startTS:     txn.committer.startTS,
   698  		forUpdateTS: txn.committer.forUpdateTS,
   699  		primaryKey:  txn.committer.primaryKey,
   700  	}
   701  	wg := new(sync.WaitGroup)
   702  	wg.Add(1)
   703  	go func() {
   704  		if val, err := util.EvalFailpoint("beforeAsyncPessimisticRollback"); err == nil {
   705  			if s, ok := val.(string); ok {
   706  				if s == "skip" {
   707  					logutil.Logger(ctx).Info("[failpoint] injected skip async pessimistic rollback",
   708  						zap.Uint64("txnStartTS", txn.startTS))
   709  					wg.Done()
   710  					return
   711  				} else if s == "delay" {
   712  					duration := time.Duration(rand.Int63n(int64(time.Second) * 2))
   713  					logutil.Logger(ctx).Info("[failpoint] injected delay before async pessimistic rollback",
   714  						zap.Uint64("txnStartTS", txn.startTS), zap.Duration("duration", duration))
   715  					time.Sleep(duration)
   716  				}
   717  			}
   718  		}
   719  
   720  		err := committer.pessimisticRollbackMutations(retry.NewBackofferWithVars(ctx, pessimisticRollbackMaxBackoff, txn.vars), &PlainMutations{keys: keys})
   721  		if err != nil {
   722  			logutil.Logger(ctx).Warn("[kv] pessimisticRollback failed.", zap.Error(err))
   723  		}
   724  		wg.Done()
   725  	}()
   726  	return wg
   727  }
   728  
   729  func hashInKeys(deadlockKeyHash uint64, keys [][]byte) bool {
   730  	for _, key := range keys {
   731  		if farm.Fingerprint64(key) == deadlockKeyHash {
   732  			return true
   733  		}
   734  	}
   735  	return false
   736  }
   737  
   738  // IsReadOnly checks if the transaction has only performed read operations.
   739  func (txn *KVTxn) IsReadOnly() bool {
   740  	return !txn.us.GetMemBuffer().Dirty()
   741  }
   742  
   743  // StartTS returns the transaction start timestamp.
   744  func (txn *KVTxn) StartTS() uint64 {
   745  	return txn.startTS
   746  }
   747  
   748  // Valid returns if the transaction is valid.
   749  // A transaction become invalid after commit or rollback.
   750  func (txn *KVTxn) Valid() bool {
   751  	return txn.valid
   752  }
   753  
   754  // Len returns the number of entries in the DB.
   755  func (txn *KVTxn) Len() int {
   756  	return txn.us.GetMemBuffer().Len()
   757  }
   758  
   759  // Size returns sum of keys and values length.
   760  func (txn *KVTxn) Size() int {
   761  	return txn.us.GetMemBuffer().Size()
   762  }
   763  
   764  // Reset reset the Transaction to initial states.
   765  func (txn *KVTxn) Reset() {
   766  	txn.us.GetMemBuffer().Reset()
   767  }
   768  
   769  // GetUnionStore returns the UnionStore binding to this transaction.
   770  func (txn *KVTxn) GetUnionStore() *unionstore.KVUnionStore {
   771  	return txn.us
   772  }
   773  
   774  // GetMemBuffer return the MemBuffer binding to this transaction.
   775  func (txn *KVTxn) GetMemBuffer() *unionstore.MemDB {
   776  	return txn.us.GetMemBuffer()
   777  }
   778  
   779  // GetSnapshot returns the Snapshot binding to this transaction.
   780  func (txn *KVTxn) GetSnapshot() *txnsnapshot.KVSnapshot {
   781  	return txn.snapshot
   782  }
   783  
   784  // SetBinlogExecutor sets the method to perform binlong synchronization.
   785  func (txn *KVTxn) SetBinlogExecutor(binlog BinlogExecutor) {
   786  	txn.binlog = binlog
   787  	if txn.committer != nil {
   788  		txn.committer.binlog = binlog
   789  	}
   790  }
   791  
   792  // GetClusterID returns store's cluster id.
   793  func (txn *KVTxn) GetClusterID() uint64 {
   794  	return txn.store.GetClusterID()
   795  }