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 }