-
Notifications
You must be signed in to change notification settings - Fork 0
/
faultgauge.go
116 lines (99 loc) · 2.67 KB
/
faultgauge.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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package faultgauge
import (
"sync"
"time"
)
// FailRate returns the current fail rate and the previous fail rate.
type FailRate interface {
FailRate() (float32, float32)
}
// Controller controls the gauge by incrementing the success or fail count.
type Controller interface {
IncrementFail()
IncrementSuccess()
// Stats for current and previous windows respectively.
NumFail() (uint64, uint64)
NumSuccess() (uint64, uint64)
Counter() (uint64, uint64)
}
type FaultGauge struct {
windowLength time.Duration
currWindow time.Time
prevWindow time.Time
currFailCount uint64
prevFailCount uint64
currSuccessCount uint64
prevSuccessCount uint64
currCounter uint64
prevCounter uint64
mu sync.RWMutex
}
// NewFaultGauge creates a new gauge which samples the fail rate over the
// window length. Be sure to call IncrementFail() or IncrementSuccess() to
// inform the gauge of the success or failure. A windowLength of 10 seconds
// or more is recommended.
func NewFaultGauge(windowLength time.Duration) *FaultGauge {
return &FaultGauge{
windowLength: windowLength,
}
}
var _ FailRate = (*FaultGauge)(nil)
var _ Controller = (*FaultGauge)(nil)
func (f *FaultGauge) IncrementFail() {
f.mu.Lock()
f.sample(true)
f.mu.Unlock()
}
func (f *FaultGauge) IncrementSuccess() {
f.mu.Lock()
f.sample(false)
f.mu.Unlock()
}
func (f *FaultGauge) FailRate() (float32, float32) {
f.mu.RLock()
currFailRate := float32(f.currFailCount) / float32(f.currSuccessCount+f.currFailCount)
prevFailRate := float32(f.prevFailCount) / float32(f.prevSuccessCount+f.prevFailCount)
f.mu.RUnlock()
return currFailRate, prevFailRate
}
func (f *FaultGauge) NumFail() (uint64, uint64) {
f.mu.RLock()
currFailCount := f.currFailCount
prevFailCount := f.prevFailCount
f.mu.RUnlock()
return currFailCount, prevFailCount
}
func (f *FaultGauge) NumSuccess() (uint64, uint64) {
f.mu.RLock()
currSuccessCount := f.currSuccessCount
prevSuccessCount := f.prevSuccessCount
f.mu.RUnlock()
return currSuccessCount, prevSuccessCount
}
func (f *FaultGauge) Counter() (uint64, uint64) {
f.mu.RLock()
currCounter := f.currCounter
prevCounter := f.prevCounter
f.mu.RUnlock()
return currCounter, prevCounter
}
func (f *FaultGauge) sample(fail bool) {
now := time.Now().UTC()
currWindow := now.Truncate(f.windowLength)
if f.currWindow != currWindow {
f.prevWindow = f.currWindow
f.currWindow = currWindow
f.prevFailCount = f.currFailCount
f.currFailCount = 0
f.prevSuccessCount = f.currSuccessCount
f.currSuccessCount = 0
f.prevCounter = f.currCounter
f.currCounter = 0
}
if fail {
f.currFailCount += 1
} else {
f.currSuccessCount += 1
}
f.currCounter += 1
}