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 ¶
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 ¶
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 Exponential ¶
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.
type Immediate ¶
type Immediate struct{}
Immediate is a Policy that always returns a zero wait time.
type LimitAttempts ¶
LimitAttempts wraps a Policy such that Run will return after Limit attempts.
type LimitTotal ¶
LimitTotal wraps a Policy such that Run will stop after total time meets or exceeds Limit.
type Linear ¶
Linear is a Policy that increases wait time linearly starting from Initial and adding Increment for each additional attempt.
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 ¶
Randomize wraps a Policy such that its wait time is randomly selected from the range [wait * (1 - Factor), wait * (1 + Factor)].