Table of contents
Part of nurago, a collection of independent Go packages for backend services.
import "github.com/tecnickcom/nurago/pkg/backoff"
Computes the delay before the next retry attempt. Schedule.Next returns a duration and advances the progression.
It performs no timing, no scheduling, no goroutines and no I/O. You own the loop and the timer; this answers how long to wait.
s := backoff.New(backoff.Config{...})
for {
if err := do(); err == nil {
break
}
time.Sleep(s.Next())
}
Three jitter strategies
Without jitter, a fleet of clients that all failed at the same instant retries at the same instant, and again at the same instant, hammering a recovering service in synchronised waves.
| Strategy | Behaviour |
|---|---|
JitterAdditive (default) | adds a fixed random ceiling to the delay |
JitterFull | randomises across the whole interval up to the delay |
JitterEqual | half the delay, plus a random half |
The default is the weakest of the three at large delays, and knowingly so: a fixed ceiling added to a growing delay becomes proportionally smaller as the delay grows, so its desynchronising effect fades exactly when clients have drifted furthest apart. JitterFull and JitterEqual scale the jitter with the delay and keep decorrelating clients at any attempt number. Both come from the AWS architecture blog’s treatment of backoff and jitter.
AddJitter exposes the jitter step alone, for pacing work at a fixed interval rather than backing off.
Overflow is clamped, in both places
time.Duration is a signed int64 of nanoseconds. Exponential growth reaches math.MaxInt64 after enough attempts, and signed overflow in Go wraps rather than saturating, so the next delay comes back negative. A negative sleep returns immediately, which turns a backoff loop into a tight retry loop, during the outage that produced the failures in the first place.
The package clamps in both places where it can happen. The progression is capped below the int64 limit, roughly 146 years. The jitter addition saturates at math.MaxInt64 rather than wrapping. Negative and NaN inputs are floored to zero, so a misconfigured factor cannot produce one either.
These are reachable only after many consecutive failures, which is to say during a sustained outage.
Precision above 104 days
Delays are computed in float64, so integer-nanosecond precision is exact only to about 2^53 ns, around 104 days. Beyond that a result may differ by a few nanoseconds.
Recorded because it is a real property of the implementation, not because it matters: any retry schedule reaching 104-day delays has other problems.
Where it is used
retrier and httpretrier both take their schedule from here, so one policy applies consistently to HTTP and non-HTTP work. periodic uses the jitter step alone for fixed-interval scheduling.
Dependencies
This package reaches no external module: it uses only the Go standard library.
Links
- Reference documentation
- Source code
- In depth: Exponential Backoff with Jitter in Go, Without the Overflow Footgun
- The retry engine: retrier
- HTTP retries: httpretrier
- All nurago packages