retry: github.com/juju/retry Index | Files

package retry

import "github.com/juju/retry"

The retry package encapsulates the mechanism around retrying commands.

The simple use is to call retry.Call with a function closure.

err := retry.Call(retry.CallArgs{
	Func:     func() error { ... },
	Attempts: 5,
	Delay:    time.Minute,
	Clock:    clock.WallClock,
})

The bare minimum arguments that need to be specified are:

* Func - the function to call
* Attempts - the number of times to try Func before giving up, or a negative number for unlimited attempts (`retry.UnlimitedAttempts`)
* Delay - how long to wait between each try that returns an error
* Clock - either the wall clock, or some testing clock

Any error that is returned from the Func is considered transient. In order to identify some errors as fatal, pass in a function for the IsFatalError CallArgs value.

In order to have the Delay change for each iteration, a BackoffFunc needs to be set on the CallArgs. A simple doubling delay function is provided by DoubleDelay.

An example of a more complex BackoffFunc could be a stepped function such as:

func StepDelay(last time.Duration, attempt int) time.Duration {
	switch attempt{
	case 1:
		return time.Second
	case 2:
		return 5 * time.Second
	case 3:
		return 20 * time.Second
	case 4:
		return time.Minute
	case 5:
		return 5 * time.Minute
	default:
		return 2 * last
	}
}

Consider some package foo that has a TryAgainError, which looks something like this:

type TryAgainError struct {
	After time.Duration
}

and we create something that looks like this:

type TryAgainHelper struct {
	next time.Duration
}

func (h *TryAgainHelper) notify(lastError error, attempt int) {
	if tryAgain, ok := lastError.(*foo.TryAgainError); ok {
		h.next = tryAgain.After
	} else {
		h.next = 0
	}
}

func (h *TryAgainHelper) next(last time.Duration) time.Duration {
	if h.next != 0 {
		return h.next
	}
	return last
}

Then we could do this:

helper := TryAgainHelper{}
retry.Call(retry.CallArgs{
	Func: func() error {
		return foo.SomeFunc()
	},
	NotifyFunc:  helper.notify,
	BackoffFunc: helper.next,
	Attempts:    20,
	Delay:       100 * time.Millisecond,
	Clock:       clock.WallClock,
})

Index

Package Files

clock.go doc.go retry.go

Constants

const (
    // UnlimitedAttempts can be used as a value for Attempts to clearly
    // show to the reader that there is no limit to the number of attempts.
    UnlimitedAttempts = -1
)

func Call Uses

func Call(args CallArgs) error

Call will repeatedly execute the Func until either the function returns no error, the retry count is exceeded or the stop channel is closed.

func DoubleDelay Uses

func DoubleDelay(delay time.Duration, attempt int) time.Duration

DoubleDelay provides a simple function that doubles the duration passed in. This can then be easily used as the BackoffFunc in the CallArgs structure.

func IsAttemptsExceeded Uses

func IsAttemptsExceeded(err error) bool

IsAttemptsExceeded returns true if the error is the result of the Call function finishing due to hitting the requested number of Attempts.

func IsDurationExceeded Uses

func IsDurationExceeded(err error) bool

IsDurationExceeded returns true if the error is the result of the Call function finishing due to the total duration exceeding the specified MaxDuration value.

func IsRetryStopped Uses

func IsRetryStopped(err error) bool

IsRetryStopped returns true if the error is the result of the Call function finishing due to the stop channel being closed.

func LastError Uses

func LastError(err error) error

LastError retrieves the last error returned from Func before iteration was terminated due to the attempt count being exceeded, the maximum duration being exceeded, or the stop channel being closed.

type CallArgs Uses

type CallArgs struct {
    // Func is the function that will be retried if it returns an error result.
    Func func() error

    // IsFatalError is a function that, if set, will be called for every non-
    // nil error result from Func. If IsFatalError returns true, the error
    // is immediately returned breaking out from any further retries.
    IsFatalError func(error) bool

    // NotifyFunc is a function that is called if Func fails, and the attempt
    // number. The first time this function is called attempt is 1, the second
    // time, attempt is 2 and so on.
    NotifyFunc func(lastError error, attempt int)

    // Attempts specifies the number of times Func should be retried before
    // giving up and returning the AttemptsExceeded error. If a negative
    // value is specified, the Call will retry forever.
    Attempts int

    // Delay specifies how long to wait between retries.
    Delay time.Duration

    // MaxDelay specifies how longest time to wait between retries. If no
    // value is specified there is no maximum delay.
    MaxDelay time.Duration

    // MaxDuration specifies the maximum time the Call function should spend
    // iterating over Func. The duration is calculated from the start of the
    // Call function.  If the next delay time would take the total duration
    // of the call over MaxDuration, then a DurationExceeded error is
    // returned. If no value is specified, Call will continue until the number
    // of attempts is complete.
    MaxDuration time.Duration

    // BackoffFunc allows the caller to provide a function that alters the
    // delay each time through the loop. If this function is not provided the
    // delay is the same each iteration. Alternatively a function such as
    // retry.DoubleDelay can be used that will provide an exponential
    // backoff. The first time this function is called attempt is 1, the
    // second time, attempt is 2 and so on.
    BackoffFunc func(delay time.Duration, attempt int) time.Duration

    // Clock provides the mechanism for waiting. Normal program execution is
    // expected to use something like clock.WallClock, and tests can override
    // this to not actually sleep in tests.
    Clock Clock

    // Stop is a channel that can be used to indicate that the waiting should
    // be interrupted. If Stop is nil, then the Call function cannot be interrupted.
    // If the channel is closed prior to the Call function being executed, the
    // Func is still attempted once.
    Stop <-chan struct{}
}

CallArgs is a simple structure used to define the behaviour of the Call function.

func (*CallArgs) Validate Uses

func (args *CallArgs) Validate() error

Validate the values are valid. The ensures that the Func, Delay, Attempts and Clock have been specified.

type Clock Uses

type Clock interface {
    // Now returns the current clock time.
    Now() time.Time

    // After waits for the duration to elapse and then sends the
    // current time on the returned channel.
    After(time.Duration) <-chan time.Time
}

Clock provides an interface for dealing with clocks.

Package retry imports 3 packages (graph) and is imported by 167 packages. Updated 2018-09-08. Refresh now. Tools for package owners.