retry

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

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

Go to latest
Published: Oct 17, 2019 License: MIT Imports: 6 Imported by: 1

README

retry GoDoc Report card Travis-CI Coverage Status

Package retry provides a reliable, simple way to retry operations.

go get -u github.com/AdamSLevy/retry

	// The provided policies can be composed to a custom Policy.
	policy := retry.LimitTotal{25 * time.Minute,
		retry.LimitAttempts{10,
			retry.Max{10 * time.Minute,
				retry.Randomize{.5,
					retry.Exponential{5 * time.Second, 2}}}}}

	// A notify function is called before each wait period.
	notify := func(err error, attempt uint, d time.Duration) {
		fmt.Printf("Attempt %v returned %v. Retrying in %v...\n",
			attempt, err, d)
	}

	// A filter function can be used to omit or wrap certain errors to tell
	// Run to stop immediately.
	filter := func(err error) error {
		if errors.Is(err, errors.New("unrecoverable err")) {
			return retry.ErrorStop(err)
		}
		return err
	}

	// A context.Context may be passed so that waits can be canceled.
	var ctx = context.TODO()

	err := retry.Run(ctx, policy, filter, notify, func() error {
		return tryWork(ctx)
	})
	if err != nil {
		return
	}

This package was inspired by github.com/cenkalti/backoff but improves on the design by providing Policy types that are composable, re-usable and safe for repeated or concurrent calls to Run.

Documentation

Overview

Package retry provides a reliable, simple way to retry operations.

This package was inspired by github.com/cenkalti/backoff but improves on the design by providing Policy types that are composable, re-usable and safe for repeated or concurrent calls to Run.

Index

Examples

Constants

View Source
const Stop = time.Duration(-1)

Stop can be returned by Policy.Wait to tell Run to stop.

Variables

This section is empty.

Functions

func ErrorStop

func ErrorStop(err error) error

ErrorStop wraps err such that when returned from an op or filter, it will cause Run to stop immediately and return err.

func Run

func Run(ctx context.Context,
	p Policy, filter func(error) error,
	notify func(error, uint, time.Duration),
	op func() error) error

Run op until one of the following occurs,

  • op returns nil.
  • op returns context.Canceled or context.DeadlineExceeded.
  • op returns an error wrapped by ErrorStop.
  • p.Wait returns Stop.
  • ctx.Done() is closed.

If the above conditions are not met, then op is retried after waiting p.Wait. The total number of attempts and the total time elapsed since Run was envoked are passed to p.Wait. See Policy for more details.

If filter is not nil, all calls to op are wrapped by filter: filter(op()). Use a filter to add special handling for certain errors. For example, a filter can cause Run to return immediately by returning nil to censor and error, or by wrapping the error with ErrorStop to pass an error up the call stack.

Run always returns the latest filtered op return value. If the error was wrapped by ErrorStop, it is unwrapped, and the original error is returned.

If notify is not nil, it is called with the latest return values of op and p.Wait prior to waiting.

If ctx is nil, context.Background() is used.

If ctx.Done() is closed while waiting, Run returns immediately.

Example
package main

import (
	"context"
	"errors"
	"fmt"
	"time"

	"github.com/AdamSLevy/retry"
)

func workToRetry(context.Context) error { return nil }

func main() {
	// The provided policies can be composed to a custom Policy. The
	// following Policy implements exponential backoff. The policy
	// increases exponentially by a factor of 1.5 starting from 500
	// milliseconds with some random variation, up to a max wait time of a
	// minute. Additionally, the Policy limits retries to 15 attempts or 20
	// minutes.
	policy := retry.LimitTotal{20 * time.Minute,
		retry.LimitAttempts{15,
			retry.Max{time.Minute,
				retry.Randomize{.5,
					retry.Exponential{500 * time.Millisecond, 1.5}}}}}

	// A notify function is called before each wait period.
	notify := func(err error, attempt uint, d time.Duration) {
		fmt.Printf("Attempt %v returned %v. Retrying in %v...\n",
			attempt, err, d)
	}

	// A filter function can be used to omit or wrap certain errors to tell
	// Run to stop immediately.
	filter := func(err error) error {
		if errors.Is(err, errors.New("unrecoverable")) {
			// Run will return err.
			return retry.ErrorStop(err)
		}
		if errors.Is(err, errors.New("ignorable")) {
			// Run will return nil.
			return nil
		}
		return err
	}

	// A context.Context may be passed so that waits can be canceled.
	var ctx = context.TODO()

	err := retry.Run(ctx, policy, filter, notify, func() error {
		// If your op requires a context.Context you should create a
		// closure around it. If tryWork returns context.Canceled or
		// context.DeadlinExceeded Run will return immediately.
		return workToRetry(ctx)
	})
	if err != nil {
		return
	}
}
Output:

Types

type Constant

type Constant time.Duration

Constant is a Policy that always returns a fixed waited time.

func (Constant) Wait

Wait always returns c.Fixed.

type Exponential

type Exponential struct {
	Initial    time.Duration
	Multiplier float64
}

Exponential is a Policy that increases wait time exponentially starting from Initial and multiplying Multiplier for each additional attempt.

Initial must be non-zero and Multiplier must be greater than 1 in order for the wait time to increase.

func (Exponential) Wait

func (e Exponential) Wait(attempts uint, total time.Duration) time.Duration

Wait returns e.Initial * math.Pow(e.Multiplier, attempts) up to the number of attempts that would cause overflow, at which point the largest value that does not overflow is returned.

type Immediate

type Immediate struct{}

Immediate is a Policy that always returns a zero wait time.

func (Immediate) Wait

Wait always returns c.Fixed.

type LimitAttempts

type LimitAttempts struct {
	Limit uint
	Policy
}

LimitAttempts wraps a Policy such that Run will return after Limit attempts.

func (LimitAttempts) Wait

func (l LimitAttempts) Wait(attempts uint, total time.Duration) time.Duration

Wait returns Stop if attempts >= l.Limit, otherwise the result of l.Policy.Wait(attempts, total) is returned.

type LimitTotal

type LimitTotal struct {
	Limit time.Duration
	Policy
}

LimitTotal wraps a Policy such that Run will stop after total time meets or exceeds Limit.

func (LimitTotal) Wait

func (l LimitTotal) Wait(attempts uint, total time.Duration) time.Duration

Wait returns Stop if total >= l.Limit, otherwise the result of l.Policy.Wait(attempts, total) is returned.

type Linear

type Linear struct {
	Initial   time.Duration
	Increment time.Duration
}

Linear is a Policy that increases wait time linearly starting from Initial and adding Increment for each additional attempt.

func (Linear) Wait

func (l Linear) Wait(attempts uint, total time.Duration) time.Duration

Wait returns l.Initial + (attempts-1)*l.Increment or math.MaxInt64 if any integer overflow occurs.

type Max

type Max struct {
	Cap time.Duration
	Policy
}

Max wraps a Policy such that wait time is capped to Cap.

func (Max) Wait

func (m Max) Wait(attempts uint, total time.Duration) time.Duration

Wait returns the minimum between m.Max and the result of m.Policy.Wait(attempts, total).

type Policy

type Policy interface {
	// Wait returns a wait time based on the number of previous failed
	// attempts within a call to Run, and the total = time.Since(start),
	// where start is the time.Now() when Run was called.
	//
	// For example, after the first failed op, Run calls Wait with attempts
	// = 1 and a total time roughly equal to how long the first call to op
	// took.
	//
	// If Wait returns 0, Run retries its op immediately.
	//
	// If Wait returns Stop, Run returns the last op error immediately.
	//
	// In order to ensure that a Policy is re-usable across concurrent
	// calls to Run, Wait should not have any side-effects such as mutating
	// any internal state of Policy. The one exception to this is the use
	// of math/rand.Float64() the in Randomize Policy.
	Wait(attempts uint, total time.Duration) (wait time.Duration)
}

Policy tells Run how long to wait before the next retry of op.

type Randomize

type Randomize struct {
	Factor float64
	Policy
}

Randomize wraps a Policy such that its wait time is randomly selected from the range [wait * (1 - Factor), wait * (1 + Factor)].

func (Randomize) Wait

func (r Randomize) Wait(attempts uint, total time.Duration) time.Duration

Wait returns a wait time randomly selected from the range

[wait * (1 - r.Factor), wait * (1 + r.Factor)]

such that wait will not overflow, where wait is the return value of r.Policy.Wait(attempts, total).

If wait is 0 or Stop, it is returned directly.

Jump to

Keyboard shortcuts

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