backoff

package module
v2.0.8 Latest Latest
Warning

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

Go to latest
Published: Feb 28, 2021 License: MIT Imports: 5 Imported by: 47

README

backoff Go Reference

Idiomatic backoff for Go

This library is an implementation of backoff algorithm for retrying operations in an idiomatic Go way. It respects context.Context natively, and the critical notifications are done through channel operations, allowing you to write code that is both more explicit and flexibile.

For a longer discussion, please read this article

IMPORT

import "github.com/lestrrat-go/backoff/v2"

SYNOPSIS

func ExampleExponential() {
  p := backoff.Exponential(
    backoff.WithMinInterval(time.Second),
    backoff.WithMaxInterval(time.Minute),
    backoff.WithJitterFactor(0.05),
  )

  flakyFunc := func(a int) (int, error) {
    // silly function that only succeeds if the current call count is
    // divisible by either 3 or 5 but not both
    switch {
    case a%15 == 0:
      return 0, errors.New(`invalid`)
    case a%3 == 0 || a%5 == 0:
      return a, nil
    }
    return 0, errors.New(`invalid`)
  }

  ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
  defer cancel()

  retryFunc := func(v int) (int, error) {
    b := p.Start(ctx)
    for backoff.Continue(b) {
      x, err := flakyFunc(v)
      if err == nil {
        return x, nil
      }
    }
    return 0, errors.New(`failed to get value`)
  }

  retryFunc(15)
}

POLICIES

Policy objects describe a backoff policy, and are factories to create backoff Controller objects. Controller objects does the actual coordination. Create a new controller for each invocation of a backoff enabled operation. This way the controller object is protected from concurrent access (if you have one) and can easily be discarded

Null

A null policy means there's no backoff.

For example, if you were to support both using and not using a backoff in your code you can say

  var p backoff.Policy
  if useBackoff {
    p = backoff.Exponential(...)
  } else {
    p = backoff.Null()
  }
  c := p.Start(ctx)
  for backoff.Continue(c) {
    if err := doSomething(); err != nil {
      continue
    }
    return
  }

Instead of

  if useBackoff {
    p := backoff.Exponential(...)
    c := p.Start(ctx)
    for backoff.Continue(c) {
      if err := doSomething(); err != nil {
        continue
      }
      return
    }
  } else {
    if err := doSomething(); err != nil {
      continue
    }
  }

Constant

A constant policy implements are backoff where the intervals are always the same

Exponential

This is the most "common" of the backoffs. Intervals between calls are spaced out such that as you keep retrying, the intervals keep increasing.

FAQ

I'm getting "package github.com/lestrrat-go/backoff/v2: no Go files in /go/src/github.com/lestrrat-go/backoff/v2"

You are using Go in GOPATH mode, which was the way before Go Modules were introduced in Go 1.11 (Aug 2018). GOPATH has slowly been phased out, and in Go 1.14 onwards, Go Modules pretty much Just Work. Go 1.16 introduced more visible changes that forces users to be aware of the existance of go.mod files.

The short answer when you you get the above error is: Migrate to using Go Modules. This is simple: All you need to do is to include a go.mod (and go.sum) file to your library or app.

For example, if you have previously been doing this:

git clone git@github.com:myusername/myawesomeproject.git
cd myawesomeproject
go get ./...

First include go.mod and go.sum in your repository:

git clone git@github.com:myusername/myawesomeproject.git
cd myawesomeproject
go mod init
go mod tidy
git add go.mod go.sum
git commit -m "Add go.mod and go.sum" -a
git push 

Then from subsequent calls:

git clone git@github.com:myusername/myawesomeproject.git
cd myawesomeproject
go build # or go test, or go run, or whatever.

This will tell go to respect tags, and will automatically pick up the latest version of github.com/lestrrat-go/backoff

If you really can't do this, then the quick and dirty workaround is to just copy the files over to /v2 directory of this library

BACKOFF=github.com/lestrrat-go/backoff
go get github.com/lestrrat-go/backoff
if [[ if ! -d "$GOPATH/src/$BACKOFF/v2" ]]; then
  mkdir "$GOPATH/src/$BACKOFF/v2" # just to make sure it exists
fi
cp "$GOPATH/src/$BACKOFF/*.go" "$GOPATH/src/$BACKOFF/v2"

git clone git@github.com:myusername/myawesomeproject.git
cd myawesomeproject
go get ./...

Why won't you just add the files in /v2?

Because it's hard to maintain multiple sources of truth. Sorry, I don't get paid to do this. I will not hold anything against you if you decided to fork this to your repository, and move files to your own /v2 directory. Then, if you have a go.mod in your app, you can just do

go mod edit -replace github.com/lestrrat-go/backoff/v2=github.com/myusername/myawesomemfork/v2

Oh, wait, then you already have go.mod, so this is a non-issue.

...Yeah, just migrate to using go modules, please?

Documentation

Overview

Package backoff implments backoff algorithms for retrying operations.

Users first create an appropriate `Policy` object, and when the operation that needs retrying is about to start, they kick the actual backoff

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Continue

func Continue(c Controller) bool

Continue is a convenience function to check when we can fire the next invocation of the desired backoff code

for backoff.Continue(c) {
 ... your code ...
}

Types

type CommonOption added in v2.0.6

type CommonOption interface {
	ExponentialOption
	ConstantOption
}

CommonOption is an option that can be passed to any of the backoff policies.

func WithJitterFactor

func WithJitterFactor(v float64) CommonOption

WithJitterFactor enables some randomness (jittering) in the computation of the backoff intervals. This value must be between 0.0 < v < 1.0. If a value outside of this range is specified, the value will be silently ignored and jittering is disabled.

This option can be passed to ExponentialPolicy or ConstantPolicy constructor

func WithRNG

func WithRNG(v Random) CommonOption

WithRNG specifies the random number generator used for jittering. If not provided one will be created, but if you want a truly random jittering, make sure to provide one that you explicitly initialized

type ConstantInterval

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

func NewConstantInterval

func NewConstantInterval(options ...ConstantOption) *ConstantInterval

func (*ConstantInterval) Next

func (g *ConstantInterval) Next() time.Duration

type ConstantOption added in v2.0.5

type ConstantOption interface {
	Option
	// contains filtered or unexported methods
}

ConstantOption is an option that is used by the Constant policy.

func WithInterval

func WithInterval(v time.Duration) ConstantOption

WithInterval specifies the constant interval used in ConstantPolicy and ConstantInterval. The default value is 1 minute.

type ConstantPolicy

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

func NewConstantPolicy

func NewConstantPolicy(options ...Option) *ConstantPolicy

func (*ConstantPolicy) Start

func (p *ConstantPolicy) Start(ctx context.Context) Controller

type Controller

type Controller interface {
	Done() <-chan struct{}
	Next() <-chan struct{}
}

type ControllerOption added in v2.0.4

type ControllerOption interface {
	ConstantOption
	ExponentialOption
	CommonOption
	// contains filtered or unexported methods
}

ControllerOption is an option that may be passed to Policy objects, but are ultimately passed down to the Controller objects. (Normally you do not have to care about the distinction)

func WithMaxRetries

func WithMaxRetries(v int) ControllerOption

WithMaxRetries specifies the maximum number of attempts that can be made by the backoff policies. By default each policy tries up to 10 times.

If you would like to retry forever, specify "0" and pass to the constructor of each policy.

This option can be passed to all policy constructors except for NullPolicy

type ExponentialInterval

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

func NewExponentialInterval

func NewExponentialInterval(options ...ExponentialOption) *ExponentialInterval

func (*ExponentialInterval) Next

func (g *ExponentialInterval) Next() time.Duration

type ExponentialOption added in v2.0.5

type ExponentialOption interface {
	Option
	// contains filtered or unexported methods
}

ExponentialOption is an option that is used by the Exponential policy.

func WithMaxInterval

func WithMaxInterval(v time.Duration) ExponentialOption

WithMaxInterval specifies the maximum duration used in exponential backoff The default value is 1 minute.

func WithMinInterval

func WithMinInterval(v time.Duration) ExponentialOption

WithMinInterval specifies the minimum duration used in exponential backoff. The default value is 500ms.

func WithMultiplier

func WithMultiplier(v float64) ExponentialOption

WithMultiplier specifies the factor in which the backoff intervals are increased. By default this value is set to 1.5, which means that for every iteration a 50% increase in the interval for every iteration (up to the value specified by WithMaxInterval). this value must be greater than 1.0. If the value is less than equal to 1.0, the default value of 1.5 is used.

type ExponentialPolicy

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

func NewExponentialPolicy

func NewExponentialPolicy(options ...ExponentialOption) *ExponentialPolicy

func (*ExponentialPolicy) Start

type IntervalGenerator

type IntervalGenerator interface {
	Next() time.Duration
}

type NullPolicy

type NullPolicy struct{}

NullPolicy does not do any backoff. It allows the caller to execute the desired code once, and no more

func NewNull

func NewNull() *NullPolicy

func (*NullPolicy) Start

func (p *NullPolicy) Start(ctx context.Context) Controller

type Option

type Option = option.Interface

type Policy

type Policy interface {
	Start(context.Context) Controller
}

Policy is an interface for the backoff policies that this package implements. Users must create a controller object from this policy to actually do anything with it

func Constant

func Constant(options ...Option) Policy

Constant creates a new ConstantPolicy object

Example
package main

import (
	"context"
	"errors"
	"time"

	backoff "github.com/lestrrat-go/backoff/v2"
)

func main() {
	p := backoff.Constant(backoff.WithInterval(time.Second))

	flakyFunc := func(a int) (int, error) {
		// silly function that only succeeds if the current call count is
		// divisible by either 3 or 5 but not both
		switch {
		case a%15 == 0:
			return 0, errors.New(`invalid`)
		case a%3 == 0 || a%5 == 0:
			return a, nil
		}
		return 0, errors.New(`invalid`)
	}

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	retryFunc := func(v int) (int, error) {
		b := p.Start(ctx)
		for backoff.Continue(b) {
			x, err := flakyFunc(v)
			if err == nil {
				return x, nil
			}
		}
		return 0, errors.New(`failed to get value`)
	}

	retryFunc(15)
}
Output:

func Exponential

func Exponential(options ...ExponentialOption) Policy

Constant creates a new ExponentialPolicy object

Example
package main

import (
	"context"
	"errors"
	"time"

	backoff "github.com/lestrrat-go/backoff/v2"
)

func main() {
	p := backoff.Exponential(
		backoff.WithMinInterval(time.Second),
		backoff.WithMaxInterval(time.Minute),
		backoff.WithJitterFactor(0.05),
	)

	flakyFunc := func(a int) (int, error) {
		// silly function that only succeeds if the current call count is
		// divisible by either 3 or 5 but not both
		switch {
		case a%15 == 0:
			return 0, errors.New(`invalid`)
		case a%3 == 0 || a%5 == 0:
			return a, nil
		}
		return 0, errors.New(`invalid`)
	}

	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	retryFunc := func(v int) (int, error) {
		b := p.Start(ctx)
		for backoff.Continue(b) {
			x, err := flakyFunc(v)
			if err == nil {
				return x, nil
			}
		}
		return 0, errors.New(`failed to get value`)
	}

	retryFunc(15)
}
Output:

func Null

func Null() Policy

Null creates a new NullPolicy object

type Random

type Random interface {
	Float64() float64
}

Jump to

Keyboard shortcuts

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