Documentation
¶
Overview ¶
Package retry provides a safe-by-default retrying http.RoundTripper for the Flashduty SDK, usable as a composable transport middleware.
It plugs into the client as a composable transport layer:
client, err := flashduty.NewClient(appKey, flashduty.WithTransport(retry.New()), )
The Transport is pure net/http and deliberately does NOT import the parent flashduty package, so it can never introduce an import cycle.
Safe-by-default body replay ¶
A request is only ever retried when its body is replayable — that is, when req.Body is nil OR req.GetBody is non-nil. On each retry attempt the body is rebuilt from req.GetBody and attached to a per-attempt clone of the request, so the caller's original *http.Request is never mutated across attempts. If a retry would otherwise be warranted but the body cannot be replayed, the last response/error is returned as-is instead of risking a partial or empty body.
The Flashduty SDK sets GetBody on every request it builds, so all SDK POST bodies are replayable; the guard exists so the middleware is still correct when handed arbitrary requests.
Retry policy ¶
Retries are attempted on HTTP 429, on any 5xx (status >= 500), and on transport errors (a non-nil error from the underlying RoundTripper). Other 4xx responses and all 2xx/3xx responses are returned immediately.
Backoff is deterministic exponential: MinWait * 2^attempt, capped per-wait at MaxWait. A valid integer Retry-After header (delta-seconds) overrides the computed backoff (also capped at MaxWait). There is no randomized jitter — math/rand and time-based jitter are intentionally avoided.
Context cancellation is honored while waiting between attempts: if the request's context is done, RoundTrip returns the context error.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Option ¶
type Option func(*Transport)
Option configures a Transport in New.
func WithBase ¶
func WithBase(base http.RoundTripper) Option
WithBase sets the underlying RoundTripper. Nil falls back to http.DefaultTransport at request time.
func WithMaxRetries ¶
WithMaxRetries sets the maximum number of retry attempts after the first try. A negative value disables retries.
func WithMaxWait ¶
WithMaxWait sets the cap on any single backoff wait.
func WithMinWait ¶
WithMinWait sets the base backoff duration.
type Transport ¶
type Transport struct {
// Base is the underlying RoundTripper used to execute requests.
// Defaults to http.DefaultTransport when nil.
Base http.RoundTripper
// MaxRetries is the maximum number of retry attempts after the first try.
// Defaults to 3 when zero. A negative value disables retries entirely.
MaxRetries int
// MinWait is the base backoff duration (the wait before the first retry).
// Defaults to 500ms when zero.
MinWait time.Duration
// MaxWait caps the duration of any single backoff wait.
// Defaults to 30s when zero.
MaxWait time.Duration
}
Transport is an http.RoundTripper that transparently retries idempotent and replayable requests on transient failures (HTTP 429, 5xx, and transport errors), using deterministic exponential backoff.
All fields are optional: the zero value is usable and sensible defaults are applied inside RoundTrip, so both retry.New() and &retry.Transport{} produce a working transport.
func New ¶
New returns a *Transport configured by the given options. With no options it returns a Transport that uses http.DefaultTransport and the default retry budget (3 retries, 500ms..30s backoff).
func (*Transport) RoundTrip ¶
RoundTrip implements http.RoundTripper. It executes req and, on transient failures, retries up to MaxRetries times with deterministic exponential backoff — but only while the request body is replayable (see the package doc). It never mutates the caller's original *http.Request across attempts; each retry runs against a fresh clone with a rebuilt body.