blob: 6b32f09df6062f27e39d522fc76b7cf8bc398861 [file] [log] [blame]
David K. Bainbridge528b3182017-01-23 08:51:59 -08001// Copyright 2015 Canonical Ltd.
2// Copyright 2015 Cloudbase Solutions SRL
3// Licensed under the LGPLv3, see LICENCE file for details.
4
5package utils
6
7import (
8 "math/rand"
9 "time"
10
11 "github.com/juju/utils/clock"
12)
13
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//
19// TODO(katco): 2016-08-09: This type is deprecated: lp:1611427
20type Countdown interface {
21 // Reset stops the timer and resets its duration to the minimum one.
22 // Start must be called to start the timer again.
23 Reset()
24
25 // Start starts the internal timer.
26 // At the end of the timer, if Reset hasn't been called in the mean time
27 // Func will be called and the duration is increased for the next call.
28 Start()
29}
30
31// NewBackoffTimer creates and initializes a new BackoffTimer
32// A backoff timer starts at min and gets multiplied by factor
33// until it reaches max. Jitter determines whether a small
34// randomization is added to the duration.
35//
36// TODO(katco): 2016-08-09: This type is deprecated: lp:1611427
37func NewBackoffTimer(config BackoffTimerConfig) *BackoffTimer {
38 return &BackoffTimer{
39 config: config,
40 currentDuration: config.Min,
41 }
42}
43
44// BackoffTimer implements Countdown.
45// A backoff timer starts at min and gets multiplied by factor
46// until it reaches max. Jitter determines whether a small
47// randomization is added to the duration.
48//
49// TODO(katco): 2016-08-09: This type is deprecated: lp:1611427
50type BackoffTimer struct {
51 config BackoffTimerConfig
52
53 timer clock.Timer
54 currentDuration time.Duration
55}
56
57// BackoffTimerConfig is a helper struct for backoff timer
58// that encapsulates config information.
59//
60// TODO(katco): 2016-08-09: This type is deprecated: lp:1611427
61type BackoffTimerConfig struct {
62 // The minimum duration after which Func is called.
63 Min time.Duration
64
65 // The maximum duration after which Func is called.
66 Max time.Duration
67
68 // Determines whether a small randomization is applied to
69 // the duration.
70 Jitter bool
71
72 // The factor by which you want the duration to increase
73 // every time.
74 Factor int64
75
76 // Func is the function that will be called when the countdown reaches 0.
77 Func func()
78
79 // Clock provides the AfterFunc function used to call func.
80 // It is exposed here so it's easier to mock it in tests.
81 Clock clock.Clock
82}
83
84// Start implements the Timer interface.
85// Any existing timer execution is stopped before
86// a new one is created.
87func (t *BackoffTimer) Start() {
88 if t.timer != nil {
89 t.timer.Stop()
90 }
91 t.timer = t.config.Clock.AfterFunc(t.currentDuration, t.config.Func)
92
93 // Since it's a backoff timer we will increase
94 // the duration after each signal.
95 t.increaseDuration()
96}
97
98// Reset implements the Timer interface.
99func (t *BackoffTimer) Reset() {
100 if t.timer != nil {
101 t.timer.Stop()
102 }
103 if t.currentDuration > t.config.Min {
104 t.currentDuration = t.config.Min
105 }
106}
107
108// increaseDuration will increase the duration based on
109// the current value and the factor. If jitter is true
110// it will add a 0.3% jitter to the final value.
111func (t *BackoffTimer) increaseDuration() {
112 current := int64(t.currentDuration)
113 nextDuration := time.Duration(current * t.config.Factor)
114 if t.config.Jitter {
115 // Get a factor in [-1; 1].
116 randFactor := (rand.Float64() * 2) - 1
117 jitter := float64(nextDuration) * randFactor * 0.03
118 nextDuration = nextDuration + time.Duration(jitter)
119 }
120 if nextDuration > t.config.Max {
121 nextDuration = t.config.Max
122 }
123 t.currentDuration = nextDuration
124}