1
// Copyright 2015 Canonical Ltd.
2
// Copyright 2015 Cloudbase Solutions SRL
3
// Licensed under the AGPLv3, see LICENCE file for details.
11
"github.com/juju/utils/clock"
14
// Countdown implements a timer that will call a provided function.
15
// after a internally stored duration. The steps as well as min and max
16
// durations are declared upon initialization and depend on
17
// the particular implementation.
18
type Countdown interface {
19
// Reset stops the timer and resets its duration to the minimum one.
20
// Start must be called to start the timer again.
23
// Start starts the internal timer.
24
// At the end of the timer, if Reset hasn't been called in the mean time
25
// Func will be called and the duration is increased for the next call.
29
// NewBackoffTimer creates and initializes a new BackoffTimer
30
// A backoff timer starts at min and gets multiplied by factor
31
// until it reaches max. Jitter determines whether a small
32
// randomization is added to the duration.
33
func NewBackoffTimer(config BackoffTimerConfig) *BackoffTimer {
36
currentDuration: config.Min,
40
// BackoffTimer implements Countdown.
41
// A backoff timer starts at min and gets multiplied by factor
42
// until it reaches max. Jitter determines whether a small
43
// randomization is added to the duration.
44
type BackoffTimer struct {
45
config BackoffTimerConfig
48
currentDuration time.Duration
51
// BackoffTimerConfig is a helper struct for backoff timer
52
// that encapsulates config information.
53
type BackoffTimerConfig struct {
54
// The minimum duration after which Func is called.
57
// The maximum duration after which Func is called.
60
// Determines whether a small randomization is applied to
64
// The factor by which you want the duration to increase
68
// Func is the function that will be called when the countdown reaches 0.
71
// Clock provides the AfterFunc function used to call func.
72
// It is exposed here so it's easier to mock it in tests.
76
// Start implements the Timer interface.
77
// Any existing timer execution is stopped before
78
// a new one is created.
79
func (t *BackoffTimer) Start() {
83
t.timer = t.config.Clock.AfterFunc(t.currentDuration, t.config.Func)
85
// Since it's a backoff timer we will increase
86
// the duration after each signal.
90
// Reset implements the Timer interface.
91
func (t *BackoffTimer) Reset() {
95
if t.currentDuration > t.config.Min {
96
t.currentDuration = t.config.Min
100
// increaseDuration will increase the duration based on
101
// the current value and the factor. If jitter is true
102
// it will add a 0.3% jitter to the final value.
103
func (t *BackoffTimer) increaseDuration() {
104
current := int64(t.currentDuration)
105
nextDuration := time.Duration(current * t.config.Factor)
107
// Get a factor in [-1; 1].
108
randFactor := (rand.Float64() * 2) - 1
109
jitter := float64(nextDuration) * randFactor * 0.03
110
nextDuration = nextDuration + time.Duration(jitter)
112
if nextDuration > t.config.Max {
113
nextDuration = t.config.Max
115
t.currentDuration = nextDuration