forked from pingcap/tidb
-
Notifications
You must be signed in to change notification settings - Fork 0
/
backoff.go
174 lines (156 loc) · 4.35 KB
/
backoff.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
package tikv
import (
"math"
"math/rand"
"time"
"github.com/juju/errors"
"github.com/ngaut/log"
)
const (
// NoJitter makes the backoff sequence strict exponential.
NoJitter = 1 + iota
// FullJitter applies random factors to strict exponential.
FullJitter
// EqualJitter is also randomized, but prevents very short sleeps.
EqualJitter
// DecorrJitter increases the maximum jitter based on the last random value.
DecorrJitter
)
// NewBackoffFn creates a backoff func which implements exponential backoff with
// optional jitters.
// See http://www.awsarchitectureblog.com/2015/03/backoff.html
func NewBackoffFn(base, cap, jitter int) func() int {
attempts := 0
lastSleep := base
return func() int {
var sleep int
switch jitter {
case NoJitter:
sleep = expo(base, cap, attempts)
case FullJitter:
v := expo(base, cap, attempts)
sleep = rand.Intn(v)
case EqualJitter:
v := expo(base, cap, attempts)
sleep = v/2 + rand.Intn(v/2)
case DecorrJitter:
sleep = int(math.Min(float64(cap), float64(base+rand.Intn(lastSleep*3-base))))
}
time.Sleep(time.Duration(sleep) * time.Millisecond)
attempts++
lastSleep = sleep
return lastSleep
}
}
func expo(base, cap, n int) int {
return int(math.Min(float64(cap), float64(base)*math.Pow(2.0, float64(n))))
}
type backoffType int
const (
boTiKVRPC backoffType = iota
boTxnLock
boPDRPC
boRegionMiss
boServerBusy
)
func (t backoffType) createFn() func() int {
switch t {
case boTiKVRPC:
return NewBackoffFn(100, 2000, EqualJitter)
case boTxnLock:
return NewBackoffFn(300, 3000, EqualJitter)
case boPDRPC:
return NewBackoffFn(500, 3000, EqualJitter)
case boRegionMiss:
return NewBackoffFn(100, 500, NoJitter)
case boServerBusy:
return NewBackoffFn(2000, 10000, EqualJitter)
}
return nil
}
func (t backoffType) String() string {
switch t {
case boTiKVRPC:
return "tikvRPC"
case boTxnLock:
return "txnLock"
case boPDRPC:
return "pdRPC"
case boRegionMiss:
return "regionMiss"
case boServerBusy:
return "serverBusy"
}
return ""
}
// Maximum total sleep time(in ms) for kv/cop commands.
const (
copBuildTaskMaxBackoff = 5000
tsoMaxBackoff = 5000
scannerNextMaxBackoff = 5000
batchGetMaxBackoff = 10000
copNextMaxBackoff = 10000
getMaxBackoff = 10000
prewriteMaxBackoff = 10000
commitMaxBackoff = 10000
cleanupMaxBackoff = 10000
gcMaxBackoff = 100000
gcResolveLockMaxBackoff = 100000
)
// Backoffer is a utility for retrying queries.
type Backoffer struct {
fn map[backoffType]func() int
maxSleep int
totalSleep int
errors []error
}
// NewBackoffer creates a Backoffer with maximum sleep time(in ms).
func NewBackoffer(maxSleep int) *Backoffer {
return &Backoffer{
maxSleep: maxSleep,
}
}
// Backoff sleeps a while base on the backoffType and records the error message.
// It returns a retryable error if total sleep time exceeds maxSleep.
func (b *Backoffer) Backoff(typ backoffType, err error) error {
backoffCounter.WithLabelValues(typ.String()).Inc()
start := time.Now()
defer func() { backoffHistogram.WithLabelValues(typ.String()).Observe(time.Since(start).Seconds()) }()
// Lazy initialize.
if b.fn == nil {
b.fn = make(map[backoffType]func() int)
}
f, ok := b.fn[typ]
if !ok {
f = typ.createFn()
b.fn[typ] = f
}
b.totalSleep += f()
log.Warnf("%v, retry later(totalSleep %dms, maxSleep %dms)", err, b.totalSleep, b.maxSleep)
b.errors = append(b.errors, err)
if b.totalSleep >= b.maxSleep {
e := errors.Errorf("backoffer.maxSleep %dms is exceeded, errors: %v", b.maxSleep, b.errors)
return errors.Annotate(e, txnRetryableMark)
}
return nil
}
// Fork creates a new Backoffer which keeps current Backoffer's sleep time and errors.
func (b *Backoffer) Fork() *Backoffer {
return &Backoffer{
maxSleep: b.maxSleep,
totalSleep: b.totalSleep,
errors: b.errors,
}
}