github.com/KinWaiYuen/client-go/v2@v2.5.4/internal/retry/config.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/retry/config.go 19 // 20 21 // Copyright 2021 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 retry 36 37 import ( 38 "context" 39 "math" 40 "math/rand" 41 "strings" 42 "time" 43 44 tikverr "github.com/KinWaiYuen/client-go/v2/error" 45 "github.com/KinWaiYuen/client-go/v2/internal/logutil" 46 "github.com/KinWaiYuen/client-go/v2/kv" 47 "github.com/KinWaiYuen/client-go/v2/metrics" 48 "github.com/prometheus/client_golang/prometheus" 49 "go.uber.org/zap" 50 ) 51 52 // Config is the configuration of the Backoff function. 53 type Config struct { 54 name string 55 metric *prometheus.Observer 56 fnCfg *BackoffFnCfg 57 err error 58 } 59 60 // backoffFn is the backoff function which compute the sleep time and do sleep. 61 type backoffFn func(ctx context.Context, maxSleepMs int) int 62 63 func (c *Config) createBackoffFn(vars *kv.Variables) backoffFn { 64 if strings.EqualFold(c.name, txnLockFastName) { 65 return newBackoffFn(vars.BackoffLockFast, c.fnCfg.cap, c.fnCfg.jitter) 66 } 67 return newBackoffFn(c.fnCfg.base, c.fnCfg.cap, c.fnCfg.jitter) 68 } 69 70 // BackoffFnCfg is the configuration for the backoff func which implements exponential backoff with 71 // optional jitters. 72 // See http://www.awsarchitectureblog.com/2015/03/backoff.html 73 type BackoffFnCfg struct { 74 base int 75 cap int 76 jitter int 77 } 78 79 // NewBackoffFnCfg creates the config for BackoffFn. 80 func NewBackoffFnCfg(base, cap, jitter int) *BackoffFnCfg { 81 return &BackoffFnCfg{ 82 base, 83 cap, 84 jitter, 85 } 86 } 87 88 // NewConfig creates a new Config for the Backoff operation. 89 func NewConfig(name string, metric *prometheus.Observer, backoffFnCfg *BackoffFnCfg, err error) *Config { 90 return &Config{ 91 name: name, 92 metric: metric, 93 fnCfg: backoffFnCfg, 94 err: err, 95 } 96 } 97 98 func (c *Config) String() string { 99 return c.name 100 } 101 102 // SetErrors sets a more detailed error instead of the default bo config. 103 func (c *Config) SetErrors(err error) { 104 c.err = err 105 } 106 107 const txnLockFastName = "txnLockFast" 108 109 // Backoff Config variables. 110 var ( 111 // TODO: distinguish tikv and tiflash in metrics 112 BoTiKVRPC = NewConfig("tikvRPC", &metrics.BackoffHistogramRPC, NewBackoffFnCfg(100, 2000, EqualJitter), tikverr.ErrTiKVServerTimeout) 113 BoTiFlashRPC = NewConfig("tiflashRPC", &metrics.BackoffHistogramRPC, NewBackoffFnCfg(100, 2000, EqualJitter), tikverr.ErrTiFlashServerTimeout) 114 BoTxnLock = NewConfig("txnLock", &metrics.BackoffHistogramLock, NewBackoffFnCfg(200, 3000, EqualJitter), tikverr.ErrResolveLockTimeout) 115 BoPDRPC = NewConfig("pdRPC", &metrics.BackoffHistogramPD, NewBackoffFnCfg(500, 3000, EqualJitter), tikverr.NewErrPDServerTimeout("")) 116 // change base time to 2ms, because it may recover soon. 117 BoRegionMiss = NewConfig("regionMiss", &metrics.BackoffHistogramRegionMiss, NewBackoffFnCfg(2, 500, NoJitter), tikverr.ErrRegionUnavailable) 118 BoRegionScheduling = NewConfig("regionScheduling", &metrics.BackoffHistogramRegionScheduling, NewBackoffFnCfg(2, 500, NoJitter), tikverr.ErrRegionUnavailable) 119 BoTiKVServerBusy = NewConfig("tikvServerBusy", &metrics.BackoffHistogramServerBusy, NewBackoffFnCfg(2000, 10000, EqualJitter), tikverr.ErrTiKVServerBusy) 120 BoTiKVDiskFull = NewConfig("tikvDiskFull", &metrics.BackoffHistogramTiKVDiskFull, NewBackoffFnCfg(500, 5000, NoJitter), tikverr.ErrTiKVDiskFull) 121 BoTiFlashServerBusy = NewConfig("tiflashServerBusy", &metrics.BackoffHistogramServerBusy, NewBackoffFnCfg(2000, 10000, EqualJitter), tikverr.ErrTiFlashServerBusy) 122 BoTxnNotFound = NewConfig("txnNotFound", &metrics.BackoffHistogramEmpty, NewBackoffFnCfg(2, 500, NoJitter), tikverr.ErrResolveLockTimeout) 123 BoStaleCmd = NewConfig("staleCommand", &metrics.BackoffHistogramStaleCmd, NewBackoffFnCfg(2, 1000, NoJitter), tikverr.ErrTiKVStaleCommand) 124 BoMaxTsNotSynced = NewConfig("maxTsNotSynced", &metrics.BackoffHistogramEmpty, NewBackoffFnCfg(2, 500, NoJitter), tikverr.ErrTiKVMaxTimestampNotSynced) 125 BoMaxDataNotReady = NewConfig("dataNotReady", &metrics.BackoffHistogramDataNotReady, NewBackoffFnCfg(100, 2000, NoJitter), tikverr.ErrRegionDataNotReady) 126 BoMaxRegionNotInitialized = NewConfig("regionNotInitialized", &metrics.BackoffHistogramEmpty, NewBackoffFnCfg(2, 1000, NoJitter), tikverr.ErrRegionNotInitialized) 127 // TxnLockFast's `base` load from vars.BackoffLockFast when create BackoffFn. 128 BoTxnLockFast = NewConfig(txnLockFastName, &metrics.BackoffHistogramLockFast, NewBackoffFnCfg(2, 3000, EqualJitter), tikverr.ErrResolveLockTimeout) 129 ) 130 131 var isSleepExcluded = map[*Config]struct{}{ 132 BoTiKVServerBusy: {}, 133 // add BoTiFlashServerBusy if appropriate 134 } 135 136 const ( 137 // NoJitter makes the backoff sequence strict exponential. 138 NoJitter = 1 + iota 139 // FullJitter applies random factors to strict exponential. 140 FullJitter 141 // EqualJitter is also randomized, but prevents very short sleeps. 142 EqualJitter 143 // DecorrJitter increases the maximum jitter based on the last random value. 144 DecorrJitter 145 ) 146 147 // newBackoffFn creates a backoff func which implements exponential backoff with 148 // optional jitters. 149 // See http://www.awsarchitectureblog.com/2015/03/backoff.html 150 func newBackoffFn(base, cap, jitter int) backoffFn { 151 if base < 2 { 152 // Top prevent panic in 'rand.Intn'. 153 base = 2 154 } 155 attempts := 0 156 lastSleep := base 157 return func(ctx context.Context, maxSleepMs int) int { 158 var sleep int 159 switch jitter { 160 case NoJitter: 161 sleep = expo(base, cap, attempts) 162 case FullJitter: 163 v := expo(base, cap, attempts) 164 sleep = rand.Intn(v) 165 case EqualJitter: 166 v := expo(base, cap, attempts) 167 sleep = v/2 + rand.Intn(v/2) 168 case DecorrJitter: 169 sleep = int(math.Min(float64(cap), float64(base+rand.Intn(lastSleep*3-base)))) 170 } 171 logutil.BgLogger().Debug("backoff", 172 zap.Int("base", base), 173 zap.Int("sleep", sleep), 174 zap.Int("attempts", attempts)) 175 176 realSleep := sleep 177 // when set maxSleepMs >= 0 in `tikv.BackoffWithMaxSleep` will force sleep maxSleepMs milliseconds. 178 if maxSleepMs >= 0 && realSleep > maxSleepMs { 179 realSleep = maxSleepMs 180 } 181 select { 182 case <-time.After(time.Duration(realSleep) * time.Millisecond): 183 attempts++ 184 lastSleep = sleep 185 return realSleep 186 case <-ctx.Done(): 187 return 0 188 } 189 } 190 } 191 192 func expo(base, cap, n int) int { 193 return int(math.Min(float64(cap), float64(base)*math.Pow(2.0, float64(n)))) 194 }