-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathonlyonce.go
135 lines (115 loc) · 3.5 KB
/
onlyonce.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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package dynsampler
import (
"fmt"
"sync"
"time"
)
// OnlyOnce implements Sampler and returns a sample rate of 1 the first time a
// key is seen and 1,000,000,000 every subsequent time. Essentially, this means
// that every key will be reported the first time it's seen during each
// ClearFrequencySec and never again. Set ClearFrequencySec to a negative
// number to report each key only once for the life of the process.
//
// (Note that it's not guaranteed that each key will be reported exactly once,
// just that the first seen event will be reported and subsequent events are
// unlikely to be reported. It is probable that an additional event will be
// reported for every billion times the key appears.)
//
// This emulates what you might expect from something catching stack traces -
// the first one is important but every subsequent one just repeats the same
// information.
type OnlyOnce struct {
// DEPRECATED -- use ClearFrequencyDuration.
// ClearFrequencySec is how often the counters reset in seconds.
ClearFrequencySec int
// ClearFrequencyDuration is how often the counters reset as a Duration.
// Note that either this or ClearFrequencySec can be specified, but not both.
// If neither one is set, the default is 30s.
ClearFrequencyDuration time.Duration
seen map[string]bool
done chan struct{}
// metrics
requestCount int64
eventCount int64
lock sync.Mutex
}
// Ensure we implement the sampler interface
var _ Sampler = (*OnlyOnce)(nil)
// Start initializes the static dynsampler
func (o *OnlyOnce) Start() error {
if o.ClearFrequencyDuration != 0 && o.ClearFrequencySec != 0 {
return fmt.Errorf("the ClearFrequencySec configuration value is deprecated; use only ClearFrequencyDuration")
}
if o.ClearFrequencyDuration == 0 && o.ClearFrequencySec == 0 {
o.ClearFrequencyDuration = 30 * time.Second
} else if o.ClearFrequencySec != 0 {
o.ClearFrequencyDuration = time.Duration(o.ClearFrequencySec) * time.Second
}
// if it's negative, we don't even start something
if o.ClearFrequencyDuration < 0 {
return nil
}
o.seen = make(map[string]bool)
o.done = make(chan struct{})
// spin up calculator
go func() {
ticker := time.NewTicker(o.ClearFrequencyDuration)
defer ticker.Stop()
for {
select {
case <-ticker.C:
o.updateMaps()
case <-o.done:
return
}
}
}()
return nil
}
func (o *OnlyOnce) Stop() error {
if o.done != nil {
close(o.done)
}
return nil
}
func (o *OnlyOnce) updateMaps() {
o.lock.Lock()
defer o.lock.Unlock()
o.seen = make(map[string]bool)
}
// GetSampleRate takes a key and returns the appropriate sample rate for that
// key.
func (o *OnlyOnce) GetSampleRate(key string) int {
return o.GetSampleRateMulti(key, 1)
}
// GetSampleRateMulti takes a key representing count spans and returns the
// appropriate sample rate for that key.
func (o *OnlyOnce) GetSampleRateMulti(key string, count int) int {
o.lock.Lock()
defer o.lock.Unlock()
o.requestCount++
o.eventCount += int64(count)
if _, found := o.seen[key]; found {
return 1000000000
}
o.seen[key] = true
return 1
}
// SaveState is not implemented
func (o *OnlyOnce) SaveState() ([]byte, error) {
return nil, nil
}
// LoadState is not implemented
func (o *OnlyOnce) LoadState(state []byte) error {
return nil
}
func (o *OnlyOnce) GetMetrics(prefix string) map[string]int64 {
o.lock.Lock()
defer o.lock.Unlock()
mets := map[string]int64{
prefix + "request_count": o.requestCount,
prefix + "event_count": o.eventCount,
prefix + "keyspace_size": int64(len(o.seen)),
}
return mets
}