backoff

package module
v0.0.1 Latest Latest
Warning

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

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

README

Release Reference DeepWiki Test

Insights Insights

go-backoff

go-backoff repository.

Features

  • Support various backoff algorithms
  • Offset support
  • Jitter support
  • Zero dependency

Supported backoff algorithms:

  • Fixed backoff
  • Random backoff
  • Linear backoff
  • Polynomial backoff
  • Exponential backoff
  • Fibonacci backoff

Usages

Basic backoff usage

Instanciate a backoff provider with paramerters. The Attempt(n int) returns the n-th backoff duration instead of blocking the call.

// Parameters
offset := 0 * time.Millisecond
limit := 200 * time.Millisecond
coeff := 5 * time.Millisecond
jitter := backoff.NoJitter

// Instanciate a backoff provider.
bo, err := backoff.NewLinear(offset, limit, coeff, jitter)
if err != nil {
  panic(err)
}

for i := range 11 {
  d := bo.Attempt(i)
  fmt.Printf("%03d: %s\n", i, d)
}
// Output:
// 000: 0s
// 001: 5ms
// 002: 10ms
// 003: 15ms
// 004: 20ms
// 005: 25ms
// 006: 30ms
// 007: 35ms
// 008: 40ms
// 009: 45ms
// 010: 50ms
Run retryable functions

The Retryer provides running and retrying functions with specified backoff strategy.

Basic usage:

// Create a new backoff.
bo, _ := backoff.NewFixed(10 * time.Millisecond)

// Create a new retryer with backoff and other params.
r := backoff.NewRetryer(bo)
r.MaxRetry = 100
r.MaxElapsedTime = 0 // No timeout

// Run a retryable function.
err = r.Run(func(n *backoff.Notify) error {
  fmt.Println("Function called")
  return nil
})
Stop retrying with success

Tell retryer to stop retrying using notifier's Success(). The returned err will always be nil.

See also ExampleRetryer_success.

bo, _ := backoff.NewFixed(10 * time.Millisecond)

r := backoff.NewRetryer(bo)
r.MaxRetry = 10

counter := 0
err := r.Run(func(n *backoff.Notify) error {
  counter++
  if counter == 3 {
    n.Success() // Tell retryer to stop retrying.
    return nil
  }
  return fmt.Errorf("Err-%d", counter)
})
Stop retrying with non-success

Tell retryer to stop retrying using notifier's Stop().

See also ExampleRetryer_stop.

bo, _ := backoff.NewFixed(10 * time.Millisecond)

r := backoff.NewRetryer(bo)
r.MaxRetry = 10

counter := 0
err := r.Run(func(n *backoff.Notify) error {
  counter++
  if counter == 3 {
    n.Stop() // Tell retryer to stop retrying.
    return errors.New("Err-stop")
  }
  return fmt.Errorf("Err-%d", counter)
})
Error handling
err = r.RunContext(ctx, func(n *backoff.Notify) error {
  // Your codes.
  return nil
})

if err != nil {
  switch {
  case errors.Is(err, context.Canceled):
    // Given ctx was canceled.
    // This won't happen when Run, not RunContext, is used.
  case errors.Is(err, context.DeadlineExceeded):
    // Given ctx's deadline was exceeded.
    // This won't happen when Run, not RunContext, is used.
  case errors.Is(err, backoff.ErrMaxRetry):
    // Maximum retry count was reached.
  case errors.Is(err, backoff.ErrTimeout):
    // Maximum elapsed time was reached.
  default:
    // The retryable function told retryer to stop retrying.
    // The err contains all errors returned by the retryable function. 
  }
}

Docs & Examples

Benchmarks

Benchmarks of calling Attempt(10).

See benchmark_test.go for test conditions.

BenchmarkFixed-8         1000000000    0.948 ns/op   0 B/op   0 allocs/op
BenchmarkRandom-8         100000000   10.760 ns/op   0 B/op   0 allocs/op
BenchmarkLinear-8         100000000   12.660 ns/op   0 B/op   0 allocs/op
BenchmarkPolynomial-8      45811842   26.200 ns/op   0 B/op   0 allocs/op
BenchmarkExponential-8     48073263   28.350 ns/op   0 B/op   0 allocs/op
BenchmarkFibonacci-8      104355823   11.280 ns/op   0 B/op   0 allocs/op

References

Documentation

Index

Examples

Constants

View Source
const (
	NoJitter     float64 = 0.0
	FullJitter   float64 = 1.0
	EqualJitter  float64 = 0.5
	HalfJitter   float64 = 0.5
	QuaterJitter float64 = 0.25
)

NoLimit is the alias for MaxInt64 in time.Duration. It is the actual limit of durations.

Variables

View Source
var (
	ErrMaxRetry = errors.New("go-backoff/backoff: max retry count reached")
	ErrTimeout  = errors.New("go-backoff/backoff: max elapsed time reached")
)

Functions

This section is empty.

Types

type Backoff

type Backoff interface {
	// Attempt returns the n-th backoff duration.
	// Returned duration can be zero or positive.
	// n<=0 always returns zero.
	Attempt(n int) time.Duration
}

Backoff provides backoff algorithm.

type Error

type Error struct {
	Inner error  // Inner is the inner error.
	Type  string // Type is the error type.
	Alg   string // Alg is the backoff algorithm.
	Msg   string // Msg is the error message.
}

Error is the backoff error.

func (*Error) Error

func (e *Error) Error() string

func (*Error) Is

func (e *Error) Is(target error) bool

func (*Error) Unwrap

func (e *Error) Unwrap() error

type Exponential

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

Exponential provides exponential backoff strategy.

Example
package main

import (
	"fmt"
	"time"

	"github.com/aileron-projects/go-backoff"
)

func main() {
	// Parameters
	offset := 0 * time.Millisecond
	limit := 600 * time.Second
	coeff := 1 * time.Millisecond
	base := 1.2
	jitter := backoff.NoJitter

	// Instanciate a backoff provider.
	bo, err := backoff.NewExponential(offset, limit, coeff, base, jitter)
	if err != nil {
		panic(err)
	}

	for i := range 11 {
		d := bo.Attempt(i)
		fmt.Printf("%03d: %s\n", i, d)
	}
}
Output:
000: 0s
001: 1.2ms
002: 1.44ms
003: 1.728ms
004: 2.0736ms
005: 2.48832ms
006: 2.985983ms
007: 3.58318ms
008: 4.299816ms
009: 5.15978ms
010: 6.191736ms

func NewExponential

func NewExponential(offset, limit, coeff time.Duration, base, jitter float64) (*Exponential, error)

NewExponential returns a new instance of Exponential.

  • offset: offset for the backoff duration. (>=0)
  • limit: maximum backoff duration. (>=offset)
  • coeff: coefficient for exponential function. (>=0)
  • base: base number for exponential function. (>=1)
  • jitter: parameter for random fluctuation. 1.0 for full jitter. (>=0.0, <=1.0)

Backoff duration for n-th attempt with no jitter will be:

d = offset + coeff * base^n

func (*Exponential) Attempt

func (b *Exponential) Attempt(n int) time.Duration

type Fibonacci

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

Fibonacci provides fibonacci backoff strategy.

Example
package main

import (
	"fmt"
	"time"

	"github.com/aileron-projects/go-backoff"
)

func main() {
	// Parameters
	offset := 0 * time.Millisecond
	limit := 600 * time.Second
	coeff := 500 * time.Microsecond
	jitter := backoff.NoJitter

	// Instanciate a backoff provider.
	bo, err := backoff.NewFibonacci(offset, limit, coeff, jitter)
	if err != nil {
		panic(err)
	}

	for i := range 11 {
		d := bo.Attempt(i)
		fmt.Printf("%03d: %s\n", i, d)
	}
}
Output:
000: 0s
001: 500µs
002: 500µs
003: 1ms
004: 1.5ms
005: 2.5ms
006: 4ms
007: 6.5ms
008: 10.5ms
009: 17ms
010: 27.5ms

func NewFibonacci

func NewFibonacci(offset, limit, coeff time.Duration, jitter float64) (*Fibonacci, error)

NewFibonacci returns a new instance of Fibonacci.

  • offset: offset for the backoff duration. (>=0)
  • limit: maximum backoff duration. (>=offset)
  • coeff: coefficient for fibonacci number. (>=0)
  • jitter: parameter for random fluctuation. 1.0 for full jitter. (>=0.0, <=1.0)

Backoff duration for n-th attempt with no jitter will be:

d = offset + coeff * fibonacci(n)

func (*Fibonacci) Attempt

func (b *Fibonacci) Attempt(n int) time.Duration

type Fixed

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

Fixed provides fixed-interval backoff strategy.

Example
package main

import (
	"fmt"
	"time"

	"github.com/aileron-projects/go-backoff"
)

func main() {
	// Parameters
	interval := 100 * time.Millisecond

	// Instanciate a backoff provider.
	bo, err := backoff.NewFixed(interval)
	if err != nil {
		panic(err)
	}

	for i := range 11 {
		d := bo.Attempt(i)
		fmt.Printf("%03d: %s\n", i, d)
	}
}
Output:
000: 0s
001: 100ms
002: 100ms
003: 100ms
004: 100ms
005: 100ms
006: 100ms
007: 100ms
008: 100ms
009: 100ms
010: 100ms

func NewFixed

func NewFixed(interval time.Duration) (*Fixed, error)

NewFixed returns a new instance of Fixed.

  • interval: fixed value of backoff duration. (>=0)

Backoff duration for n-th attempt will be:

d = interval

func (*Fixed) Attempt

func (b *Fixed) Attempt(n int) time.Duration

type Linear

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

Linear provides linear backoff strategy.

Example
package main

import (
	"fmt"
	"time"

	"github.com/aileron-projects/go-backoff"
)

func main() {
	// Parameters
	offset := 0 * time.Millisecond
	limit := 200 * time.Millisecond
	coeff := 5 * time.Millisecond
	jitter := backoff.NoJitter

	// Instanciate a backoff provider.
	bo, err := backoff.NewLinear(offset, limit, coeff, jitter)
	if err != nil {
		panic(err)
	}

	for i := range 11 {
		d := bo.Attempt(i)
		fmt.Printf("%03d: %s\n", i, d)
	}
}
Output:
000: 0s
001: 5ms
002: 10ms
003: 15ms
004: 20ms
005: 25ms
006: 30ms
007: 35ms
008: 40ms
009: 45ms
010: 50ms

func NewLinear

func NewLinear(offset, limit, coeff time.Duration, jitter float64) (*Linear, error)

NewLinear returns a new instance of Linear.

  • offset: offset for the backoff duration. (>=0)
  • limit: maximum backoff duration. (>=offset)
  • coeff: coefficient for linear function. (>=0)
  • jitter: parameter for random fluctuation. 1.0 for full jitter. (>=0.0, <=1.0)

Backoff duration for n-th attempt with no jitter will be:

d = offset + coeff * (n)

func (*Linear) Attempt

func (b *Linear) Attempt(n int) time.Duration

type Notify

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

Notify reports the outcome of a retryable function to the retryer. The retryer stops retrying when either Stop or Success is called.

func (*Notify) Stop

func (s *Notify) Stop()

Stop tells the retryer to stop retrying because the operation cannot succeed by retrying. Use [Success] when the operation has completed successfully.

func (*Notify) Success

func (s *Notify) Success()

Success tells the retryer to stop retrying because the operation has completed successfully. Use [Stop] when the operation should stop retrying without success.

type Polynomial

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

Polynomial provides polynomial backoff strategy.

Example
package main

import (
	"fmt"
	"time"

	"github.com/aileron-projects/go-backoff"
)

func main() {
	// Parameters
	offset := 0 * time.Millisecond
	limit := 600 * time.Second
	coeff := 10 * time.Millisecond
	exponent := 2.0
	jitter := backoff.NoJitter

	// Instanciate a backoff provider.
	bo, err := backoff.NewPolynomial(offset, limit, coeff, exponent, jitter)
	if err != nil {
		panic(err)
	}

	for i := range 11 {
		d := bo.Attempt(i)
		fmt.Printf("%03d: %s\n", i, d)
	}
}
Output:
000: 0s
001: 10ms
002: 40ms
003: 90ms
004: 160ms
005: 250ms
006: 360ms
007: 490ms
008: 640ms
009: 810ms
010: 1s

func NewPolynomial

func NewPolynomial(offset, limit, coeff time.Duration, exponent, jitter float64) (*Polynomial, error)

NewPolynomial returns a new instance of Polynomial.

  • offset: offset for the backoff duration. (>=0)
  • limit: maximum backoff duration. (>=offset)
  • coeff: coefficient for polynomial function. (>=0)
  • exponent: exponent of n. (>=0)
  • jitter: parameter for random fluctuation. 1.0 for full jitter. (>=0.0, <=1.0)

Backoff duration for n-th attempt with no jitter will be:

d = offset + coeff * n^exponent

func (Polynomial) Attempt

func (b Polynomial) Attempt(n int) time.Duration

type Random

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

Random provides random backoff strategy.

Example
package main

import (
	"fmt"
	"time"

	"github.com/aileron-projects/go-backoff"
)

func main() {
	// Parameters
	offset := 0 * time.Millisecond
	limit := 100 * time.Millisecond

	// Instanciate a backoff provider.
	bo, err := backoff.NewRandom(offset, limit)
	if err != nil {
		panic(err)
	}

	for i := range 11 {
		d := bo.Attempt(i)
		fmt.Printf("%03d: %s\n", i, d)
	}
}

func NewRandom

func NewRandom(offset, limit time.Duration) (*Random, error)

NewRandom returns a new instance of Random.

  • offset: offset for the backoff duration. (>=0)
  • limit: maximum backoff duration. (>=offset)

Backoff duration for n-th attempt will be:

d = offset + random(limit-offset)

func (*Random) Attempt

func (b *Random) Attempt(n int) time.Duration

type Retryer

type Retryer struct {

	// MaxRetry specifies the maximum number of retries.
	// The retryable function is executed at most MaxRetry + 1 times.
	// A value of zero or less disables retries.
	MaxRetry int
	// MaxElapsedTime specifies the maximum duration allowed for retries.
	// The duration starts when Run or RunContext is called.
	// It does not cancel a retryable function that is already running.
	MaxElapsedTime time.Duration
	// contains filtered or unexported fields
}

Retryer runs retryable functions with the backoff algorithm.

Example (Stop)
package main

import (
	"errors"
	"fmt"
	"time"

	"github.com/aileron-projects/go-backoff"
)

func main() {
	bo, err := backoff.NewFixed(10 * time.Millisecond)
	if err != nil {
		panic(err)
	}

	r := backoff.NewRetryer(bo)
	r.MaxRetry = 100
	r.MaxElapsedTime = 0 // No timeout

	counter := 0
	err = r.Run(func(n *backoff.Notify) error {
		counter++
		fmt.Printf("%d-th call\n", counter)
		if counter == 3 {
			n.Stop()
			fmt.Println("stop !!")
			return errors.New("Err-stop")
		}
		return fmt.Errorf("Err-%d", counter)
	})

	fmt.Println("----- Error -----")
	fmt.Println(err)
}
Output:
1-th call
2-th call
3-th call
stop !!
----- Error -----
Err-1
Err-2
Err-stop
Example (Success)
package main

import (
	"fmt"
	"time"

	"github.com/aileron-projects/go-backoff"
)

func main() {
	bo, err := backoff.NewFixed(10 * time.Millisecond)
	if err != nil {
		panic(err)
	}

	r := backoff.NewRetryer(bo)
	r.MaxRetry = 100
	r.MaxElapsedTime = 0 // No timeout

	counter := 0
	err = r.Run(func(n *backoff.Notify) error {
		counter++
		fmt.Printf("%d-th call\n", counter)
		if counter == 3 {
			n.Success()
			fmt.Println("success !!")
			return nil
		}
		return fmt.Errorf("Err-%d", counter)
	})

	fmt.Println("----- Error -----")
	fmt.Println(err)
}
Output:
1-th call
2-th call
3-th call
success !!
----- Error -----
<nil>

func NewRetryer

func NewRetryer(backoff Backoff) *Retryer

NewRetryer creates a new Retryer with given backoff. Returned retryer has no retry and has no timeout. It uses NewRandom(0, time.Second) when nil was given.

func (*Retryer) Run

func (r *Retryer) Run(retryable func(*Notify) error) error

Run executes the retryable function using a background context.

func (*Retryer) RunContext

func (r *Retryer) RunContext(ctx context.Context, retryable func(*Notify) error) error

RunContext executes the retryable function with the configured backoff strategy. It returns nil if Notify.Success is called. It returns the errors returned by the retryable function joined with errors.Join if Notify.Stop is called. It returns an error wrapping ErrTimeout and all errors returned by the retryable function if MaxElapsedTime is reached. It returns an error wrapping ErrMaxRetry and all errors returned by the retryable function if MaxRetry is reached.

Directories

Path Synopsis
exponential command
fibonacci command
fixed command
linear command
polynomial command
random command
retry command

Jump to

Keyboard shortcuts

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