forked from cenkalti/backoff
-
Notifications
You must be signed in to change notification settings - Fork 1
/
exponential.go
112 lines (96 loc) · 4.14 KB
/
exponential.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
package backoff
import (
"math/rand"
"time"
)
/*
ExponentialBackOff is an implementation of BackOff that increases
it's back off period for each retry attempt using a randomization function
that grows exponentially.
Backoff() time is calculated using the following formula:
randomized_interval =
retry_interval * (random value in range [1 - randomization_factor, 1 + randomization_factor])
In other words BackOff() will sleep for times between the randomization factor
percentage below and above the retry interval.
For example, using 2 seconds as the base retry interval and 0.5 as the
randomization factor, the actual back off period used in the next retry
attempt will be between 1 and 3 seconds.
Note: max_interval caps the retry_interval and not the randomized_interval.
Example: The default retry_interval is .5 seconds, default randomization_factor
is 0.5, default multiplier is 1.5 and the max_interval is set to 25 seconds.
For 12 tries the sequence will sleep (values in seconds) (output from ExampleExpBackOffTimes) :
request# retry_interval randomized_interval
1 0.5 [0.25, 0.75]
2 0.75 [0.375, 1.125]
3 1.125 [0.562, 1.687]
4 1.687 [0.8435, 2.53]
5 2.53 [1.265, 3.795]
6 3.795 [1.897, 5.692]
7 5.692 [2.846, 8.538]
8 8.538 [4.269, 12.807]
9 12.807 [6.403, 19.210]
10 19.22 [9.611, 28.833]
11 25 [12.5, 37.5]
12 25 [12.5, 37.5]
Implementation is not thread-safe.
*/
type ExponentialBackOff struct {
InitialInterval time.Duration
currentInterval time.Duration
MaxInterval time.Duration
RandomizationFactor float64
Multiplier float64
}
// Default values for ExponentialBackOff.
const (
DefaultInitialInterval = 500 * time.Millisecond
DefaultRandomizationFactor = 0.5
DefaultMultiplier = 1.5
DefaultMaxInterval = 60 * time.Second
)
// NewExponential creates an instance of ExponentialBackOff using default values.
func NewExponential() *ExponentialBackOff {
b := &ExponentialBackOff{
InitialInterval: DefaultInitialInterval,
RandomizationFactor: DefaultRandomizationFactor,
Multiplier: DefaultMultiplier,
MaxInterval: DefaultMaxInterval,
currentInterval: DefaultInitialInterval,
}
b.Reset()
return b
}
// Reset the interval back to the initial retry interval and restarts the timer.
func (b *ExponentialBackOff) Reset() {
b.currentInterval = b.InitialInterval
}
func (b *ExponentialBackOff) GetSleepTime() time.Duration {
return getRandomValueFromInterval(b.RandomizationFactor, rand.Float64(), b.currentInterval)
}
func (b *ExponentialBackOff) BackOff() {
time.Sleep(b.GetSleepTime())
b.IncrementCurrentInterval()
}
// Increments the current interval by multiplying it with the multiplier.
func (b *ExponentialBackOff) IncrementCurrentInterval() {
// Check for overflow, if overflow is detected set the current interval to the max interval.
if float64(b.currentInterval) >= float64(b.MaxInterval)/b.Multiplier {
b.currentInterval = b.MaxInterval
} else {
b.currentInterval = time.Duration(float64(b.currentInterval) * b.Multiplier)
}
}
func (b *ExponentialBackOff) Inverval() time.Duration {
return b.currentInterval
}
// Returns a random value from the interval:
// [randomizationFactor * currentInterval, randomizationFactor * currentInterval].
func getRandomValueFromInterval(randomizationFactor, random float64, currentInterval time.Duration) time.Duration {
var delta = randomizationFactor * float64(currentInterval)
var minInterval = float64(currentInterval) - delta
var maxInterval = float64(currentInterval) + delta
// Get a random value from the range [minInterval, maxInterval].
// The formula used below has a +1 because if the minInterval is 1 and the maxInterval is 3 then
// we want a 33% chance for selecting either 1, 2 or 3.
return time.Duration(minInterval + (random * (maxInterval - minInterval + 1)))
}