fiber/addon/retry/exponential_backoff.go
Gökhan Özeloğlu 4adda508b0
v3 (feature): add retry mechanism (#1972)
* v3-retry-mechanism: Add retry mechanism

* General logic is implemented.

* Unit tests are added.

Signed-off-by: Gökhan Özeloğlu <gokhan.ozeloglu@deliveryhero.com>

* Refactor test assertion

* Replaced testify/assert with fiber's assert.

Signed-off-by: Gökhan Özeloğlu <gokhan.ozeloglu@deliveryhero.com>

* Add test for next method

* currentInterval bug is fixed in Retry.

* If condition is fixed in next.

* struct definition refactored and if condtion is removed in TestExponentialBackoff_Retry.

Signed-off-by: Gökhan Özeloğlu <gokhan.ozeloglu@deliveryhero.com>

* Add config for retry.

* Constant variables are removed.

* Helper function is added for default.

* Helper function is used in New function.

Signed-off-by: Gökhan Özeloğlu <gokhan.ozeloglu@deliveryhero.com>

* Replace math/rand with crypto/rand

* Random number generation package has been replaced with more secure one,
crypto/rand.

Signed-off-by: Gökhan Özeloğlu <gokhan.ozeloglu@deliveryhero.com>

* Add a README for retry middleware

* Explanation and examples are added.

Signed-off-by: Gökhan Özeloğlu <gokhan.ozeloglu@deliveryhero.com>

* Add comment line for documentation

* Comment lines are added for ExponentialBackoff variables.

Signed-off-by: Gökhan Özeloğlu <gokhan.ozeloglu@deliveryhero.com>

* Run go mod tidy

* Unused package(s) removed.

Signed-off-by: Gökhan Özeloğlu <gokhan.ozeloglu@deliveryhero.com>

* move middleware -> addon

Signed-off-by: Gökhan Özeloğlu <gokhan.ozeloglu@deliveryhero.com>
Co-authored-by: Muhammed Efe Çetin <efectn@protonmail.com>
2022-08-19 08:20:14 +02:00

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
}