~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/utils/timer.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2015 Canonical Ltd.
 
2
// Copyright 2015 Cloudbase Solutions SRL
 
3
// Licensed under the AGPLv3, see LICENCE file for details.
 
4
 
 
5
package utils
 
6
 
 
7
import (
 
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
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.
 
21
        Reset()
 
22
 
 
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.
 
26
        Start()
 
27
}
 
28
 
 
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 {
 
34
        return &BackoffTimer{
 
35
                config:          config,
 
36
                currentDuration: config.Min,
 
37
        }
 
38
}
 
39
 
 
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
 
46
 
 
47
        timer           clock.Timer
 
48
        currentDuration time.Duration
 
49
}
 
50
 
 
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.
 
55
        Min time.Duration
 
56
 
 
57
        // The maximum duration after which Func is called.
 
58
        Max time.Duration
 
59
 
 
60
        // Determines whether a small randomization is applied to
 
61
        // the duration.
 
62
        Jitter bool
 
63
 
 
64
        // The factor by which you want the duration to increase
 
65
        // every time.
 
66
        Factor int64
 
67
 
 
68
        // Func is the function that will be called when the countdown reaches 0.
 
69
        Func func()
 
70
 
 
71
        // Clock provides the AfterFunc function used to call func.
 
72
        // It is exposed here so it's easier to mock it in tests.
 
73
        Clock clock.Clock
 
74
}
 
75
 
 
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() {
 
80
        if t.timer != nil {
 
81
                t.timer.Stop()
 
82
        }
 
83
        t.timer = t.config.Clock.AfterFunc(t.currentDuration, t.config.Func)
 
84
 
 
85
        // Since it's a backoff timer we will increase
 
86
        // the duration after each signal.
 
87
        t.increaseDuration()
 
88
}
 
89
 
 
90
// Reset implements the Timer interface.
 
91
func (t *BackoffTimer) Reset() {
 
92
        if t.timer != nil {
 
93
                t.timer.Stop()
 
94
        }
 
95
        if t.currentDuration > t.config.Min {
 
96
                t.currentDuration = t.config.Min
 
97
        }
 
98
}
 
99
 
 
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)
 
106
        if t.config.Jitter {
 
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)
 
111
        }
 
112
        if nextDuration > t.config.Max {
 
113
                nextDuration = t.config.Max
 
114
        }
 
115
        t.currentDuration = nextDuration
 
116
}