Documentation
¶
Overview ¶
Package retry provides a flexible retry framework with configurable backoff strategies, max attempts, timeouts, and retryable-error filtering.
Strategies ¶
The package ships with two built-in backoff strategies:
- ExponentialBackoff: delay = base * factor^attempt, capped at maxDelay, with optional jitter to avoid thundering-herd.
- FixedInterval: a constant delay between every attempt.
Custom strategies can be created by implementing the Backoff interface.
Retryable errors ¶
By default every non-nil error is retried. Use WithRetryIf to restrict retries to specific error types (e.g. transient network errors).
Basic usage ¶
err := retry.Do(ctx, func(ctx context.Context) error {
return callRemoteAPI(ctx)
},
retry.WithMaxAttempts(5),
retry.WithExponentialBackoff(100*time.Millisecond, 10*time.Second, 2.0, true),
retry.WithRetryIf(func(err error) bool {
var netErr net.Error
return errors.As(err, &netErr) && netErr.Timeout()
}),
)
Decorator pattern ¶
Retry can wrap any func to produce a retried version:
retriedGet := retry.Decorate(
func(ctx context.Context, url string) (*http.Response, error) {
return http.Get(url)
},
retry.WithMaxAttempts(3),
retry.WithFixedInterval(500*time.Millisecond),
)
resp, err := retriedGet(ctx, "https://example.com")
Index ¶
- Variables
- func Decorate[T any, R any](fn func(ctx context.Context, args T) (R, error), opts ...Option) func(ctx context.Context, args T) (R, error)
- func Decorate0(fn func(ctx context.Context) error, opts ...Option) func(ctx context.Context) error
- func Do(ctx context.Context, op Operation, opts ...Option) error
- func IsMaxAttempts(err error) bool
- type Backoff
- type CircuitBreaker
- type ExponentialBackoff
- type FixedInterval
- type NoBackoff
- type Operation
- type Option
- func WithBackoff(b Backoff) Option
- func WithCircuitBreaker(cb CircuitBreaker) Option
- func WithExponentialBackoff(base, maxDelay time.Duration, factor float64, jitter bool) Option
- func WithFixedInterval(interval time.Duration) Option
- func WithMaxAttempts(n int) Option
- func WithNoBackoff() Option
- func WithOnError(fn func(attempts int, err error)) Option
- func WithOnRetry(fn func(attempt int, err error)) Option
- func WithOnSuccess(fn func(attempts int)) Option
- func WithRetryIf(fn func(error) bool) Option
- func WithTimeout(d time.Duration) Option
- type Options
Constants ¶
This section is empty.
Variables ¶
var ( // ErrMaxAttemptsExceeded is returned when all attempts are exhausted // and the last error is nil (e.g. OnRetry hook cancelled the loop). ErrMaxAttemptsExceeded = errors.New("retry: max attempts exceeded") // ErrNoOperation is returned when Do is called with a nil operation. ErrNoOperation = errors.New("retry: operation must not be nil") )
Sentinel errors returned by the retry package.
Functions ¶
func Decorate ¶
func Decorate[T any, R any]( fn func(ctx context.Context, args T) (R, error), opts ...Option, ) func(ctx context.Context, args T) (R, error)
Decorate wraps a function with retry logic, returning a new function with the same signature. This enables the decorator pattern:
retriedFn := retry.Decorate(
func(ctx context.Context, x int) (int, error) { return risky(x) },
retry.WithMaxAttempts(3),
retry.WithFixedInterval(100*time.Millisecond),
)
result, err := retriedFn(ctx, 42)
func Decorate0 ¶
func Decorate0( fn func(ctx context.Context) error, opts ...Option, ) func(ctx context.Context) error
Decorate0 wraps a function with no arguments and no return value.
func Do ¶
Do executes the given operation with retry logic according to opts.
The operation is called at least once. If it returns a non-nil error that satisfies RetryIf (or all errors if RetryIf is nil), the framework waits according to the Backoff strategy and tries again, up to MaxAttempts.
If the context is cancelled or the overall Timeout is reached, Do returns immediately with the context/timeout error (or the last operation error, whichever is more descriptive).
func IsMaxAttempts ¶
IsMaxAttempts reports whether err is (or wraps) ErrMaxAttemptsExceeded.
Types ¶
type Backoff ¶
Backoff computes the delay before the next attempt. attempt is zero-based: 0 means the delay before the 1st retry (after the 1st attempt failed), 1 means the delay before the 2nd retry, etc.
type CircuitBreaker ¶
type CircuitBreaker interface {
Execute(ctx context.Context, op func(context.Context) error) error
}
CircuitBreaker is an optional interface that retry can integrate with. If set via WithCircuitBreaker, each attempt is wrapped in the breaker's Execute. When the breaker is open, the retry loop stops immediately with the breaker's error (e.g. ErrCircuitOpen).
The circuitbreaker package's *CircuitBreaker satisfies this interface.
type ExponentialBackoff ¶
type ExponentialBackoff struct {
Base time.Duration // initial delay (must be > 0)
MaxDelay time.Duration // upper bound (0 = no cap)
Factor float64 // multiplier per attempt (e.g. 2.0)
Jitter bool // add ±25% random jitter
}
ExponentialBackoff delays grow exponentially: base * factor^attempt, capped at maxDelay. When jitter is enabled, a random fraction of the computed delay is added (±25%) to avoid synchronized retry storms.
func NewExponentialBackoff ¶
func NewExponentialBackoff(base, maxDelay time.Duration, factor float64, jitter bool) *ExponentialBackoff
NewExponentialBackoff is a convenience constructor with sensible defaults.
- base: initial delay
- maxDelay: upper bound (use 0 for no cap)
- factor: growth multiplier (typically 2.0)
- jitter: enable random jitter
type FixedInterval ¶
FixedInterval is a backoff strategy that always returns the same delay.
func NewFixedInterval ¶
func NewFixedInterval(interval time.Duration) *FixedInterval
NewFixedInterval creates a FixedInterval with the given delay. If interval <= 0 it defaults to 100ms.
type NoBackoff ¶
type NoBackoff struct{}
NoBackoff is a backoff strategy with zero delay — retry immediately.
type Option ¶
type Option func(*Options)
Option is a functional option for configuring Options.
func WithCircuitBreaker ¶
func WithCircuitBreaker(cb CircuitBreaker) Option
WithCircuitBreaker wraps each attempt in the given circuit breaker. When the breaker is open, the retry loop stops immediately with the breaker's error. This enables the pattern: retry handles transient failures within a single call, while the circuit breaker prevents cascading failures across calls to the same dependency.
func WithExponentialBackoff ¶
WithExponentialBackoff is a convenience that sets an ExponentialBackoff.
func WithFixedInterval ¶
WithFixedInterval is a convenience that sets a FixedInterval backoff.
func WithMaxAttempts ¶
WithMaxAttempts sets the total number of attempts (including the first). Set to 0 or 1 for a single attempt. Set to a negative value for unlimited retries (until context cancellation or timeout).
func WithOnError ¶
WithOnError sets a callback invoked when all attempts are exhausted.
func WithOnRetry ¶
WithOnRetry sets a callback invoked before each retry.
func WithOnSuccess ¶
WithOnSuccess sets a callback invoked after a successful attempt.
func WithRetryIf ¶
WithRetryIf sets a predicate that filters which errors are retryable.
func WithTimeout ¶
WithTimeout sets the overall deadline for all attempts combined.
type Options ¶
type Options struct {
// MaxAttempts is the total number of attempts (including the first).
// 0 or 1 means a single attempt with no retries.
// < 0 means retry indefinitely until ctx is cancelled or timeout.
MaxAttempts int
// Timeout is the overall deadline for all attempts combined.
// 0 means no overall timeout (rely on the caller's context).
Timeout time.Duration
// Backoff strategy. If nil, NoBackoff is used.
Backoff Backoff
// RetryIf decides whether an error is retryable.
// If nil, all non-nil errors are retried.
RetryIf func(error) bool
// CircuitBreaker wraps each attempt. If the breaker is open, the
// retry loop stops immediately. Optional.
CircuitBreaker CircuitBreaker
// OnRetry is called before each retry attempt (not before the first).
// attempt is the 1-based index of the upcoming attempt.
OnRetry func(attempt int, err error)
// OnSuccess is called after a successful attempt (if ever).
// attempts is the total number of attempts made (>= 1).
OnSuccess func(attempts int)
// OnError is called after all attempts are exhausted with the last error.
OnError func(attempts int, err error)
}
Options configures the retry behaviour.