forked from facebookarchive/flashback
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstats.go
154 lines (129 loc) · 4.19 KB
/
stats.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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package flashback
import (
"math/rand"
"time"
)
// Latency of the mongo ops
type Latency struct {
OpType OpType
Latency time.Duration
}
type IStatsCollector interface {
StartOp(opType OpType)
EndOp()
// How many ops have been captured.
Count(opType OpType) int64
// ops/sec for a given op type.
OpsSec(opType OpType) float64
// The average latency, which can give you a rough idea of the performance.
// For fine-grain performance analysis, please enable latency sampling
// and do the latency analysis by other means.
LatencyInMs(opType OpType) float64
// Enable the sampling for latency analysis. Sampled latencies will be sent
// out via a channel.
SampleLatencies(sampleRate float64, latencyChannel chan Latency)
}
type StatsCollector struct {
counts map[OpType]int64
durations map[OpType]time.Duration
total int
// sample rate will be among [0.0-1.0]
sampleRate float64
epoch *time.Time
lastOp *OpType
latencyChan chan Latency
}
func NewStatsCollector() *StatsCollector {
counts := map[OpType]int64{}
durations := map[OpType]time.Duration{}
for _, opType := range AllOpTypes {
counts[opType] = 0
durations[opType] = 0
}
collector := &StatsCollector{
counts: counts,
durations: durations,
sampleRate: 1,
}
return collector
}
func (s *StatsCollector) StartOp(opType OpType) {
s.total++
// should track count of opTypes even if they're not sampled
s.counts[opType]++
if s.sampleRate == 0 {
return
}
if s.sampleRate == 1.0 || rand.Float64() < s.sampleRate {
now := time.Now()
s.epoch = &now
s.lastOp = &opType
}
}
func (s *StatsCollector) EndOp() {
// This particular op is not sampled
if s.epoch == nil {
return
}
duration := time.Now().Sub(*s.epoch)
s.durations[*s.lastOp] += duration
// s.counts[*s.lastOp]++
if s.latencyChan != nil {
s.latencyChan <- Latency{*s.lastOp, duration}
}
s.epoch = nil
s.lastOp = nil
}
func (s *StatsCollector) Count(opType OpType) int64 {
return s.counts[opType]
}
func (s *StatsCollector) TotalTime(opType OpType) time.Duration {
return s.durations[opType]
}
func (s *StatsCollector) OpsSec(opType OpType) float64 {
// TODO: This seems like an unusual way to calculate ops/sec. TotalTime returns the total duration spent doing opType
// but really we should be dividing total ops / total wall clock time
// this may explain why ops/sec per-op is much higher than total ops/sec
nano := s.TotalTime(opType).Nanoseconds()
if nano == 0 {
return 0
}
return float64(s.counts[opType]) * float64(time.Second) / float64(nano)
}
func (s *StatsCollector) LatencyInMs(opType OpType) float64 {
count := float64(s.counts[opType])
if count == 0 {
return 0
}
sec := s.TotalTime(opType).Seconds()
return sec / count * 1000
}
func (s *StatsCollector) SampleLatencies(sampleRate float64, latencyChannel chan Latency) {
s.sampleRate = sampleRate
s.latencyChan = latencyChannel
}
// Combine the stats collected by multiple stats to one.
func CombineStats(statsList ...*StatsCollector) *StatsCollector {
newStats := NewStatsCollector()
for _, opType := range AllOpTypes {
for _, stats := range statsList {
newStats.counts[opType] += stats.counts[opType]
newStats.durations[opType] += stats.durations[opType]
newStats.total += stats.total
}
}
return newStats
}
// NullStatsCollector is a placeholder that does nothing.
type nullStatsCollector struct{}
func (e *nullStatsCollector) StartOp(opType OpType) {}
func (e *nullStatsCollector) EndOp() {}
func (e *nullStatsCollector) SampleLatencies(sampleRate float64, latencyChannel chan Latency) {}
func (e *nullStatsCollector) Count(opType OpType) int64 { return 0 }
func (e *nullStatsCollector) TotalTime(opType OpType) time.Duration { return 0 }
func (e *nullStatsCollector) OpsSec(opType OpType) float64 { return 0 }
func (e *nullStatsCollector) LatencyInMs(opType OpType) float64 { return 0 }
// NewNullStatsCollector makes a dumb stats collector that does nothing.
func NewNullStatsCollector() IStatsCollector {
return &nullStatsCollector{}
}