Retry is a Go package which wraps a function and invokes it repeatedly, until it succeeds - not returning an error. Multiple retry-able options are provided, based on number of attempts, delay options between attempts, errors to retry on or ignore, post attempts callback etc. Usable for interaction with flaky web services and similar unreliable sources of frustration.
go get -u github.com/adamliesko/retry
In the simplest and default configuration only about calling package level function Do()
, with the desired function.
If the failed function fails after 10 retries a custom error of Max Attempts reached is returned. An alternative is
using a reusable Retryer value struct (=instance in OOP), which will be reset after each Do()
method call.
import "external"
func poll() error{
return external.IsItDone()
}
// equivalent calls
err1 := retry.Do(poll)
err2 := retry.New().Do(poll)
r := retry.New()
err3 := r.Do(poll)
The usual usage would be to use either directly function, which returns an error or wrap the function call with a function, which sets the error according to the inner function output.
// can be used directly
func poll() error{
return external.IsItDone()
}
// has to be wrapped
func pollNoError bool{
return external.HasItSucceeded()
}
func wrappedPoll() error{
if !pollNoError(){
return errors.New("pollNoError has failed")
}
return nil
}
result := retry.Do(wrappedPoll)
- constant sleep delay after a failure
- custom function sleep delay (e.g. exponential back off)
- recovery of panics
- calling ensure function, regardless of the Retryer's work inside, once that it finishes
- calling a custom function after each failure
- ignoring certain errors
- retrying only on certain errors
func poll() error { return external.IsItDone() }
err := retry.New(retry.Sleep(100))
result := r.Do(poll)
func poll() error { return external.IsItDone() }
sleepFn := func(attempts int) {
sleep := time.Duration(2^attempts) * time.Millisecond
time.Sleep(sleep)
}
err := retry.New(retry.SleepFn(sleepFn)).Do(poll)
func poll() error { return external.IsItDone() }
func ensure(err error){
fmt.Println("ensure will be called regardless of err value")
}
err := retry.Do(poll, retry.Ensure(ensure))
type MyError struct {}
func (e MyError) Error() string { return "this is my custom error" }
func poll() error { return external.IsItDone() }
err := retry.New(Not([]errors{MyError{}})).Do(poll)
type MyError struct {}
func (e MyError) Error() string { return "this is my custom error" }
func poll() error { return external.IsItDone() }
err := retry.New(On([]errors{MyError{}})).Do(poll)
- recovery of panics
- attempting to call the function up to 15 times
- sleeping for 200 ms after each failed attempt
- printing the failures to the Stdout
func poll() error { return external.IsItDone() }
failCallback := func(err error){ fmt.Println("failed with error", error) }
r := retry.New(retry.Sleep(200), retry.Tries(15), retry.Recover(), retry.AfterEachFail(failCallback)
err := r.Do(poll)
See LICENSE.