152 lines
3.5 KiB
Go
152 lines
3.5 KiB
Go
|
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
|
||
|
}
|
||
|
}
|