httpretrier

package
v1.152.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 14, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package httpretrier provides configurable retry execution for outbound HTTP requests.

Problem

Remote dependencies fail in transient ways (timeouts, connection resets, temporary 5xx/429 responses). Without a consistent retry strategy, callers either fail too aggressively or duplicate ad hoc retry loops with different policies across services.

Solution

This package wraps an HTTPClient with retry orchestration driven by a pluggable RetryIfFn.

HTTPRetrier.Do executes a request up to a bounded number of attempts, applying delay growth and jitter between retries. The retry decision function receives both `*http.Response` and `error`, enabling policy decisions based on transport failures and/or HTTP status codes.

Built-in Retry Policies

Predefined helpers are provided for common semantics:

The default policy retries only when `err != nil`.

Backoff Behavior

Delay progression is configurable via:

This produces bounded exponential-style backoff with randomization, helping reduce synchronized retry storms. Optionally, WithRespectRetryAfter makes the retrier wait at least the server-provided Retry-After delay, and WithOnRetry exposes each scheduled retry for logging or metrics.

Request Body Replay

When a request has a body and retries are needed, the retrier relies on `Request.GetBody` to recreate the body stream for subsequent attempts. If the request has a body that cannot be recreated (GetBody missing or failing), retries cannot continue and Do returns an error. Bodyless requests (e.g. a GET with no body) retry without restriction.

Benefits

httpretrier standardizes retry behavior for HTTP clients, improving resilience while keeping retry policies explicit, testable, and reusable across services.

Index

Constants

View Source
const (
	// DefaultAttempts is the default maximum number of total attempts (the
	// initial request plus retries).
	DefaultAttempts = 4

	// DefaultDelay is the delay to apply after the first failed attempt.
	DefaultDelay = 1 * time.Second

	// DefaultDelayFactor is the default multiplication factor to get the successive delay value.
	DefaultDelayFactor = 2

	// DefaultJitter is the maximum random Jitter time between retries.
	DefaultJitter = 100 * time.Millisecond

	// DefaultMaxDelay is the default upper bound for the computed backoff delay
	// (before jitter). It prevents the exponential growth from overflowing.
	DefaultMaxDelay = 30 * time.Second

	// DefaultMaxRetryAfter is the default cap applied to a server-provided
	// Retry-After delay (see [WithRespectRetryAfter] and [WithMaxRetryAfter]).
	DefaultMaxRetryAfter = 24 * time.Hour
)
View Source
const (
	JitterAdditive = backoff.JitterAdditive // fixed additive ceiling (default)
	JitterFull     = backoff.JitterFull     // rand[0, delay): best decorrelation
	JitterEqual    = backoff.JitterEqual    // delay/2 + rand[0, delay/2)
)

Jitter strategies, re-exported from backoff so callers need not import it.

Variables

View Source
var ErrBodyNotReplayable = errors.New("cannot retry: the request body has already been consumed and Request.GetBody is not set")

ErrBodyNotReplayable is returned by HTTPRetrier.Do when a retry is required but the request body has already been consumed and Request.GetBody is not available to recreate it.

Functions

func RetryIfForReadRequests

func RetryIfForReadRequests(r *http.Response, err error) bool

RetryIfForReadRequests is a retry policy for idempotent requests: retries on 404/408/409/423/425/429/500/502/503/504/507 or transport error. It deliberately retries some codes that are often terminal (404/409/423): for a read this targets read-after-write eventual consistency, where the resource may materialize on a retry. Use a custom RetryIfFn if that is not desired.

func RetryIfForWriteRequests

func RetryIfForWriteRequests(r *http.Response, err error) bool

RetryIfForWriteRequests is a retry policy for state-changing requests: retries on 429/502/503 or transport error.

Types

type HTTPClient

type HTTPClient interface {
	// Do performs the HTTP request. As with [net/http.Client.Do], a non-nil error
	// implies a nil response and vice versa.
	Do(req *http.Request) (*http.Response, error)
}

HTTPClient is the minimal client contract used by HTTPRetrier.

type HTTPRetrier

type HTTPRetrier struct {
	// contains filtered or unexported fields
}

HTTPRetrier applies configurable retry policies to HTTP requests.

An HTTPRetrier holds only immutable configuration once constructed, so a single instance can be safely shared across goroutines and used for concurrent or overlapping HTTPRetrier.Do calls. All per-call mutable state is kept in a separate [doState] value created inside Do.

func New

func New(httpClient HTTPClient, opts ...Option) (*HTTPRetrier, error)

New constructs HTTP retrier wrapping client with exponential backoff retry orchestration from provided options.

func (*HTTPRetrier) Do

func (c *HTTPRetrier) Do(r *http.Request) (*http.Response, error)

Do executes request up to configured attempts with exponential delay and jitter between retries, applying retry decision function to each response.

Do is safe for concurrent use of a single HTTPRetrier instance: every call keeps its own mutable state in a local [doState]. Each concurrent call must use its own *http.Request, since a retry mutates the request's body.

An already-canceled request context fails fast. If the context is canceled just as a retry timer fires, one further attempt may run (with the canceled request) before Do returns the context error.

type JitterStrategy

type JitterStrategy = backoff.JitterStrategy

JitterStrategy selects how backoff jitter is applied. See backoff.JitterStrategy.

type OnRetryFn

type OnRetryFn func(attempt uint, delay time.Duration, r *http.Response, err error)

OnRetryFn is an optional observability callback invoked before each scheduled retry. It receives the number of the attempt that just failed (1-based), the delay before the next attempt, and the response/error that triggered the retry (the response body is already closed). It counts scheduled retries — one may still be preempted by cancellation before it runs — and must not panic; it runs inline in HTTPRetrier.Do.

type Option

type Option func(c *HTTPRetrier) error

Option configures an HTTPRetrier instance.

func WithAttempts

func WithAttempts(attempts uint) Option

WithAttempts sets the maximum number of total attempts — the initial request plus retries (default 4); must be at least 1.

func WithDelay

func WithDelay(delay time.Duration) Option

WithDelay sets initial delay between first failed attempt and first retry (default 1 second).

func WithDelayFactor

func WithDelayFactor(delayFactor float64) Option

WithDelayFactor sets exponential backoff multiplier (default 2): next_delay = current_delay * delayFactor.

func WithJitter

func WithJitter(jitter time.Duration) Option

WithJitter sets random jitter ceiling to prevent synchronized retry storms (default 100ms).

func WithJitterStrategy

func WithJitterStrategy(strategy JitterStrategy) Option

WithJitterStrategy selects how backoff jitter is applied (default JitterAdditive). JitterFull and JitterEqual scale jitter with the delay for better decorrelation. Returns error if the strategy is not a defined JitterStrategy.

func WithMaxDelay

func WithMaxDelay(maxDelay time.Duration) Option

WithMaxDelay sets the upper bound for the computed exponential backoff delay (before jitter), preventing unbounded growth and overflow (default 30s).

func WithMaxRetryAfter

func WithMaxRetryAfter(maxRetryAfter time.Duration) Option

WithMaxRetryAfter caps the delay honored from a Retry-After header (default DefaultMaxRetryAfter, 24h). Only relevant with WithRespectRetryAfter. Returns error if maxRetryAfter < 1 nanosecond.

func WithOnRetry

func WithOnRetry(onRetry OnRetryFn) Option

WithOnRetry registers an observability callback invoked before each scheduled retry (see OnRetryFn). Returns error if the callback is nil.

func WithRespectRetryAfter

func WithRespectRetryAfter() Option

WithRespectRetryAfter makes the retrier honor a response's Retry-After header (delta-seconds or HTTP-date), waiting at least that long (plus jitter) before the next attempt, capped at WithMaxRetryAfter (default DefaultMaxRetryAfter). This can exceed WithMaxDelay, which bounds only the exponential backoff; a request context deadline is the ultimate bound on any wait. Default: disabled.

func WithRetryIfFn

func WithRetryIfFn(retryIfFn RetryIfFn) Option

WithRetryIfFn customizes retry decision function (default: retry only on transport errors).

type RetryIfFn

type RetryIfFn func(r *http.Response, err error) bool

RetryIfFn decides whether a request should be retried after a response/error. It must not panic; it runs inline in HTTPRetrier.Do, before the response body is closed, so a panic would also leak that response.

func RetryIfFnByHTTPMethod

func RetryIfFnByHTTPMethod(httpMethod string) RetryIfFn

RetryIfFnByHTTPMethod selects retry policy by HTTP method: idempotent read policy for GET, write policy for all others.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL