File: exponential.go

package info (click to toggle)
golang-github-lestrrat-go-backoff 2.0.8-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 200 kB
  • sloc: makefile: 2
file content (107 lines) | stat: -rw-r--r-- 2,330 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
package backoff

import (
	"context"
	"time"
)

type ExponentialInterval struct {
	current     float64
	maxInterval float64
	minInterval float64
	multiplier  float64
	jitter      jitter
}

const (
	defaultMaxInterval = float64(time.Minute)
	defaultMinInterval = float64(500 * time.Millisecond)
	defaultMultiplier  = 1.5
)

func NewExponentialInterval(options ...ExponentialOption) *ExponentialInterval {
	jitterFactor := 0.0
	maxInterval := defaultMaxInterval
	minInterval := defaultMinInterval
	multiplier := defaultMultiplier
	var rng Random

	for _, option := range options {
		switch option.Ident() {
		case identJitterFactor{}:
			jitterFactor = option.Value().(float64)
		case identMaxInterval{}:
			maxInterval = float64(option.Value().(time.Duration))
		case identMinInterval{}:
			minInterval = float64(option.Value().(time.Duration))
		case identMultiplier{}:
			multiplier = option.Value().(float64)
		case identRNG{}:
			rng = option.Value().(Random)
		}
	}

	if minInterval > maxInterval {
		minInterval = maxInterval
	}
	if multiplier <= 1 {
		multiplier = defaultMultiplier
	}

	return &ExponentialInterval{
		maxInterval: maxInterval,
		minInterval: minInterval,
		multiplier:  multiplier,
		jitter:      newJitter(jitterFactor, rng),
	}
}

func (g *ExponentialInterval) Next() time.Duration {
	var next float64
	if g.current == 0 {
		next = g.minInterval
	} else {
		next = g.current * g.multiplier
	}

	if next > g.maxInterval {
		next = g.maxInterval
	}
	if next < g.minInterval {
		next = g.minInterval
	}

	// Apply jitter *AFTER* we calculate the base interval
	next = g.jitter.apply(next)
	g.current = next
	return time.Duration(next)
}

type ExponentialPolicy struct {
	cOptions  []ControllerOption
	igOptions []ExponentialOption
}

func NewExponentialPolicy(options ...ExponentialOption) *ExponentialPolicy {
	var cOptions []ControllerOption
	var igOptions []ExponentialOption

	for _, option := range options {
		switch opt := option.(type) {
		case ControllerOption:
			cOptions = append(cOptions, opt)
		default:
			igOptions = append(igOptions, opt)
		}
	}

	return &ExponentialPolicy{
		cOptions:  cOptions,
		igOptions: igOptions,
	}
}

func (p *ExponentialPolicy) Start(ctx context.Context) Controller {
	ig := NewExponentialInterval(p.igOptions...)
	return newController(ctx, ig, p.cOptions...)
}