backoff

package module
v0.0.0-...-c7bf2de Latest Latest
Warning

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

Go to latest
Published: Aug 22, 2025 License: MIT Imports: 6 Imported by: 0

README

PkgGoDev

Backoff

This package provides a retry mechanism for functions.

The API is very minimalistic, yet gives options for customization.

Usage

package main

import (
    "fmt"
    "time"

    "github.com/krhubert/backoff"
)

func fn() (string, error) {
    return "out", nil
}

func main() {
    out, err := backoff.Retry2(context.Background(), fn)
    if err != nil {
        fmt.Fprint(os.Stderr, "Failed", err)
        os.Exit(1)
    }
    fmt.Println(out)
}

Documentation

Overview

Package backoff implements backoff algorithms for retrying operations.

Index

Constants

View Source
const Stop time.Duration = -1

Stop is a special backoff duration that stops the retry immediately.

Variables

This section is empty.

Functions

func Retry

func Retry(
	ctx context.Context,
	fn func() error,
	options ...Option,
) error

Retry retries the function until it succeeds. The retries can be terminated by:

  • context cancellation
  • reaching the maximum number of retries (see WithMaxRetries)
  • backoff policy returning Stop
  • function panicking

When function panics, the retrying is stopped immediately and the panic is either propagated or recovered based on the WithRepanic option.

The retry behavior can be further customized with options, see Option for details and defaults.

func Retry2

func Retry2[Out any](
	ctx context.Context,
	fn func() (Out, error),
	options ...Option,
) (Out, error)

Retry2 is like Retry but for functions that return a value. See Retry for more details.

Types

type Backoff

type Backoff interface {
	// NextBackoff returns the next backoff interval.
	// If the returned interval <= 0, then Retry stops immediately.
	//
	// Method is called after error handler, if one is configured.
	// This is important, because error handler can modify the backoff state
	// allowing to implement more cusutomized backoff policies.
	NextBackoff() time.Duration
}

Backoff is a backoff policy for retrying operations.

type Error

type Error struct {
	Err        error
	Attempt    int
	StartedAt  time.Time
	FinishedAt time.Time
}

Error holds information about a single execution of a retried function. It is passed to the error handler, if one is configured.

With error handling many custom behaviors can be implemented, such as:

  • custom backoff policies
  • logging
  • metrics

Example of stopping retries after a certain error type:

type CustomBackoff struct{
	stop bool
}

func (b *CustomBackoff) BackoffNext() time.Duration {
	if b.stop {
		return backoff.Stop
	}
	return time.Second
}

func (b *CustomBackoff) ErrorHandler(e backoff.Error) {
	if errors.Is(e.Err, io.EOF) {
		b.stop = true
	}

type ExponentialBackoff

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

ExponentialBackoff is a policy that waits exponentially longer between each retries.

It uses randomization to avoid thundering herd problems.

func NewExponentialBackoff

func NewExponentialBackoff() *ExponentialBackoff

NewExponentialBackoff returns ExponentialBackoff with default settings.

Default values for ExponentialBackoff are:

DefaultExponentialInitial sets the initial value to start calculating from.

DefaultExponentialInitial = 100 * time.Millisecond

DefaultExponentialRandomizationFactor set to 0.5 to provide a good level of randomness around the current backoff duration.

DefaultExponentialRandomizationFactor = 0.5

Multiplier set to 1.5 to provide a balance between increasing the backoff duration and not not reaching the maximum interval too quickly.

DefaultExponentialMultiplier = 1.5

DefaultExponentialMaxInterval protects against excessively long backoff intervals, which might hide problems and not notify the caller. Also having a very long backoff interval, can prevent function execution if the process is restarted frequently. For very long intervals it is recommended to use a solution that stores interval and state externally.

DefaultExponentialMaxInterval = 5 * time.Minute

DefaultMaxIntervalMinRange is the minimum range when randomizing the backoff duration when the current duration is equal to maxInterval.

DefaultMaxIntervalMinRange = 0.8

func (*ExponentialBackoff) NextBackoff

func (b *ExponentialBackoff) NextBackoff() time.Duration

NextBackoff returns the next backoff duration.

func (*ExponentialBackoff) WithInitial

func (b *ExponentialBackoff) WithInitial(initial time.Duration) *ExponentialBackoff

WithInitial sets the initial backoff duration.

func (*ExponentialBackoff) WithMaxInterval

func (b *ExponentialBackoff) WithMaxInterval(max time.Duration) *ExponentialBackoff

WithMaxInterval sets the maximum backoff duration.

func (*ExponentialBackoff) WithMaxIntervalMinRange

func (b *ExponentialBackoff) WithMaxIntervalMinRange(r float64) *ExponentialBackoff

WithMaxIntervalMinRange sets the minimum range when randomizing the backoff duration when the current duration is equal to maxInterval. Value must be between 0 and 1, where 0 means no range (always return maxInterval) and 1 means full range (return value between 0 and maxInterval).

func (*ExponentialBackoff) WithMultiplier

func (b *ExponentialBackoff) WithMultiplier(multiplier float64) *ExponentialBackoff

WithMultiplier sets the multiplier for increasing the backoff duration.

func (*ExponentialBackoff) WithRandom

func (b *ExponentialBackoff) WithRandom(random func() float64) *ExponentialBackoff

WithRandom sets the custom number generator.

You can set up your own random function source or stub it for testing.

func (*ExponentialBackoff) WithRandomizationFactor

func (b *ExponentialBackoff) WithRandomizationFactor(factor float64) *ExponentialBackoff

WithRandomizationFactor sets the factor for randomizing the backoff duration.

type IntervalBackoff

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

IntervalBackoff is a policy that waits a fixed interval between each retries.

func NewIntervalBackoff

func NewIntervalBackoff(interval time.Duration) *IntervalBackoff

NewIntervalBackoff returns IntervalBackoff with the given interval.

func (*IntervalBackoff) NextBackoff

func (b *IntervalBackoff) NextBackoff() time.Duration

NextBackoff returns the constant duration.

type Option

type Option func(*option)

Option allows to configures Retry/Retry2 behavior.

Possible options are:

There's a default set of arbitrary options that are applied if no options are provided.

They are adjusted to provide a reasonable safe defaults that protects against busy loops and too many retries.

Default values are:

  • backoff: ExponentialBackoff exponential backoff is a good general purpose backoff policy that adds some randomness to avoid thundering herd problems.

  • maxRetries: 32 low value protects against infinite retries, which can lead to excessive resource consumption.

  • initialDelay: 0s 0s makes sure that function is called at least once

  • repanic: false function panics are captured and returned as PanicError by default, allowing the caller to handle them as errors without crashing the entire application.

  • no error handler it's recommended to provide your own error handler and keep track of all errors.

func WithBackoff

func WithBackoff(b Backoff) Option

WithBackoff returns an option that sets the backoff policy.

func WithClock

func WithClock(fn func() time.Time) Option

WithClock returns an option that uses custom now implementation, instead of time.Now.

This is useful for testing if you want to avoid using real time clock.

func WithErrorHandler

func WithErrorHandler(fn func(Error)) Option

WithErrorHandler returns an option that calls the given function with the error returned by the retried function.

This handler can be used for multiple purposes, see Error for details.

func WithExecDuration

func WithExecDuration() Option

WithExecDuration returns an option that subtracts the execution duration of the retried function from the backoff duration.

Backoff duration is not modified by default. With this option the backoff duration is calculated as:

next_backoff = max(0, backoff - execution_duration)

This is useful if you want to ensure that the total time between retries is approximately equal to the backoff duration.

func WithInitialDelay

func WithInitialDelay(d time.Duration) Option

WithInitialDelay returns a backoff policy that waits the given initial delay before the first execution.

func WithMaxRetries

func WithMaxRetries(max int) Option

WithMaxRetries returns a backoff policy that stops retrying after the given number of retries.

func WithRepanic

func WithRepanic() Option

WithRepanic returns an option that makes Retry/Retry2 repanic if the retried function panics.

func WithTimer

func WithTimer(t Timer) Option

WithTimer returns an option that uses a custom timer implementation, instead of time.Timer.

This is useful for testing if you want to avoid using real time timers.

type PanicError

type PanicError struct {
	PCs   []uintptr
	Value any
}

PanicError is returned when the retried function panics. It captures the panic value and the stack trace at the moment of panic.

func (PanicError) Error

func (e PanicError) Error() string

type Timer

type Timer interface {
	C() <-chan time.Time
	Reset(d time.Duration) bool
	Stop() bool
}

Timer is an interface that abstracts time.Timer methods used by retry.

Jump to

Keyboard shortcuts

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