github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/causetstore/ekv/txn.go (about)

     1  // Copyright 2020 WHTCORPS INC, Inc.
     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  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package ekv
    15  
    16  import (
    17  	"context"
    18  	"math"
    19  	"math/rand"
    20  	"sync/atomic"
    21  	"time"
    22  
    23  	"github.com/whtcorpsinc/BerolinaSQL/terror"
    24  	"github.com/whtcorpsinc/milevadb/soliton/logutil"
    25  	"go.uber.org/zap"
    26  )
    27  
    28  // RunInNewTxn will run the f in a new transaction environment.
    29  func RunInNewTxn(causetstore CausetStorage, retryable bool, f func(txn Transaction) error) error {
    30  	var (
    31  		err           error
    32  		originalTxnTS uint64
    33  		txn           Transaction
    34  	)
    35  	for i := uint(0); i < maxRetryCnt; i++ {
    36  		txn, err = causetstore.Begin()
    37  		if err != nil {
    38  			logutil.BgLogger().Error("RunInNewTxn", zap.Error(err))
    39  			return err
    40  		}
    41  
    42  		// originalTxnTS is used to trace the original transaction when the function is retryable.
    43  		if i == 0 {
    44  			originalTxnTS = txn.StartTS()
    45  		}
    46  
    47  		err = f(txn)
    48  		if err != nil {
    49  			err1 := txn.Rollback()
    50  			terror.Log(err1)
    51  			if retryable && IsTxnRetryableError(err) {
    52  				logutil.BgLogger().Warn("RunInNewTxn",
    53  					zap.Uint64("retry txn", txn.StartTS()),
    54  					zap.Uint64("original txn", originalTxnTS),
    55  					zap.Error(err))
    56  				continue
    57  			}
    58  			return err
    59  		}
    60  
    61  		err = txn.Commit(context.Background())
    62  		if err == nil {
    63  			break
    64  		}
    65  		if retryable && IsTxnRetryableError(err) {
    66  			logutil.BgLogger().Warn("RunInNewTxn",
    67  				zap.Uint64("retry txn", txn.StartTS()),
    68  				zap.Uint64("original txn", originalTxnTS),
    69  				zap.Error(err))
    70  			BackOff(i)
    71  			continue
    72  		}
    73  		return err
    74  	}
    75  	return err
    76  }
    77  
    78  var (
    79  	// maxRetryCnt represents maximum retry times in RunInNewTxn.
    80  	maxRetryCnt uint = 100
    81  	// retryBackOffBase is the initial duration, in microsecond, a failed transaction stays dormancy before it retries
    82  	retryBackOffBase = 1
    83  	// retryBackOffCap is the max amount of duration, in microsecond, a failed transaction stays dormancy before it retries
    84  	retryBackOffCap = 100
    85  )
    86  
    87  // BackOff Implements exponential backoff with full jitter.
    88  // Returns real back off time in microsecond.
    89  // See http://www.awsarchitectureblog.com/2020/03/backoff.html.
    90  func BackOff(attempts uint) int {
    91  	upper := int(math.Min(float64(retryBackOffCap), float64(retryBackOffBase)*math.Pow(2.0, float64(attempts))))
    92  	sleep := time.Duration(rand.Intn(upper)) * time.Millisecond
    93  	time.Sleep(sleep)
    94  	return int(sleep)
    95  }
    96  
    97  // mockCommitErrorEnable uses to enable `mockCommitError` and only mock error once.
    98  var mockCommitErrorEnable = int64(0)
    99  
   100  // MockCommitErrorEnable exports for gofail testing.
   101  func MockCommitErrorEnable() {
   102  	atomic.StoreInt64(&mockCommitErrorEnable, 1)
   103  }
   104  
   105  // MockCommitErrorDisable exports for gofail testing.
   106  func MockCommitErrorDisable() {
   107  	atomic.StoreInt64(&mockCommitErrorEnable, 0)
   108  }
   109  
   110  // IsMockCommitErrorEnable exports for gofail testing.
   111  func IsMockCommitErrorEnable() bool {
   112  	return atomic.LoadInt64(&mockCommitErrorEnable) == 1
   113  }
   114  
   115  // TxnInfo is used to keep track the info of a committed transaction (mainly for diagnosis and testing)
   116  type TxnInfo struct {
   117  	StartTS  uint64 `json:"start_ts"`
   118  	CommitTS uint64 `json:"commit_ts"`
   119  	ErrMsg   string `json:"error,omitempty"`
   120  }