Documentation
¶
Overview ¶
Package retry provides configurable retry logic with exponential backoff and jitter. It is used throughout the platform wherever a transient failure (network timeout, temporary database unavailability, etc.) can be recovered by simply re-attempting the operation after a short delay.
The central entry point is WithBackoff, which executes an operation up to MaxRetries+1 times (1 initial attempt + MaxRetries retries). Between attempts it sleeps for a duration computed by CalculateDelay:
delay = InitialWait * Multiplier^attempt (capped at MaxWait) delay ±= delay * JitterFraction (floored at InitialWait)
Context cancellation is checked before each sleep, so callers can abort a retry loop promptly during graceful shutdown.
Configuration uses the WithDefaults/validate pattern: pass a partially filled Config (or nil) and zero-value fields are replaced with production defaults.
Index ¶
Examples ¶
Constants ¶
const (
// DefaultMaxRetries is the number of retry attempts after the initial call fails. Total attempts = 1 (initial) + DefaultMaxRetries. Exported so callers can reference it when building retry-aware error messages.
DefaultMaxRetries = 3
)
Variables ¶
This section is empty.
Functions ¶
func CalculateDelay ¶
CalculateDelay computes the backoff sleep duration for the given 0-indexed attempt. The formula is:
base = InitialWait * Multiplier^attempt capped = min(base, MaxWait) jitter = capped * JitterFraction * uniform(-1, 1) delay = max(capped + jitter, InitialWait)
The delay is always at least InitialWait, even after negative jitter is applied. When JitterFraction is 0 the result is fully deterministic. A nil Config is promoted to defaults via WithDefaults before computation.
func WithBackoff ¶
WithBackoff executes operation and retries it up to MaxRetries times on failure, sleeping with exponential backoff between attempts. The retry loop:
- Calls operation(). On success (nil error), returns nil immediately.
- On failure, computes the backoff delay via CalculateDelay and sleeps. If the context is cancelled during the sleep, returns ctx.Err() instead of continuing.
- After exhausting all retries, returns the last error from operation.
A nil Config is promoted to defaults. If the (defaulted) config fails validation, the validation error is returned without ever calling operation.
Example ¶
ExampleWithBackoff shows the minimal configuration needed to retry an operation: pass a partially filled Config (or nil) and zero-value fields are replaced with production defaults.
package main
import (
"context"
"fmt"
"time"
"github.com/open-mrp/api/shared/retry"
)
func main() {
cfg := &retry.Config{
MaxRetries: 2,
InitialWait: 10 * time.Millisecond,
}
err := retry.WithBackoff(context.Background(), cfg, func() error {
return nil // the operation to retry
})
fmt.Println(err)
}
Output: <nil>
Types ¶
type Config ¶
type Config struct {
// MaxRetries (optional; default: 3) is the maximum number of retry attempts after the initial call. Total invocations = MaxRetries + 1. The zero value is treated as "unset" by WithDefaults and replaced with the default of 3; running exactly once with no retries is not expressible via this config.
MaxRetries int
// InitialWait (optional; default: 1s) is the delay before the first retry. It also serves as the absolute floor for the jittered delay — no sleep will ever be shorter than this value.
InitialWait time.Duration
// MaxWait (optional; default: 10s) caps the computed exponential delay. Once InitialWait * Multiplier^attempt exceeds MaxWait, the delay is pinned to MaxWait (before jitter).
MaxWait time.Duration
// Multiplier (optional; default: 2.0) is the base of the exponential growth applied per attempt. A value of 2.0 doubles the delay each retry; 1.0 produces constant-interval retries. Must be >= 1.0.
Multiplier float64
// JitterFraction (optional; default: 0.1) controls the +/- random spread applied to each delay. A value of 0.1 means the final delay is within +/-10% of the computed exponential value. Must be in [0, 1.0]. The zero value is treated as "unset" by WithDefaults and replaced with the default of 0.1, so disabling jitter is only possible when bypassing WithDefaults (e.g. calling CalculateDelay directly).
JitterFraction float64
}
Config controls the retry behavior of WithBackoff. All fields have sensible production defaults (applied by Config.WithDefaults) so callers only need to set the values they want to override.
func (*Config) WithDefaults ¶
WithDefaults returns a new Config with all zero-value fields replaced by production defaults. It is safe to call on a nil receiver — a nil Config produces a fully-defaulted config. The original Config is not mutated; a copy is always returned.