mirror of https://github.com/gofiber/fiber.git
74 lines
2.1 KiB
Go
74 lines
2.1 KiB
Go
package retry
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"math/big"
|
|
"time"
|
|
)
|
|
|
|
// ExponentialBackoff is a retry mechanism for retrying some calls.
|
|
type ExponentialBackoff struct {
|
|
// InitialInterval is the initial time interval for backoff algorithm.
|
|
InitialInterval time.Duration
|
|
|
|
// MaxBackoffTime is the maximum time duration for backoff algorithm. It limits
|
|
// the maximum sleep time.
|
|
MaxBackoffTime time.Duration
|
|
|
|
// Multiplier is a multiplier number of the backoff algorithm.
|
|
Multiplier float64
|
|
|
|
// MaxRetryCount is the maximum number of retry count.
|
|
MaxRetryCount int
|
|
|
|
// currentInterval tracks the current sleep time.
|
|
currentInterval time.Duration
|
|
}
|
|
|
|
// NewExponentialBackoff creates a ExponentialBackoff with default values.
|
|
func NewExponentialBackoff(config ...Config) *ExponentialBackoff {
|
|
cfg := configDefault(config...)
|
|
return &ExponentialBackoff{
|
|
InitialInterval: cfg.InitialInterval,
|
|
MaxBackoffTime: cfg.MaxBackoffTime,
|
|
Multiplier: cfg.Multiplier,
|
|
MaxRetryCount: cfg.MaxRetryCount,
|
|
currentInterval: cfg.currentInterval,
|
|
}
|
|
}
|
|
|
|
// Retry is the core logic of the retry mechanism. If the calling function returns
|
|
// nil as an error, then the Retry method is terminated with returning nil. Otherwise,
|
|
// if all function calls are returned error, then the method returns this error.
|
|
func (e *ExponentialBackoff) Retry(f func() error) error {
|
|
if e.currentInterval <= 0 {
|
|
e.currentInterval = e.InitialInterval
|
|
}
|
|
var err error
|
|
for i := 0; i < e.MaxRetryCount; i++ {
|
|
err = f()
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
next := e.next()
|
|
time.Sleep(next)
|
|
}
|
|
return err
|
|
}
|
|
|
|
// next calculates the next sleeping time interval.
|
|
func (e *ExponentialBackoff) next() time.Duration {
|
|
// generate a random value between [0, 1000)
|
|
n, err := rand.Int(rand.Reader, big.NewInt(1000))
|
|
if err != nil {
|
|
return e.MaxBackoffTime
|
|
}
|
|
t := e.currentInterval + (time.Duration(n.Int64()) * time.Millisecond)
|
|
e.currentInterval = time.Duration(float64(e.currentInterval) * e.Multiplier)
|
|
if t >= e.MaxBackoffTime {
|
|
e.currentInterval = e.MaxBackoffTime
|
|
return e.MaxBackoffTime
|
|
}
|
|
return t
|
|
}
|