breaker

package module
v0.3.1 Latest Latest
Warning

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

Go to latest
Published: Mar 12, 2026 License: Apache-2.0 Imports: 8 Imported by: 4

README

breaker

Exponential-backoff retry for Go with optional jitter, context-aware sleeps, and outcome metadata.

Features

  • Exponential backoff — configurable base delay and growth factor
  • Optional jitter — randomise delays to avoid thundering-herd problems
  • Context-aware sleeps — cancellation/timeout interrupts backoff immediately
  • Two retry stylesDo (retry everything, opt-out with ErrFatal) or Run (explicit OK/Fail/Retry per attempt)
  • Outcome metadataDoWithOutcome/RunWithOutcome return attempt count, latency, and retry flag
  • Structured logging — optional slog.Logger for retry/exhaustion events

Install

go get github.com/goware/breaker

Usage

Basic retry
br := breaker.New(logger, 500*time.Millisecond, 2.0, 3) // 500ms, x2, up to 3 retries

err := br.Do(ctx, func() error {
    resp, err := http.Get("https://api.example.com/data")
    if err != nil {
        return err // retryable
    }
    if resp.StatusCode == http.StatusBadRequest {
        return fmt.Errorf("bad request: %w", breaker.ErrFatal) // non-retryable
    }
    return nil
})
With jitter
br := breaker.New(logger, 1*time.Second, 2.0, 5, breaker.WithJitter(0.2)) // ±20% jitter

WithJitter(0) (the default) keeps delays fully deterministic for backward compatibility.

Outcome metadata
out := br.DoWithOutcome(ctx, func() error {
    return callAPI()
})

fmt.Printf("attempts=%d retried=%v latency=%v err=%v\n",
    out.Attempts, out.Retried, out.Latency, out.Err)
Explicit retry predicate (Run)

When the caller needs fine-grained control over which errors are retryable:

out := br.RunWithOutcome(ctx, func(attempt int) breaker.Result {
    resp, err := http.Get("https://api.example.com/data")
    if err != nil {
        return breaker.Retry(err) // network error — retry
    }
    if resp.StatusCode == http.StatusTooManyRequests {
        return breaker.Retry(fmt.Errorf("rate limited"))
    }
    if resp.StatusCode >= http.StatusBadRequest {
        return breaker.Fail(fmt.Errorf("status %d", resp.StatusCode)) // don't retry
    }
    return breaker.OK()
})
Package-level convenience
err := breaker.Do(ctx, fn, logger, 1*time.Second, 2.0, 3, breaker.WithJitter(0.1))

API

Function / Type Description
New(log, backoff, factor, maxTries, ...Option) Create a configured Breaker
Default(optLog...) Breaker with sensible defaults (1s, x2, 15 retries, no jitter)
(*Breaker).Do(ctx, fn) Retry fn (all errors retried unless ErrFatal), return error
(*Breaker).DoWithOutcome(ctx, fn) Like Do, return Outcome
(*Breaker).Run(ctx, fn) Retry fn with explicit Result predicate, return error
(*Breaker).RunWithOutcome(ctx, fn) Like Run, return Outcome
Do(ctx, fn, log, backoff, factor, maxTries, ...Option) Stateless convenience for Do
WithJitter(fraction) Option: set jitter fraction in [0, 1]
OK() / Fail(err) / Retry(err) Result constructors for Run
ErrFatal Sentinel: wrap to stop Do retrying immediately
ErrHitMaxRetries Sentinel: returned when all retries are exhausted
Result Single-attempt outcome with Err and Retryable flag
Outcome Invocation metadata: Err, Attempts, Retried, Latency

LICENSE

MIT

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrFatal         = errors.New("breaker: fatal error")
	ErrHitMaxRetries = errors.New("breaker: hit max retries")
)

Functions

func Do added in v0.1.0

func Do(ctx context.Context, fn func() error, log *slog.Logger, backoff time.Duration, factor float64, maxTries int, opts ...Option) error

Do is a convenience wrapper that creates a one-shot Breaker and calls Do on it.

Types

type Breaker added in v0.1.0

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

Breaker is an exponential-backoff-retry caller with optional jitter.

func Default added in v0.1.0

func Default(optLog ...*slog.Logger) *Breaker

Default returns a Breaker with sensible defaults: 1s backoff, 2x factor, 15 retries, no jitter. An optional logger may be provided for retry events.

func New added in v0.1.0

func New(log *slog.Logger, backoff time.Duration, factor float64, maxTries int, opts ...Option) *Breaker

New creates a Breaker with the given backoff, exponential factor, and maximum number of retries. Functional options (e.g. WithJitter) can be appended to customise behaviour. The logger may be nil to suppress retry log output.

func (*Breaker) Do added in v0.1.0

func (b *Breaker) Do(ctx context.Context, fn func() error) error

Do is an exponential-backoff-retry caller which will wait `backoff*factor**retry` up to `maxTries`. `maxTries = 1` means retry only once when an error occurs. Backoff sleeps respect context cancellation.

func (*Breaker) DoWithOutcome added in v0.3.0

func (b *Breaker) DoWithOutcome(ctx context.Context, fn func() error) Outcome

DoWithOutcome behaves like Do but returns an Outcome with attempt and timing metadata.

func (*Breaker) Run added in v0.3.0

func (b *Breaker) Run(ctx context.Context, fn func(attempt int) Result) error

Run executes fn with exponential-backoff retries. Unlike Do, the caller explicitly controls retryability by returning a Result (OK, Fail, or Retry). The attempt number (0-indexed) is passed to fn for logging or adaptive logic.

func (*Breaker) RunWithOutcome added in v0.3.0

func (b *Breaker) RunWithOutcome(ctx context.Context, fn func(attempt int) Result) Outcome

RunWithOutcome behaves like Run but returns an Outcome with attempt and timing metadata.

type Option added in v0.3.0

type Option func(*Breaker)

Option configures a Breaker.

func WithJitter added in v0.3.0

func WithJitter(fraction float64) Option

WithJitter sets the jitter fraction applied to each backoff delay. A value of 0.1 means ±10% randomisation around the computed delay. The value is clamped to the range [0, 1].

type Outcome added in v0.3.0

type Outcome struct {
	Err      error
	Attempts int           // total number of fn invocations
	Retried  bool          // true if fn was called more than once
	Latency  time.Duration // wall-clock time from start to return
}

Outcome holds metadata about a Do or Run invocation.

type Result added in v0.3.0

type Result struct {
	Err       error
	Retryable bool
}

Result holds the outcome of a single attempt. The caller decides whether the error is retryable by setting Retryable to true. Used with Run/RunWithOutcome.

func Fail added in v0.3.0

func Fail(err error) Result

Fail returns a non-retryable error Result that stops the retry loop immediately.

func OK added in v0.3.0

func OK() Result

OK returns a successful (non-retryable) Result.

func Retry added in v0.3.0

func Retry(err error) Result

Retry returns a retryable error Result that signals the operation should be retried.

Jump to

Keyboard shortcuts

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