60 lines
1.5 KiB
Go
60 lines
1.5 KiB
Go
|
package backoff
|
||
|
|
||
|
import (
|
||
|
"math/rand"
|
||
|
"time"
|
||
|
)
|
||
|
|
||
|
type jitter interface {
|
||
|
apply(interval float64) float64
|
||
|
}
|
||
|
|
||
|
func newJitter(jitterFactor float64, rng Random) jitter {
|
||
|
if jitterFactor <= 0 || jitterFactor >= 1 {
|
||
|
return newNopJitter()
|
||
|
}
|
||
|
return newRandomJitter(jitterFactor, rng)
|
||
|
}
|
||
|
|
||
|
type nopJitter struct{}
|
||
|
|
||
|
func newNopJitter() *nopJitter {
|
||
|
return &nopJitter{}
|
||
|
}
|
||
|
|
||
|
func (j *nopJitter) apply(interval float64) float64 {
|
||
|
return interval
|
||
|
}
|
||
|
|
||
|
type randomJitter struct {
|
||
|
jitterFactor float64
|
||
|
rng Random
|
||
|
}
|
||
|
|
||
|
func newRandomJitter(jitterFactor float64, rng Random) *randomJitter {
|
||
|
if rng == nil {
|
||
|
// if we have a jitter factor, and no RNG is provided, create one.
|
||
|
// This is definitely not "secure", but well, if you care enough,
|
||
|
// you would provide one
|
||
|
rng = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||
|
}
|
||
|
|
||
|
return &randomJitter{
|
||
|
jitterFactor: jitterFactor,
|
||
|
rng: rng,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (j *randomJitter) apply(interval float64) float64 {
|
||
|
jitterDelta := interval * j.jitterFactor
|
||
|
jitterMin := interval - jitterDelta
|
||
|
jitterMax := interval + jitterDelta
|
||
|
|
||
|
// Get a random value from the range [minInterval, maxInterval].
|
||
|
// The formula used below has a +1 because if the minInterval is 1 and the maxInterval is 3 then
|
||
|
// we want a 33% chance for selecting either 1, 2 or 3.
|
||
|
//
|
||
|
// see also: https://github.com/cenkalti/backoff/blob/c2975ffa541a1caeca5f76c396cb8c3e7b3bb5f8/exponential.go#L154-L157
|
||
|
return jitterMin + j.rng.Float64()*(jitterMax-jitterMin+1)
|
||
|
}
|