From 6ef3c5bdcc65f8d51f578cff7b4c4f17ea9f2c9b Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Wed, 27 Nov 2024 04:52:28 +0100 Subject: [PATCH] metrics: make meter ticker respect enabled-flag --- metrics/meter.go | 60 +++++++++++++++++++++++++------------------ metrics/meter_test.go | 10 ++------ 2 files changed, 37 insertions(+), 33 deletions(-) diff --git a/metrics/meter.go b/metrics/meter.go index e1a96d4976..de79cbf079 100644 --- a/metrics/meter.go +++ b/metrics/meter.go @@ -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. func NewMeter() *Meter { m := newMeter() - arbiter.Lock() - defer arbiter.Unlock() - arbiter.meters[m] = struct{}{} - if !arbiter.started { - arbiter.started = true - go arbiter.tick() - } + arbiter.add(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. func (m *Meter) Stop() { if stopped := m.stopped.Swap(true); !stopped { - arbiter.Lock() - delete(arbiter.meters, m) - arbiter.Unlock() + arbiter.remove(m) } } @@ -132,28 +124,46 @@ func (m *Meter) 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. -type meterArbiter struct { - sync.RWMutex +type meterTicker struct { + mu sync.RWMutex + started bool meters map[*Meter]struct{} - ticker *time.Ticker } -var arbiter = meterArbiter{ticker: time.NewTicker(5 * time.Second), meters: make(map[*Meter]struct{})} - -// tick meters on the scheduled interval -func (ma *meterArbiter) tick() { - for range ma.ticker.C { - ma.tickMeters() +// add adds another *Meter ot the arbiter, and starts the arbiter ticker. +func (ma *meterTicker) add(m *Meter) { + ma.mu.Lock() + defer ma.mu.Unlock() + ma.meters[m] = struct{}{} + if !ma.started { + ma.started = true + go ma.loop() } } -func (ma *meterArbiter) tickMeters() { - ma.RLock() - defer ma.RUnlock() - for meter := range ma.meters { - meter.tick() +// remove removes a meter from the set of ticked meters. +func (ma *meterTicker) remove(m *Meter) { + ma.mu.Lock() + delete(ma.meters, m) + 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() } } diff --git a/metrics/meter_test.go b/metrics/meter_test.go index 74910e3d6b..e3f39684bd 100644 --- a/metrics/meter_test.go +++ b/metrics/meter_test.go @@ -28,18 +28,12 @@ func TestGetOrRegisterMeter(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() - ma.meters[m] = struct{}{} m.Mark(1) - ma.tickMeters() + m.tick() rateMean := m.Snapshot().RateMean() time.Sleep(100 * time.Millisecond) - ma.tickMeters() + m.tick() if m.Snapshot().RateMean() >= rateMean { t.Error("m.RateMean() didn't decrease") }