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,
})
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(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(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 ExpBackoff(minDelay, maxDelay time.Duration, exp float64, applyJitter bool) func(time.Duration, int) time.Duration {
ExpBackoff returns a function a which generates time.Duration values using an
exponential back-off algorithm with the specified parameters. The returned value
can then be easily used as the BackoffFunc
in the CallArgs
structure.
The next delay value is calculated using the following formula:
newDelay = min(minDelay * exp^attempt, maxDelay)
If applyJitter
is set to true
, the function will randomly select and return
back a value in the [minDelay, newDelay]
range.
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(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(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(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 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.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 (args *CallArgs) Validate() error
Validate the values are valid. The ensures that the Func, Delay, Attempts and Clock have been specified.
Generated by godoc2md