package retry import ( "math/rand" "time" ) // Function signature of retry if function type RetryIfFunc func(error) bool // Function signature of OnRetry function // n = count of attempts type OnRetryFunc func(n uint, err error) type DelayTypeFunc func(n uint, config *Config) time.Duration type Config struct { attempts uint delay time.Duration maxDelay time.Duration maxJitter time.Duration onRetry OnRetryFunc retryIf RetryIfFunc delayType DelayTypeFunc lastErrorOnly bool } // Option represents an option for retry. type Option func(*Config) // return the direct last error that came from the retried function // default is false (return wrapped errors with everything) func LastErrorOnly(lastErrorOnly bool) Option { return func(c *Config) { c.lastErrorOnly = lastErrorOnly } } // Attempts set count of retry // default is 10 func Attempts(attempts uint) Option { return func(c *Config) { c.attempts = attempts } } // Delay set delay between retry // default is 100ms func Delay(delay time.Duration) Option { return func(c *Config) { c.delay = delay } } // MaxDelay set maximum delay between retry // does not apply by default func MaxDelay(maxDelay time.Duration) Option { return func(c *Config) { c.maxDelay = maxDelay } } // MaxJitter sets the maximum random Jitter between retries for RandomDelay func MaxJitter(maxJitter time.Duration) Option { return func(c *Config) { c.maxJitter = maxJitter } } // DelayType set type of the delay between retries // default is BackOff func DelayType(delayType DelayTypeFunc) Option { return func(c *Config) { c.delayType = delayType } } // BackOffDelay is a DelayType which increases delay between consecutive retries func BackOffDelay(n uint, config *Config) time.Duration { return config.delay * (1 << n) } // FixedDelay is a DelayType which keeps delay the same through all iterations func FixedDelay(_ uint, config *Config) time.Duration { return config.delay } // RandomDelay is a DelayType which picks a random delay up to config.maxJitter func RandomDelay(_ uint, config *Config) time.Duration { return time.Duration(rand.Int63n(int64(config.maxJitter))) } // CombineDelay is a DelayType the combines all of the specified delays into a new DelayTypeFunc func CombineDelay(delays ...DelayTypeFunc) DelayTypeFunc { return func(n uint, config *Config) time.Duration { var total time.Duration for _, delay := range delays { total += delay(n, config) } return total } } // OnRetry function callback are called each retry // // log each retry example: // // retry.Do( // func() error { // return errors.New("some error") // }, // retry.OnRetry(func(n uint, err error) { // log.Printf("#%d: %s\n", n, err) // }), // ) func OnRetry(onRetry OnRetryFunc) Option { return func(c *Config) { c.onRetry = onRetry } } // RetryIf controls whether a retry should be attempted after an error // (assuming there are any retry attempts remaining) // // skip retry if special error example: // // retry.Do( // func() error { // return errors.New("special error") // }, // retry.RetryIf(func(err error) bool { // if err.Error() == "special error" { // return false // } // return true // }) // ) // // By default RetryIf stops execution if the error is wrapped using `retry.Unrecoverable`, // so above example may also be shortened to: // // retry.Do( // func() error { // return retry.Unrecoverable(errors.New("special error")) // } // ) func RetryIf(retryIf RetryIfFunc) Option { return func(c *Config) { c.retryIf = retryIf } }