retry

package
v0.0.0-...-ad8c0a4 Latest Latest
Warning

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

Go to latest
Published: Aug 29, 2026 License: OSL-3.0 Imports: 5 Imported by: 0

Documentation

Overview

Package retry provides a shared exponential-backoff helper used by retry sites that previously open-coded the same loop with slightly different shapes. Today's call-sites:

  • internal/database/connection.go::createConnectionPoolWithRetry bounds Lambda cold-start RDS Ping retries with a per-attempt deadline so a single hung TCP SYN doesn't burn the full retry budget.
  • providers/gcp/services/computeengine/client.go::CreateCommitment retries on RESOURCE_EXHAUSTED with a fixed 1s/2s/4s sequence.

The AWS rate limiter at providers/aws/recommendations/ratelimiter.go is intentionally NOT migrated to this helper — its Wait/ShouldRetry/ Reset state-machine is structurally incompatible with the closure- based Do shape and rewriting its callers would be a separate change.

Index

Constants

This section is empty.

Variables

View Source
var ErrPermanent = errors.New("retry: permanent error, do not retry")

ErrPermanent is a sentinel callers wrap into the error they return from op to short-circuit retries. The shared Do checks via errors.Is(err, ErrPermanent) after every attempt.

Go 1.20+ supports multiple %w verbs in fmt.Errorf so the underlying SDK error stays unwrappable via errors.As:

if !shouldRetry(awsErr) {
    return fmt.Errorf("%w: %w", retry.ErrPermanent, awsErr)
}

Callers can then errors.Is(err, retry.ErrPermanent) AND errors.As(err, &awsSpecificErr) on the same returned value.

Functions

func Do

func Do(ctx context.Context, cfg Config, op func(ctx context.Context, attempt int) error) error

Do runs op until it returns nil or the attempt budget is exhausted. The per-attempt context (if PerAttemptTimeout > 0) is independent of ctx so a slow attempt fails fast and the outer retry budget continues. Errors marked with errors.Is(err, ErrPermanent) short-circuit retries.

Returned error: nil on success, ctx.Err() if the outer context is cancelled mid-backoff, the unwrapped op error if it short-circuits via ErrPermanent, or a wrapped "after N attempts: …" of the last op error if the budget exhausts.

Types

type Config

type Config struct {
	// MaxAttempts is the total number of attempts (NOT retries). 1 = no
	// retry, 5 = first attempt + 4 retries.
	MaxAttempts int
	// BaseDelay is the delay before the SECOND attempt; subsequent
	// delays double until MaxDelay caps them.
	BaseDelay time.Duration
	// MaxDelay caps the per-iteration backoff after exponential growth.
	MaxDelay time.Duration
	// PerAttemptTimeout, if > 0, wraps each op invocation in a
	// context.WithTimeout independent of the outer ctx so a single hung
	// attempt fails fast and the retry budget continues. Zero disables.
	PerAttemptTimeout time.Duration
	// Jitter, if true, adds ±25% noise to each backoff via math/rand/v2
	// (lock-free per-goroutine source, no global RNG mutex contention
	// under concurrent retries). math/rand/v2 is sufficient for backoff
	// jitter; cryptographic randomness is not needed.
	Jitter bool
	// OnAttempt, if non-nil, is invoked once per attempt — see OnAttemptFn.
	OnAttempt OnAttemptFn
}

Config bundles the exponential-backoff knobs.

func (Config) Validate

func (c Config) Validate() error

Validate returns an error if MaxAttempts <= 0, BaseDelay <= 0, or MaxDelay < BaseDelay. Do calls Validate on entry and returns its error verbatim if any.

type OnAttemptFn

type OnAttemptFn func(attempt int, prevErr error)

OnAttemptFn is called BEFORE each attempt (after any backoff delay has elapsed, just before op runs). It receives the attempt number (1-indexed) and the error from the previous attempt (nil on the first call). Used for per-call-site logging that the shared helper can't know about.

Why "before" not "after": call-sites today log either while about-to-wait ("retrying after Nms") or right after a failure. The shared callback fires before each attempt and gets prevErr, so call-sites guard on `prevErr != nil` to log only on retries (matching today's behaviour: no log on the first/only attempt). The first-attempt invocation with prevErr=nil is a no-op for most call-sites.

Jump to

Keyboard shortcuts

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