This repository has been archived by the owner on Aug 10, 2021. It is now read-only.
/
backoff.go
88 lines (72 loc) · 1.74 KB
/
backoff.go
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
// Package utils contains common shared code.
package utils
import (
"fmt"
"math"
"sync"
"time"
)
// Backoff holds the number of attempts as well as the min and max backoff delays.
type Backoff struct {
sync.RWMutex
attempt, Factor int
waiting bool
Min, Max time.Duration
Timer *time.Timer
}
// Reset clears the number of attempts once the API call has succeeded.
func (b *Backoff) Reset() {
b.Lock()
defer b.Unlock()
if b.attempt > 0 {
log.Debugf("Backoff succeeded after %d attempts", b.attempt)
}
if b.waiting {
log.Errorf("Backoff waiting flag is unexpectedly set")
}
b.attempt = 0
}
// Waiting flag is true while waiting for the backoff duration. Prevents
// any scaling actions.
func (b *Backoff) Waiting() bool {
b.RLock()
defer b.RUnlock()
return b.waiting
}
// Backoff calculates the duration and sets an appropriate timer. When it pops it will send on the channel.
func (b *Backoff) Backoff(c chan struct{}) error {
b.Lock()
defer b.Unlock()
if b.waiting {
return fmt.Errorf("Already backing off")
}
multiplier := math.Pow(float64(b.Factor), float64(b.attempt))
duration := time.Duration(float64(b.Min) * multiplier)
log.Debugf("Backing off for %s", duration)
// Check whether we've reached the max backoff duration
if duration > b.Max {
return fmt.Errorf("Exceeded max backoff attempts")
}
b.waiting = true
b.attempt++
b.Timer = time.NewTimer(duration)
go func() {
<-b.Timer.C
log.Debug("Backff expired")
b.Lock()
defer b.Unlock()
b.waiting = false
c <- struct{}{}
}()
return nil
}
// Stop a backoff timer and mark that we aren't waiting for it any more
func (b *Backoff) Stop() {
b.Lock()
defer b.Unlock()
if b.waiting {
b.Timer.Stop()
b.waiting = false
}
return
}