backoff_exponential.go

 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}