1package backoff
2
3import (
4	"math/rand"
5	"time"
6)
7
8type jitter interface {
9	apply(interval float64) float64
10}
11
12func newJitter(jitterFactor float64, rng Random) jitter {
13	if jitterFactor <= 0 || jitterFactor >= 1 {
14		return newNopJitter()
15	}
16	return newRandomJitter(jitterFactor, rng)
17}
18
19type nopJitter struct{}
20
21func newNopJitter() *nopJitter {
22	return &nopJitter{}
23}
24
25func (j *nopJitter) apply(interval float64) float64 {
26	return interval
27}
28
29type randomJitter struct {
30	jitterFactor float64
31	rng          Random
32}
33
34func newRandomJitter(jitterFactor float64, rng Random) *randomJitter {
35	if rng == nil {
36		// if we have a jitter factor, and no RNG is provided, create one.
37		// This is definitely not "secure", but well, if you care enough,
38		// you would provide one
39		rng = rand.New(rand.NewSource(time.Now().UnixNano()))
40	}
41
42	return &randomJitter{
43		jitterFactor: jitterFactor,
44		rng:          rng,
45	}
46}
47
48func (j *randomJitter) apply(interval float64) float64 {
49	jitterDelta := interval * j.jitterFactor
50	jitterMin := interval - jitterDelta
51	jitterMax := interval + jitterDelta
52
53	// Get a random value from the range [minInterval, maxInterval].
54	// The formula used below has a +1 because if the minInterval is 1 and the maxInterval is 3 then
55	// we want a 33% chance for selecting either 1, 2 or 3.
56	//
57	// see also: https://github.com/cenkalti/backoff/blob/c2975ffa541a1caeca5f76c396cb8c3e7b3bb5f8/exponential.go#L154-L157
58	return jitterMin + j.rng.Float64()*(jitterMax-jitterMin+1)
59}
60