retry

package module
v1.0.3 Latest Latest
Warning

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

Go to latest
Published: May 15, 2019 License: LGPL-3.0 Imports: 3 Imported by: 129

README

retry

-- import "gopkg.in/retry.v1"

Package retry provides a framework for retrying actions. It does not itself invoke the action to be retried, but is intended to be used in a retry loop.

The basic usage is as follows:

for a := someStrategy.Start(); a.Next(); {
	try()
}

See examples for details of suggested usage.

Usage

type Attempt
type Attempt struct {
}

Attempt represents a running retry attempt.

func Start
func Start(strategy Strategy, clk Clock) *Attempt

Start begins a new sequence of attempts for the given strategy using the given Clock implementation for time keeping. If clk is nil, the time package will be used to keep time.

func StartWithCancel
func StartWithCancel(strategy Strategy, clk Clock, stop <-chan struct{}) *Attempt

StartWithCancel is like Start except that if a value is received on stop while waiting, the attempt will be aborted.

func (*Attempt) Count
func (a *Attempt) Count() int

Count returns the current attempt count number, starting at 1. It returns 0 if called before Next is called. When the loop has terminated, it holds the total number of retries made.

func (*Attempt) More
func (a *Attempt) More() bool

More reports whether there are more retry attempts to be made. It does not sleep.

If More returns false, Next will return false. If More returns true, Next will return true except when the attempt has been explicitly stopped via the stop channel.

func (*Attempt) Next
func (a *Attempt) Next() bool

Next reports whether another attempt should be made, waiting as necessary until it's time for the attempt. It always returns true the first time it is called unless a value is received on the stop channel - we are guaranteed to make at least one attempt unless stopped.

func (*Attempt) Stopped
func (a *Attempt) Stopped() bool

Stopped reports whether the attempt has terminated because a value was received on the stop channel.

type Clock
type Clock interface {
	Now() time.Time
	After(time.Duration) <-chan time.Time
}

Clock represents a virtual clock interface that can be replaced for testing.

type Exponential
type Exponential struct {
	// Initial holds the initial delay.
	Initial time.Duration
	// Factor holds the factor that the delay time will be multiplied
	// by on each iteration.
	Factor float64
	// MaxDelay holds the maximum delay between the start
	// of attempts. If this is zero, there is no maximum delay.
	MaxDelay time.Duration
}

Exponential represents an exponential backoff retry strategy. To limit the number of attempts or their overall duration, wrap this in LimitCount or LimitDuration.

func (Exponential) NewTimer
func (r Exponential) NewTimer(now time.Time) Timer

NewTimer implements Strategy.NewTimer.

type Regular
type Regular struct {
	// Total specifies the total duration of the attempt.
	Total time.Duration

	// Delay specifies the interval between the start of each try
	// in the burst. If an try takes longer than Delay, the
	// next try will happen immediately.
	Delay time.Duration

	// Min holds the minimum number of retries. It overrides Total.
	// To limit the maximum number of retries, use LimitCount.
	Min int
}

Regular represents a strategy that repeats at regular intervals.

func (Regular) NewTimer
func (r Regular) NewTimer(now time.Time) Timer

NewTimer implements Strategy.NewTimer.

func (Regular) Start
func (r Regular) Start(clk Clock) *Attempt

Start is short for Start(r, clk, nil)

type Strategy
type Strategy interface {
	// NewTimer is called when the strategy is started - it is
	// called with the time that the strategy is started and returns
	// an object that is used to find out how long to sleep before
	// each retry attempt.
	NewTimer(now time.Time) Timer
}

Strategy is implemented by types that represent a retry strategy.

Note: You probably won't need to implement a new strategy - the existing types and functions are intended to be sufficient for most purposes.

func LimitCount
func LimitCount(n int, strategy Strategy) Strategy

LimitCount limits the number of attempts that the given strategy will perform to n. Note that all strategies will allow at least one attempt.

func LimitTime
func LimitTime(limit time.Duration, strategy Strategy) Strategy

LimitTime limits the given strategy such that no attempt will made after the given duration has elapsed.

type Timer
type Timer interface {
	// NextSleep is called with the time that Next or More has been
	// called and returns the length of time to sleep before the
	// next retry. If no more attempts should be made it should
	// return false, and the returned duration will be ignored.
	//
	// Note that NextSleep is called once after each iteration has
	// completed, assuming the retry loop is continuing.
	NextSleep(now time.Time) (time.Duration, bool)
}

Timer represents a source of timing events for a retry strategy.

Documentation

Overview

Package retry implements flexible retry loops, including support for channel cancellation, mocked time, and composable retry strategies including exponential backoff with jitter.

The basic usage is as follows:

for a := retry.Start(someStrategy, nil); a.Next(); {
	try()
}

See examples for details of suggested usage.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Attempt

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

Attempt represents a running retry attempt.

func Start

func Start(strategy Strategy, clk Clock) *Attempt

Start begins a new sequence of attempts for the given strategy using the given Clock implementation for time keeping. If clk is nil, the time package will be used to keep time.

func StartWithCancel

func StartWithCancel(strategy Strategy, clk Clock, stop <-chan struct{}) *Attempt

StartWithCancel is like Start except that if a value is received on stop while waiting, the attempt will be aborted.

func (*Attempt) Count

func (a *Attempt) Count() int

Count returns the current attempt count number, starting at 1. It returns 0 if called before Next is called. When the loop has terminated, it holds the total number of retries made.

func (*Attempt) More

func (a *Attempt) More() bool

More reports whether there are more retry attempts to be made. It does not sleep.

If More returns false, Next will return false. If More returns true, Next will return true except when the attempt has been explicitly stopped via the stop channel.

Example
// This example shows how Attempt.More can be used to help
// structure an attempt loop. If the godoc example code allowed
// us to make the example return an error, we would uncomment
// the commented return statements.
attempts := retry.Regular{
	Total: 1 * time.Second,
	Delay: 250 * time.Millisecond,
}
for attempt := attempts.Start(nil); attempt.Next(); {
	x, err := doSomething()
	if shouldRetry(err) && attempt.More() {
		continue
	}
	if err != nil {
		// return err
		return
	}
	doSomethingWith(x)
}
// return ErrTimedOut
return
Output:

func (*Attempt) Next

func (a *Attempt) Next() bool

Next reports whether another attempt should be made, waiting as necessary until it's time for the attempt. It always returns true the first time it is called unless a value is received on the stop channel - we are guaranteed to make at least one attempt unless stopped.

func (*Attempt) Stopped

func (a *Attempt) Stopped() bool

Stopped reports whether the attempt has terminated because a value was received on the stop channel.

type Clock

type Clock interface {
	Now() time.Time
	After(time.Duration) <-chan time.Time
}

Clock represents a virtual clock interface that can be replaced for testing.

type Exponential

type Exponential struct {
	// Initial holds the initial delay.
	Initial time.Duration
	// Factor holds the factor that the delay time will be multiplied
	// by on each iteration. If this is zero, a factor of two will be used.
	Factor float64
	// MaxDelay holds the maximum delay between the start
	// of attempts. If this is zero, there is no maximum delay.
	MaxDelay time.Duration
	// Jitter specifies whether jitter should be added to the
	// retry interval. The algorithm used is described as "Full Jitter"
	// in https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
	Jitter bool
}

Exponential represents an exponential backoff retry strategy. To limit the number of attempts or their overall duration, wrap this in LimitCount or LimitDuration.

Example
// This example shows a retry loop that will retry an
// HTTP POST request with an exponential backoff
// for up to 30s.
strategy := retry.LimitTime(30*time.Second,
	retry.Exponential{
		Initial: 10 * time.Millisecond,
		Factor:  1.5,
	},
)
for a := retry.Start(strategy, nil); a.Next(); {
	if reply, err := http.Post("http://example.com/form", "", nil); err == nil {
		reply.Body.Close()
		break
	}
}
Output:

func (Exponential) NewTimer

func (r Exponential) NewTimer(now time.Time) Timer

NewTimer implements Strategy.NewTimer.

type Regular

type Regular struct {
	// Total specifies the total duration of the attempt.
	Total time.Duration

	// Delay specifies the interval between the start of each try
	// in the burst. If an try takes longer than Delay, the
	// next try will happen immediately.
	Delay time.Duration

	// Min holds the minimum number of retries. It overrides Total.
	// To limit the maximum number of retries, use LimitCount.
	Min int
}

Regular represents a strategy that repeats at regular intervals.

func (Regular) NewTimer

func (r Regular) NewTimer(now time.Time) Timer

NewTimer implements Strategy.NewTimer.

func (Regular) Start

func (r Regular) Start(clk Clock) *Attempt

Start is short for Start(r, clk, nil)

type Strategy

type Strategy interface {
	// NewTimer is called when the strategy is started - it is
	// called with the time that the strategy is started and returns
	// an object that is used to find out how long to sleep before
	// each retry attempt.
	NewTimer(now time.Time) Timer
}

Strategy is implemented by types that represent a retry strategy.

Note: You probably won't need to implement a new strategy - the existing types and functions are intended to be sufficient for most purposes.

func LimitCount

func LimitCount(n int, strategy Strategy) Strategy

LimitCount limits the number of attempts that the given strategy will perform to n. Note that all strategies will allow at least one attempt.

func LimitTime

func LimitTime(limit time.Duration, strategy Strategy) Strategy

LimitTime limits the given strategy such that no attempt will made after the given duration has elapsed.

type Timer

type Timer interface {
	// NextSleep is called with the time that Next or More has been
	// called and returns the length of time to sleep before the
	// next retry. If no more attempts should be made it should
	// return false, and the returned duration will be ignored.
	//
	// Note that NextSleep is called once after each iteration has
	// completed, assuming the retry loop is continuing.
	NextSleep(now time.Time) (time.Duration, bool)
}

Timer represents a source of timing events for a retry strategy.

Jump to

Keyboard shortcuts

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