Skip to content
This repository was archived by the owner on Apr 1, 2025. It is now read-only.

Commit 9180854

Browse files
committed
Use set of meters and update documentation
- use map[*StandardMeter]struct{} instead of map[int64]*StandardMeter to avoid id management pitfalls - update documentation and examples
1 parent ff4b9a8 commit 9180854

File tree

4 files changed

+32
-19
lines changed

4 files changed

+32
-19
lines changed

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,22 @@ t.Update(47)
4242
Register() is not threadsafe. For threadsafe metric registration use
4343
GetOrRegister:
4444

45-
```
45+
```go
4646
t := metrics.GetOrRegisterTimer("account.create.latency", nil)
4747
t.Time(func() {})
4848
t.Update(47)
4949
```
5050

51+
**NOTE:** Be sure to either unregister meters and timers otherwise and they will
52+
leak memory:
53+
54+
```go
55+
// Will call Stop() on the Meter to allow for garbage collection
56+
metrics.Unregister("quux")
57+
// Or similarly for a Timer that embeds a Meter
58+
metrics.Unregister("bang")
59+
```
60+
5161
Periodically log every metric in human-readable form to standard error:
5262

5363
```go

meter.go

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ type Meter interface {
2020

2121
// GetOrRegisterMeter returns an existing Meter or constructs and registers a
2222
// new StandardMeter.
23+
// Be sure to unregister the meter from the registry once it is of no use to
24+
// allow for garbage collection.
2325
func GetOrRegisterMeter(name string, r Registry) Meter {
2426
if nil == r {
2527
r = DefaultRegistry
@@ -28,15 +30,15 @@ func GetOrRegisterMeter(name string, r Registry) Meter {
2830
}
2931

3032
// NewMeter constructs a new StandardMeter and launches a goroutine.
33+
// Be sure to call Stop() once the meter is of no use to allow for garbage collection.
3134
func NewMeter() Meter {
3235
if UseNilMetrics {
3336
return NilMeter{}
3437
}
3538
m := newStandardMeter()
3639
arbiter.Lock()
3740
defer arbiter.Unlock()
38-
m.id = arbiter.newID()
39-
arbiter.meters[m.id] = m
41+
arbiter.meters[m] = struct{}{}
4042
if !arbiter.started {
4143
arbiter.started = true
4244
go arbiter.tick()
@@ -46,6 +48,8 @@ func NewMeter() Meter {
4648

4749
// NewMeter constructs and registers a new StandardMeter and launches a
4850
// goroutine.
51+
// Be sure to unregister the meter from the registry once it is of no use to
52+
// allow for garbage collection.
4953
func NewRegisteredMeter(name string, r Registry) Meter {
5054
c := NewMeter()
5155
if nil == r {
@@ -125,7 +129,6 @@ type StandardMeter struct {
125129
a1, a5, a15 EWMA
126130
startTime time.Time
127131
stopped bool
128-
id int64
129132
}
130133

131134
func newStandardMeter() *StandardMeter {
@@ -138,15 +141,15 @@ func newStandardMeter() *StandardMeter {
138141
}
139142
}
140143

141-
// Stop stops the meter, Mark() will panic if you use it after being stopped.
144+
// Stop stops the meter, Mark() will be a no-op if you use it after being stopped.
142145
func (m *StandardMeter) Stop() {
143146
m.lock.Lock()
144147
stopped := m.stopped
145148
m.stopped = true
146149
m.lock.Unlock()
147150
if !stopped {
148151
arbiter.Lock()
149-
delete(arbiter.meters, m.id)
152+
delete(arbiter.meters, m)
150153
arbiter.Unlock()
151154
}
152155
}
@@ -231,15 +234,16 @@ func (m *StandardMeter) tick() {
231234
m.updateSnapshot()
232235
}
233236

237+
// meterArbiter ticks meters every 5s from a single goroutine.
238+
// meters are references in a set for future stopping.
234239
type meterArbiter struct {
235240
sync.RWMutex
236241
started bool
237-
meters map[int64]*StandardMeter
242+
meters map[*StandardMeter]struct{}
238243
ticker *time.Ticker
239-
id int64
240244
}
241245

242-
var arbiter = meterArbiter{ticker: time.NewTicker(5e9), meters: make(map[int64]*StandardMeter)}
246+
var arbiter = meterArbiter{ticker: time.NewTicker(5e9), meters: make(map[*StandardMeter]struct{})}
243247

244248
// Ticks meters on the scheduled interval
245249
func (ma *meterArbiter) tick() {
@@ -251,16 +255,10 @@ func (ma *meterArbiter) tick() {
251255
}
252256
}
253257

254-
// should only be called with Lock() held
255-
func (ma *meterArbiter) newID() int64 {
256-
ma.id++
257-
return ma.id
258-
}
259-
260258
func (ma *meterArbiter) tickMeters() {
261259
ma.RLock()
262260
defer ma.RUnlock()
263-
for _, meter := range ma.meters {
261+
for meter := range ma.meters {
264262
meter.tick()
265263
}
266264
}

meter_test.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,10 @@ func TestGetOrRegisterMeter(t *testing.T) {
2424
func TestMeterDecay(t *testing.T) {
2525
ma := meterArbiter{
2626
ticker: time.NewTicker(time.Millisecond),
27-
meters: make(map[int64]*StandardMeter),
27+
meters: make(map[*StandardMeter]struct{}),
2828
}
2929
m := newStandardMeter()
30-
m.id = ma.newID()
31-
ma.meters[m.id] = m
30+
ma.meters[m] = struct{}{}
3231
go ma.tick()
3332
m.Mark(1)
3433
rateMean := m.RateMean()

timer.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ type Timer interface {
2929

3030
// GetOrRegisterTimer returns an existing Timer or constructs and registers a
3131
// new StandardTimer.
32+
// Be sure to unregister the meter from the registry once it is of no use to
33+
// allow for garbage collection.
3234
func GetOrRegisterTimer(name string, r Registry) Timer {
3335
if nil == r {
3436
r = DefaultRegistry
@@ -37,6 +39,7 @@ func GetOrRegisterTimer(name string, r Registry) Timer {
3739
}
3840

3941
// NewCustomTimer constructs a new StandardTimer from a Histogram and a Meter.
42+
// Be sure to call Stop() once the timer is of no use to allow for garbage collection.
4043
func NewCustomTimer(h Histogram, m Meter) Timer {
4144
if UseNilMetrics {
4245
return NilTimer{}
@@ -48,6 +51,8 @@ func NewCustomTimer(h Histogram, m Meter) Timer {
4851
}
4952

5053
// NewRegisteredTimer constructs and registers a new StandardTimer.
54+
// Be sure to unregister the meter from the registry once it is of no use to
55+
// allow for garbage collection.
5156
func NewRegisteredTimer(name string, r Registry) Timer {
5257
c := NewTimer()
5358
if nil == r {
@@ -59,6 +64,7 @@ func NewRegisteredTimer(name string, r Registry) Timer {
5964

6065
// NewTimer constructs a new StandardTimer using an exponentially-decaying
6166
// sample with the same reservoir size and alpha as UNIX load averages.
67+
// Be sure to call Stop() once the timer is of no use to allow for garbage collection.
6268
func NewTimer() Timer {
6369
if UseNilMetrics {
6470
return NilTimer{}

0 commit comments

Comments
 (0)