flashdutyretry

package
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: May 30, 2026 License: Apache-2.0 Imports: 4 Imported by: 0

Documentation

Overview

Package flashdutyretry provides a safe-by-default retrying http.RoundTripper for the Flashduty SDK, in the spirit of go-github's transport middleware.

It plugs into the client as a composable transport layer:

client, err := flashduty.NewClient(appKey,
	flashduty.WithTransport(flashdutyretry.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

func WithMaxRetries(n int) Option

WithMaxRetries sets the maximum number of retry attempts after the first try. A negative value disables retries.

func WithMaxWait

func WithMaxWait(d time.Duration) Option

WithMaxWait sets the cap on any single backoff wait.

func WithMinWait

func WithMinWait(d time.Duration) Option

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 flashdutyretry.New() and &flashdutyretry.Transport{} produce a working transport.

func New

func New(opts ...Option) *Transport

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

func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error)

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.

Jump to

Keyboard shortcuts

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