-
Notifications
You must be signed in to change notification settings - Fork 165
Expand file tree
/
Copy pathretry.go
More file actions
86 lines (71 loc) · 2.19 KB
/
Copy pathretry.go
File metadata and controls
86 lines (71 loc) · 2.19 KB
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
package redislock
import (
"sync/atomic"
"time"
)
// RetryStrategy allows to customise the lock retry strategy.
type RetryStrategy interface {
// NextBackoff returns the next backoff duration.
NextBackoff() time.Duration
}
type linearBackoff time.Duration
// LinearBackoff allows retries regularly with customized intervals
func LinearBackoff(backoff time.Duration) RetryStrategy {
return linearBackoff(backoff)
}
// NoRetry acquire the lock only once.
func NoRetry() RetryStrategy {
return linearBackoff(0)
}
func (r linearBackoff) NextBackoff() time.Duration {
return time.Duration(r)
}
type limitedRetry struct {
s RetryStrategy
cnt atomic.Int64
max int64
}
// LimitRetry limits the number of retries to max attempts.
func LimitRetry(s RetryStrategy, max int) RetryStrategy {
return &limitedRetry{s: s, max: int64(max)}
}
func (r *limitedRetry) NextBackoff() time.Duration {
if r.cnt.Add(1) > r.max {
return 0
}
return r.s.NextBackoff()
}
type exponentialBackoff struct {
cnt atomic.Uint64
min, max time.Duration
}
// ExponentialBackoff returns a strategy that doubles the wait between retries,
// computed as 2**(n+1) milliseconds where n is the attempt count (so the
// first wait is 4ms, the second 8ms, the third 16ms, and so on, capped at
// 2**26 ms once n reaches 25).
//
// The min and max arguments clamp the returned duration:
// - if the computed value is below min, min is returned; pass 0 to disable
// the lower bound. The first few attempts produce sub-16ms waits, so
// callers that want a sensible floor should pass min >= 16ms.
// - if max is non-zero and the computed value exceeds max, max is returned.
// Passing 0 means no upper bound, which lets the wait grow into hours
// after enough attempts; combine with LimitRetry or a ctx deadline if
// that is undesirable.
func ExponentialBackoff(min, max time.Duration) RetryStrategy {
return &exponentialBackoff{min: min, max: max}
}
func (r *exponentialBackoff) NextBackoff() time.Duration {
cnt := r.cnt.Add(1)
ms := 2 << 25
if cnt < 25 {
ms = 2 << cnt
}
if d := time.Duration(ms) * time.Millisecond; d < r.min {
return r.min
} else if r.max != 0 && d > r.max {
return r.max
} else {
return d
}
}