/* Simple library for retry mechanism slightly inspired by [Try::Tiny::Retry](https://metacpan.org/pod/Try::Tiny::Retry) SYNOPSIS http get with retry: url := "http://example.com" var body []byte err := retry.Do( func() error { resp, err := http.Get(url) if err != nil { return err } defer resp.Body.Close() body, err = ioutil.ReadAll(resp.Body) if err != nil { return err } return nil }, ) fmt.Println(body) [next examples](https://github.com/avast/retry-go/tree/master/examples) SEE ALSO * [giantswarm/retry-go](https://github.com/giantswarm/retry-go) - slightly complicated interface. * [sethgrid/pester](https://github.com/sethgrid/pester) - only http retry for http calls with retries and backoff * [cenkalti/backoff](https://github.com/cenkalti/backoff) - Go port of the exponential backoff algorithm from Google's HTTP Client Library for Java. Really complicated interface. * [rafaeljesus/retry-go](https://github.com/rafaeljesus/retry-go) - looks good, slightly similar as this package, don't have 'simple' `Retry` method * [matryer/try](https://github.com/matryer/try) - very popular package, nonintuitive interface (for me) BREAKING CHANGES 3.0.0 * `DelayTypeFunc` accepts a new parameter `err` - this breaking change affects only your custom Delay Functions. This change allow [make delay functions based on error](examples/delay_based_on_error_test.go). 1.0.2 -> 2.0.0 * argument of `retry.Delay` is final delay (no multiplication by `retry.Units` anymore) * function `retry.Units` are removed * [more about this breaking change](https://github.com/avast/retry-go/issues/7) 0.3.0 -> 1.0.0 * `retry.Retry` function are changed to `retry.Do` function * `retry.RetryCustom` (OnRetry) and `retry.RetryCustomWithOpts` functions are now implement via functions produces Options (aka `retry.OnRetry`) */
package retry import ( ) // Function signature of retryable function type RetryableFunc func() error var ( DefaultAttempts = uint(10) DefaultDelay = 100 * time.Millisecond DefaultMaxJitter = 100 * time.Millisecond DefaultOnRetry = func( uint, error) {} DefaultRetryIf = IsRecoverable DefaultDelayType = CombineDelay(BackOffDelay, RandomDelay) DefaultLastErrorOnly = false DefaultContext = context.Background() ) func ( RetryableFunc, ...Option) error { var uint //default := &Config{ attempts: DefaultAttempts, delay: DefaultDelay, maxJitter: DefaultMaxJitter, onRetry: DefaultOnRetry, retryIf: DefaultRetryIf, delayType: DefaultDelayType, lastErrorOnly: DefaultLastErrorOnly, context: DefaultContext, } //apply opts for , := range { () } if := .context.Err(); != nil { return } var Error if !.lastErrorOnly { = make(Error, .attempts) } else { = make(Error, 1) } := for < .attempts { := () if != nil { [] = unpackUnrecoverable() if !.retryIf() { break } .onRetry(, ) // if this is last attempt - don't wait if == .attempts-1 { break } := .delayType(, , ) if .maxDelay > 0 && > .maxDelay { = .maxDelay } select { case <-time.After(): case <-.context.Done(): return .context.Err() } } else { return nil } ++ if !.lastErrorOnly { = } } if .lastErrorOnly { return [] } return } // Error type represents list of errors in retry type Error []error // Error method return string representation of Error // It is an implementation of error interface func ( Error) () string { := make([]string, lenWithoutNil()) for , := range { if != nil { [] = fmt.Sprintf("#%d: %s", +1, .Error()) } } return fmt.Sprintf("All attempts fail:\n%s", strings.Join(, "\n")) } func lenWithoutNil( Error) ( int) { for , := range { if != nil { ++ } } return } // WrappedErrors returns the list of errors that this Error is wrapping. // It is an implementation of the `errwrap.Wrapper` interface // in package [errwrap](https://github.com/hashicorp/errwrap) so that // `retry.Error` can be used with that library. func ( Error) () []error { return } type unrecoverableError struct { error } // Unrecoverable wraps an error in `unrecoverableError` struct func ( error) error { return unrecoverableError{} } // IsRecoverable checks if error is an instance of `unrecoverableError` func ( error) bool { , := .(unrecoverableError) return ! } func unpackUnrecoverable( error) error { if , := .(unrecoverableError); { return .error } return }