metrics: make meter ticker respect enabled-flag

This commit is contained in:
Martin Holst Swende 2024-11-27 04:52:28 +01:00
parent d803afd7b8
commit 6ef3c5bdcc
No known key found for this signature in database
GPG key ID: 683B438C05A5DDF0
2 changed files with 37 additions and 33 deletions

View file

@ -22,13 +22,7 @@ func GetOrRegisterMeter(name string, r Registry) *Meter {
// Be sure to call Stop() once the meter is of no use to allow for garbage collection. // Be sure to call Stop() once the meter is of no use to allow for garbage collection.
func NewMeter() *Meter { func NewMeter() *Meter {
m := newMeter() m := newMeter()
arbiter.Lock() arbiter.add(m)
defer arbiter.Unlock()
arbiter.meters[m] = struct{}{}
if !arbiter.started {
arbiter.started = true
go arbiter.tick()
}
return m return m
} }
@ -95,9 +89,7 @@ func newMeter() *Meter {
// Stop stops the meter, Mark() will be a no-op if you use it after being stopped. // Stop stops the meter, Mark() will be a no-op if you use it after being stopped.
func (m *Meter) Stop() { func (m *Meter) Stop() {
if stopped := m.stopped.Swap(true); !stopped { if stopped := m.stopped.Swap(true); !stopped {
arbiter.Lock() arbiter.remove(m)
delete(arbiter.meters, m)
arbiter.Unlock()
} }
} }
@ -132,28 +124,46 @@ func (m *Meter) tick() {
m.a15.Tick() m.a15.Tick()
} }
// meterArbiter ticks meters every 5s from a single goroutine. var arbiter = meterTicker{meters: make(map[*Meter]struct{})}
// meterTicker ticks meters every 5s from a single goroutine.
// meters are references in a set for future stopping. // meters are references in a set for future stopping.
type meterArbiter struct { type meterTicker struct {
sync.RWMutex mu sync.RWMutex
started bool started bool
meters map[*Meter]struct{} meters map[*Meter]struct{}
ticker *time.Ticker
} }
var arbiter = meterArbiter{ticker: time.NewTicker(5 * time.Second), meters: make(map[*Meter]struct{})} // add adds another *Meter ot the arbiter, and starts the arbiter ticker.
func (ma *meterTicker) add(m *Meter) {
// tick meters on the scheduled interval ma.mu.Lock()
func (ma *meterArbiter) tick() { defer ma.mu.Unlock()
for range ma.ticker.C { ma.meters[m] = struct{}{}
ma.tickMeters() if !ma.started {
ma.started = true
go ma.loop()
} }
} }
func (ma *meterArbiter) tickMeters() { // remove removes a meter from the set of ticked meters.
ma.RLock() func (ma *meterTicker) remove(m *Meter) {
defer ma.RUnlock() ma.mu.Lock()
for meter := range ma.meters { delete(ma.meters, m)
meter.tick() ma.mu.Unlock()
}
// loop ticks meters on a 5 second interval.
func (ma *meterTicker) loop() {
ticker := time.NewTicker(5 * time.Second)
for range ticker.C {
if !metricsEnabled {
continue
}
ma.mu.RLock()
for meter := range ma.meters {
meter.tick()
}
ma.mu.RUnlock()
} }
} }

View file

@ -28,18 +28,12 @@ func TestGetOrRegisterMeter(t *testing.T) {
} }
func TestMeterDecay(t *testing.T) { func TestMeterDecay(t *testing.T) {
ma := meterArbiter{
ticker: time.NewTicker(time.Millisecond),
meters: make(map[*Meter]struct{}),
}
defer ma.ticker.Stop()
m := newMeter() m := newMeter()
ma.meters[m] = struct{}{}
m.Mark(1) m.Mark(1)
ma.tickMeters() m.tick()
rateMean := m.Snapshot().RateMean() rateMean := m.Snapshot().RateMean()
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
ma.tickMeters() m.tick()
if m.Snapshot().RateMean() >= rateMean { if m.Snapshot().RateMean() >= rateMean {
t.Error("m.RateMean() didn't decrease") t.Error("m.RateMean() didn't decrease")
} }