Commit 4ff0a623 authored by protolambda's avatar protolambda

op-service: fix exponential backoff strategy in case of high number of attempts

parent 8a3c8964
......@@ -14,7 +14,7 @@ type Strategy interface {
}
// ExponentialStrategy performs exponential backoff. The exponential backoff
// function is min(e.Min + (2^attempt * 1000) + randBetween(0, e.MaxJitter), e.Max)
// function is min(e.Min + (2^attempt * second), e.Max) + randBetween(0, e.MaxJitter)
type ExponentialStrategy struct {
// Min is the minimum amount of time to wait between attempts.
Min time.Duration
......@@ -23,15 +23,24 @@ type ExponentialStrategy struct {
Max time.Duration
// MaxJitter is the maximum amount of random jitter to insert between attempts.
// Jitter is added on top of the maximum, if the maximum is reached.
MaxJitter time.Duration
}
func (e *ExponentialStrategy) Duration(attempt int) time.Duration {
var jitter time.Duration
var jitter time.Duration // non-negative jitter
if e.MaxJitter > 0 {
jitter = time.Duration(rand.Int63n(e.MaxJitter.Nanoseconds()))
}
dur := e.Min + time.Duration(int(math.Pow(2, float64(attempt))*1000))*time.Millisecond
if attempt < 0 {
return e.Min + jitter
}
durFloat := float64(e.Min) * float64(time.Nanosecond)
durFloat += math.Pow(2, float64(attempt)) * float64(time.Second)
dur := time.Duration(durFloat)
if durFloat > float64(e.Max) {
dur = e.Max
}
dur += jitter
if dur > e.Max {
return e.Max
......@@ -42,8 +51,9 @@ func (e *ExponentialStrategy) Duration(attempt int) time.Duration {
func Exponential() Strategy {
return &ExponentialStrategy{
Max: time.Duration(10000 * time.Millisecond),
MaxJitter: time.Duration(250 * time.Millisecond),
Min: 0,
Max: 10 * time.Second,
MaxJitter: 250 * time.Millisecond,
}
}
......
package retry
import (
"math"
"testing"
"time"
......@@ -9,13 +10,17 @@ import (
func TestExponential(t *testing.T) {
strategy := &ExponentialStrategy{
Min: 3000 * time.Millisecond,
Max: 10000 * time.Millisecond,
Min: 3 * time.Second,
Max: 10 * time.Second,
MaxJitter: 0,
}
require.Equal(t, 3*time.Second, strategy.Duration(-1))
durations := []time.Duration{4, 5, 7, 10, 10}
for i, dur := range durations {
require.Equal(t, dur*time.Second, strategy.Duration(i))
require.Equal(t, dur*time.Second, strategy.Duration(i), "attempt %d", i)
}
require.Equal(t, 10*time.Second, strategy.Duration(100))
require.Equal(t, 10*time.Second, strategy.Duration(16000))
require.Equal(t, 10*time.Second, strategy.Duration(math.MaxInt))
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment