1package retry
2
3import (
4 "context"
5 "math"
6 "sync/atomic"
7 "time"
8)
9
10type exponentialBackoff struct {
11 base time.Duration
12 attempt uint64
13}
14
15// Exponential is a wrapper around Retry that uses an exponential backoff. See
16// NewExponential.
17func Exponential(ctx context.Context, base time.Duration, f RetryFunc) error {
18 return Do(ctx, NewExponential(base), f)
19}
20
21// NewExponential creates a new exponential backoff using the starting value of
22// base and doubling on each failure (1, 2, 4, 8, 16, 32, 64...), up to max.
23//
24// Once it overflows, the function constantly returns the maximum time.Duration
25// for a 64-bit integer.
26//
27// It panics if the given base is less than zero.
28func NewExponential(base time.Duration) Backoff {
29 if base <= 0 {
30 panic("base must be greater than 0")
31 }
32
33 return &exponentialBackoff{
34 base: base,
35 }
36}
37
38// Next implements Backoff. It is safe for concurrent use.
39func (b *exponentialBackoff) Next() (time.Duration, bool) {
40 next := b.base << (atomic.AddUint64(&b.attempt, 1) - 1)
41 if next <= 0 {
42 atomic.AddUint64(&b.attempt, ^uint64(0))
43 next = math.MaxInt64
44 }
45
46 return next, false
47}