Skip to content

Commit c3e5539

Browse files
authored
Merge pull request #10 from kpp/clear_stat
Add methods to clear stats
2 parents 99ae915 + 2fc48f1 commit c3e5539

File tree

5 files changed

+112
-10
lines changed

5 files changed

+112
-10
lines changed

meter.go

+15-2
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,19 @@ func (s Snapshot) String() string {
3333
type Meter struct {
3434
accumulator uint64
3535

36+
// managed by the sweeper loop.
37+
registered bool
38+
3639
// Take lock.
3740
snapshot Snapshot
3841
}
3942

4043
// Mark updates the total.
4144
func (m *Meter) Mark(count uint64) {
4245
if count > 0 && atomic.AddUint64(&m.accumulator, count) == count {
43-
// I'm the first one to bump this above 0.
44-
// Register it.
46+
// The accumulator is 0 so we probably need to register. We may
47+
// already _be_ registered however, if we are, the registration
48+
// loop will notice that `m.registered` is set and ignore us.
4549
globalSweeper.Register(m)
4650
}
4751
}
@@ -53,6 +57,15 @@ func (m *Meter) Snapshot() Snapshot {
5357
return m.snapshot
5458
}
5559

60+
// Reset sets accumulator, total and rate to zero.
61+
func (m *Meter) Reset() {
62+
globalSweeper.snapshotMu.Lock()
63+
atomic.StoreUint64(&m.accumulator, 0)
64+
m.snapshot.Rate = 0
65+
m.snapshot.Total = 0
66+
globalSweeper.snapshotMu.Unlock()
67+
}
68+
5669
func (m *Meter) String() string {
5770
return m.Snapshot().String()
5871
}

meter_test.go

+39
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package flow
33
import (
44
"fmt"
55
"math"
6+
"sync"
7+
"testing"
68
"time"
79
)
810

@@ -29,6 +31,43 @@ func ExampleMeter() {
2931
// Output: 3000 (300/s)
3032
}
3133

34+
func TestResetMeter(t *testing.T) {
35+
meter := new(Meter)
36+
37+
meter.Mark(30)
38+
39+
time.Sleep(2 * time.Second)
40+
41+
if total := meter.Snapshot().Total; total != 30 {
42+
t.Errorf("total = %d; want 30", total)
43+
}
44+
45+
meter.Reset()
46+
47+
if total := meter.Snapshot().Total; total != 0 {
48+
t.Errorf("total = %d; want 0", total)
49+
}
50+
}
51+
52+
func TestMarkResetMeterMulti(t *testing.T) {
53+
var wg sync.WaitGroup
54+
wg.Add(2)
55+
56+
meter := new(Meter)
57+
go func(meter *Meter) {
58+
meter.Mark(30)
59+
meter.Mark(30)
60+
wg.Done()
61+
}(meter)
62+
63+
go func(meter *Meter) {
64+
meter.Reset()
65+
wg.Done()
66+
}(meter)
67+
68+
wg.Wait()
69+
}
70+
3271
func roundTens(x float64) int64 {
3372
return int64(math.Floor(x/10+0.5)) * 10
3473
}

registry.go

+8
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,11 @@ func (r *MeterRegistry) ForEach(iterFunc func(string, *Meter)) {
7272
return true
7373
})
7474
}
75+
76+
// Clear removes all meters from the registry.
77+
func (r *MeterRegistry) Clear() {
78+
r.meters.Range(func(k, v interface{}) bool {
79+
r.meters.Delete(k)
80+
return true
81+
})
82+
}

registry_test.go

+25-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ func TestRegistry(t *testing.T) {
1515
m1.Mark(10)
1616
m2.Mark(30)
1717

18-
time.Sleep(2 * time.Second)
18+
time.Sleep(2*time.Second + time.Millisecond)
1919

2020
if total := r.Get("first").Snapshot().Total; total != 10 {
2121
t.Errorf("expected first total to be 10, got %d", total)
@@ -98,3 +98,27 @@ func TestRegistry(t *testing.T) {
9898
t.Error("expected to trim 2 idle timers")
9999
}
100100
}
101+
102+
func TestClearRegistry(t *testing.T) {
103+
r := new(MeterRegistry)
104+
m1 := r.Get("first")
105+
m2 := r.Get("second")
106+
107+
m1.Mark(10)
108+
m2.Mark(30)
109+
110+
time.Sleep(2 * time.Second)
111+
112+
r.Clear()
113+
114+
r.ForEach(func(n string, _m *Meter) {
115+
t.Errorf("expected no meters at all, found a meter %s", n)
116+
})
117+
118+
if total := r.Get("first").Snapshot().Total; total != 0 {
119+
t.Errorf("expected first total to be 0, got %d", total)
120+
}
121+
if total := r.Get("second").Snapshot().Total; total != 0 {
122+
t.Errorf("expected second total to be 0, got %d", total)
123+
}
124+
}

sweeper.go

+25-7
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ var globalSweeper sweeper
2323
type sweeper struct {
2424
sweepOnce sync.Once
2525

26-
snapshotMu sync.RWMutex
27-
meters []*Meter
26+
snapshotMu sync.RWMutex
27+
meters []*Meter
28+
activeMeters int
2829

2930
lastUpdateTime time.Time
3031
registerChannel chan *Meter
@@ -43,9 +44,11 @@ func (sw *sweeper) run() {
4344
}
4445

4546
func (sw *sweeper) register(m *Meter) {
46-
// Add back the snapshot total. If we unregistered this
47-
// one, we set it to zero.
48-
atomic.AddUint64(&m.accumulator, m.snapshot.Total)
47+
if m.registered {
48+
// registered twice, move on.
49+
return
50+
}
51+
m.registered = true
4952
sw.meters = append(sw.meters, m)
5053
}
5154

@@ -85,9 +88,9 @@ func (sw *sweeper) update() {
8588
sw.lastUpdateTime = now
8689
timeMultiplier := float64(time.Second) / float64(tdiff)
8790

91+
// Calculate the bandwidth for all active meters.
8892
newLen := len(sw.meters)
89-
90-
for i, m := range sw.meters {
93+
for i, m := range sw.meters[:sw.activeMeters] {
9194
total := atomic.LoadUint64(&m.accumulator)
9295
diff := total - m.snapshot.Total
9396
instant := timeMultiplier * float64(diff)
@@ -142,16 +145,31 @@ func (sw *sweeper) update() {
142145
}
143146

144147
// Reset the rate, keep the total.
148+
m.registered = false
145149
m.snapshot.Rate = 0
146150
newLen--
147151
sw.meters[i] = sw.meters[newLen]
148152
}
149153

154+
// Re-add the total to all the newly active accumulators and set the snapshot to the total.
155+
// 1. We don't do this on register to avoid having to take the snapshot lock.
156+
// 2. We skip calculating the bandwidth for this round so we get an _accurate_ bandwidth calculation.
157+
for _, m := range sw.meters[sw.activeMeters:] {
158+
total := atomic.AddUint64(&m.accumulator, m.snapshot.Total)
159+
if total > m.snapshot.Total {
160+
m.snapshot.LastUpdate = now
161+
}
162+
m.snapshot.Total = total
163+
}
164+
150165
// trim the meter list
151166
for i := newLen; i < len(sw.meters); i++ {
152167
sw.meters[i] = nil
153168
}
154169
sw.meters = sw.meters[:newLen]
170+
171+
// Finally, mark all meters still in the list as "active".
172+
sw.activeMeters = len(sw.meters)
155173
}
156174

157175
func (sw *sweeper) Register(m *Meter) {

0 commit comments

Comments
 (0)