Install cenkalti/backoff/v7
v7Install the v7 version of the library using go get. Note the /v7 suffix in the import path.
go get github.com/cenkalti/backoff/v7repository·v7·Indexed 26 days ago
https://github.com/cenkalti/backoffA Go port of Google's exponential backoff algorithm from the Java HTTP Client Library. It provides mechanisms to retry operations with increasing delays to handle transient failures, featuring a flexible BackOff interface, configurable ExponentialBackOff, and a Retry function with options for max tries, max elapsed time, and permanent error signaling.
Install the v7 version of the library using go get. Note the /v7 suffix in the import path.
go get github.com/cenkalti/backoff/v7There are two ways to limit the total time spent retrying:
context.WithTimeout. This is reactive and can interrupt the wait between attempts or abort an in-flight operation if the operation respects the context. It reports context.DeadlineExceeded.WithMaxElapsedTime: This bounds only the retry scheduling. It is checked between attempts and never interrupts a running operation. It reports backoff.ErrMaxElapsedTime.Note: WithMaxElapsedTime defaults to 15 minutes. To rely solely on the context and disable the default 15-minute limit, pass backoff.WithMaxElapsedTime(0).
Wrap your operation in backoff.Retry(ctx, operation) to execute it with exponential backoff. The operation should return a value and an error. To stop retrying immediately on specific errors (like client-side errors), wrap the error with backoff.Permanent(err).
Available options to configure the retry behavior include:
WithBackOffWithMaxTriesWithMaxElapsedTimeWithNotifyresult, err := backoff.Retry(ctx, func() (string, error) {
resp, err := http.Get("https://www.example.com")
if err != nil {
return "", err // transient: Retry will try again
}
defer resp.Body.Close()
switch {
case resp.StatusCode >= 500:
return "", fmt.Errorf("server error: %s", resp.Status) // retried
case resp.StatusCode >= 400:
// client errors won't fix themselves, so stop retrying.
return "", backoff.Permanent(fmt.Errorf("client error: %s", resp.Status))
}
return "ok", nil
}, backoff.WithMaxTries(5))When Retry fails, it returns a *RetryError. You can use errors.Is to check the reason why retrying stopped or backoff.AsRetryError(err) to access the LastErr (the error from the final attempt).
Common error causes to check for:
backoff.ErrPermanent: The operation returned an error wrapped in backoff.Permanent().context.Canceled or context.DeadlineExceeded: The provided context was cancelled or expired.backoff.ErrMaxElapsedTime: The time limit set by WithMaxElapsedTime was reached.backoff.ErrExhausted: The maximum number of tries (WithMaxTries) was reached or the backoff policy signaled to stop.result, err := backoff.Retry(ctx, operation)
switch {
case errors.Is(err, backoff.ErrPermanent):
// the operation returned a Permanent error
case errors.Is(err, context.Canceled), errors.Is(err, context.DeadlineExceeded):
// the caller's context was cancelled or its deadline expired
case errors.Is(err, backoff.ErrMaxElapsedTime):
// the WithMaxElapsedTime budget was exhausted
case errors.Is(err, backoff.ErrExhausted):
// WithMaxTries was reached or the backoff policy returned Stop
}
// The last operation error is always available, whatever the cause:
if re := backoff.AsRetryError(err); re != nil {
log.Printf("gave up after last error: %v", re.LastErr)
}Retry with a simple operation and a maximum number of tries.The BackOff interface defines the contract for any retry policy in this package. It allows you to control the delay between retries and determine when to stop retrying.
To implement or use a BackOff policy, you interact with two methods:
NextBackOff(): Returns the time.Duration to wait before the next attempt. If it returns backoff.Stop, the caller should cease all retry attempts.Reset(): Resets the policy to its initial state.Note that backoff.Stop is a constant representing a duration of -1 used to signal that no more retries should be made.
// Example of using the NextBackOff interface
duration := b.NextBackOff()
if duration == backoff.Stop {
// Do not retry operation.
} else {
// Sleep for duration and retry operation.
}To tell the retry loop to wait for a specific duration before the next attempt, return an error created by backoff.RetryAfter(duration, cause).
When this error is returned, backoff.Retry will wait for the specified duration and then reset the backoff policy (restarting the backoff sequence). The cause error is preserved and will be available via Unwrap() or as RetryError.LastErr if retrying eventually stops.
You can customize the behavior of Retry using functional options.
Available options:
WithBackOff(b BackOff): Sets the backoff strategy. Defaults to NewExponentialBackOff. Note that BackOff is stateful and not thread-safe; provide a unique instance per Retry call.WithNotify(n Notify): Sets an optional function called after a failed attempt that will be retried. It receives the error and the duration to wait before the next attempt.WithMaxTries(n uint): Limits the total number of attempts (e.g., WithMaxTries(1) runs the operation exactly once). A value of 0 means no limit. If reached, returns ErrExhausted.WithMaxElapsedTime(d time.Duration): Limits the total wall-clock time for all retries. The limit is checked between attempts. If reached, returns ErrMaxElapsedTime. Pass 0 to disable this limit. The default is DefaultMaxElapsedTime (15 minutes).func WithBackOff(b BackOff) RetryOption
func WithNotify(n Notify) RetryOption
func WithMaxTries(n uint) RetryOption
func WithMaxElapsedTime(d time.Duration) RetryOptionReset() to set the internal current interval back to the InitialInterval. This is useful when you want to restart the backoff sequence from the beginning.You can customize the backoff behavior by setting the following fields on an ExponentialBackOff struct:
InitialInterval (time.Duration): The starting interval for the first retry.RandomizationFactor (float64): A factor used to jitter the interval. The actual interval will be within [1 - RandomizationFactor, 1 + RandomizationFactor] of the current interval.Multiplier (float64): The factor by which the interval is multiplied after each attempt.MaxInterval (time.Duration): The maximum cap for the currentInterval (not the randomized interval).The Retry[T] function executes an Operation[T] until it succeeds, returns a permanent error, or reaches a configured limit (max tries, max elapsed time, or backoff exhaustion).
Key behaviors:
nil error.*RetryError. The Cause field of the RetryError indicates why retrying stopped (e.g., ErrPermanent, ErrExhausted, ErrMaxElapsedTime, or a context cancellation).ctx parameter bounds the retry loop. To abort an in-flight attempt, you must capture the ctx inside your Operation.WithMaxElapsedTime instead.func Retry[T any](ctx context.Context, operation Operation[T], opts ...RetryOption) (T, error)Use NewExponentialBackOff() to create a new instance of ExponentialBackOff using the library's default configuration:
InitialInterval: 500msRandomizationFactor: 0.5Multiplier: 1.5MaxInterval: 60sNote: The implementation is not thread-safe.