From a6038c67e7cb5c0fd68685f9c823f14364e5a815 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Wed, 27 Nov 2024 02:50:56 +0100 Subject: [PATCH] metrics: fix timer, remove interfaces and nil-implementations --- eth/downloader/queue.go | 2 +- metrics/debug.go | 2 +- metrics/exp/exp.go | 4 +- metrics/graphite.go | 2 +- metrics/inactive.go | 1 - metrics/influxdb/influxdb.go | 2 +- metrics/log.go | 2 +- metrics/opentsdb.go | 2 +- metrics/prometheus/collector.go | 4 +- metrics/registry.go | 4 +- metrics/syslog.go | 2 +- metrics/timer.go | 104 +++++++++++--------------------- metrics/writer.go | 2 +- 13 files changed, 49 insertions(+), 84 deletions(-) diff --git a/eth/downloader/queue.go b/eth/downloader/queue.go index 617fa5a3b8..a07a529a71 100644 --- a/eth/downloader/queue.go +++ b/eth/downloader/queue.go @@ -881,7 +881,7 @@ func (q *queue) DeliverReceipts(id string, receiptList [][]*types.Receipt, recei // to access the queue, so they already need a lock anyway. func (q *queue) deliver(id string, taskPool map[common.Hash]*types.Header, taskQueue *prque.Prque[int64, *types.Header], pendPool map[string]*fetchRequest, - reqTimer metrics.Timer, resInMeter, resDropMeter *metrics.Meter, + reqTimer *metrics.Timer, resInMeter, resDropMeter *metrics.Meter, results int, validate func(index int, header *types.Header) error, reconstruct func(index int, result *fetchResult)) (int, error) { // Short circuit if the data was never requested diff --git a/metrics/debug.go b/metrics/debug.go index c21bb70350..5d0d3992f1 100644 --- a/metrics/debug.go +++ b/metrics/debug.go @@ -14,7 +14,7 @@ var ( //PauseQuantiles Histogram PauseTotal *Gauge } - ReadGCStats Timer + ReadGCStats *Timer } gcStats debug.GCStats ) diff --git a/metrics/exp/exp.go b/metrics/exp/exp.go index 80c0705bb4..11cabb9b2e 100644 --- a/metrics/exp/exp.go +++ b/metrics/exp/exp.go @@ -155,7 +155,7 @@ func (exp *exp) publishMeter(name string, metric *metrics.Meter) { exp.getFloat(name + ".mean").Set(m.RateMean()) } -func (exp *exp) publishTimer(name string, metric metrics.Timer) { +func (exp *exp) publishTimer(name string, metric *metrics.Timer) { t := metric.Snapshot() ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) exp.getInt(name + ".count").Set(t.Count()) @@ -202,7 +202,7 @@ func (exp *exp) syncToExpvar() { exp.publishHistogram(name, i) case *metrics.Meter: exp.publishMeter(name, i) - case metrics.Timer: + case *metrics.Timer: exp.publishTimer(name, i) case metrics.ResettingTimer: exp.publishResettingTimer(name, i) diff --git a/metrics/graphite.go b/metrics/graphite.go index 483ce717b8..bec51621cf 100644 --- a/metrics/graphite.go +++ b/metrics/graphite.go @@ -94,7 +94,7 @@ func graphite(c *GraphiteConfig) error { fmt.Fprintf(w, "%s.%s.five-minute %.2f %d\n", c.Prefix, name, m.Rate5(), now) fmt.Fprintf(w, "%s.%s.fifteen-minute %.2f %d\n", c.Prefix, name, m.Rate15(), now) fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, m.RateMean(), now) - case Timer: + case *Timer: t := metric.Snapshot() ps := t.Percentiles(c.Percentiles) fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, t.Count(), now) diff --git a/metrics/inactive.go b/metrics/inactive.go index 1d35254e30..a2c2603375 100644 --- a/metrics/inactive.go +++ b/metrics/inactive.go @@ -19,7 +19,6 @@ package metrics // compile-time checks that interfaces are implemented. var ( _ HistogramSnapshot = (*emptySnapshot)(nil) - _ TimerSnapshot = (*emptySnapshot)(nil) ) type emptySnapshot struct{} diff --git a/metrics/influxdb/influxdb.go b/metrics/influxdb/influxdb.go index 693aea9558..86de929d64 100644 --- a/metrics/influxdb/influxdb.go +++ b/metrics/influxdb/influxdb.go @@ -73,7 +73,7 @@ func readMeter(namespace, name string, i interface{}) (string, map[string]interf "mean": ms.RateMean(), } return measurement, fields - case metrics.Timer: + case *metrics.Timer: ms := metric.Snapshot() ps := ms.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999}) diff --git a/metrics/log.go b/metrics/log.go index 2a41c62069..3380bbf9c4 100644 --- a/metrics/log.go +++ b/metrics/log.go @@ -58,7 +58,7 @@ func LogScaled(r Registry, freq time.Duration, scale time.Duration, l Logger) { l.Printf(" 5-min rate: %12.2f\n", m.Rate5()) l.Printf(" 15-min rate: %12.2f\n", m.Rate15()) l.Printf(" mean rate: %12.2f\n", m.RateMean()) - case Timer: + case *Timer: t := metric.Snapshot() ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) l.Printf("timer %s\n", name) diff --git a/metrics/opentsdb.go b/metrics/opentsdb.go index 7b47df99c2..57af3d025e 100644 --- a/metrics/opentsdb.go +++ b/metrics/opentsdb.go @@ -94,7 +94,7 @@ func (c *OpenTSDBConfig) writeRegistry(w io.Writer, now int64, shortHostname str fmt.Fprintf(w, "put %s.%s.five-minute %d %.2f host=%s\n", c.Prefix, name, now, m.Rate5(), shortHostname) fmt.Fprintf(w, "put %s.%s.fifteen-minute %d %.2f host=%s\n", c.Prefix, name, now, m.Rate15(), shortHostname) fmt.Fprintf(w, "put %s.%s.mean %d %.2f host=%s\n", c.Prefix, name, now, m.RateMean(), shortHostname) - case Timer: + case *Timer: t := metric.Snapshot() ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, t.Count(), shortHostname) diff --git a/metrics/prometheus/collector.go b/metrics/prometheus/collector.go index 968c390370..4d19b216d4 100644 --- a/metrics/prometheus/collector.go +++ b/metrics/prometheus/collector.go @@ -65,7 +65,7 @@ func (c *collector) Add(name string, i any) error { c.addHistogram(name, m.Snapshot()) case *metrics.Meter: c.addMeter(name, m.Snapshot()) - case metrics.Timer: + case *metrics.Timer: c.addTimer(name, m.Snapshot()) case metrics.ResettingTimer: c.addResettingTimer(name, m.Snapshot()) @@ -110,7 +110,7 @@ func (c *collector) addMeter(name string, m *metrics.MeterSnapshot) { c.writeGaugeCounter(name, m.Count()) } -func (c *collector) addTimer(name string, m metrics.TimerSnapshot) { +func (c *collector) addTimer(name string, m *metrics.TimerSnapshot) { pv := []float64{0.5, 0.75, 0.95, 0.99, 0.999, 0.9999} ps := m.Percentiles(pv) c.writeSummaryCounter(name, m.Count()) diff --git a/metrics/registry.go b/metrics/registry.go index 564c141bef..89d3511478 100644 --- a/metrics/registry.go +++ b/metrics/registry.go @@ -183,7 +183,7 @@ func (r *StandardRegistry) GetAll() map[string]map[string]interface{} { values["5m.rate"] = m.Rate5() values["15m.rate"] = m.Rate15() values["mean.rate"] = m.RateMean() - case Timer: + case *Timer: t := metric.Snapshot() ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) values["count"] = t.Count() @@ -214,7 +214,7 @@ func (r *StandardRegistry) Unregister(name string) { func (r *StandardRegistry) loadOrRegister(name string, i interface{}) (interface{}, bool, bool) { switch i.(type) { - case *Counter, *CounterFloat64, *Gauge, *GaugeFloat64, *GaugeInfo, *Healthcheck, Histogram, *Meter, Timer, ResettingTimer: + case *Counter, *CounterFloat64, *Gauge, *GaugeFloat64, *GaugeInfo, *Healthcheck, Histogram, *Meter, *Timer, ResettingTimer: default: return nil, false, false } diff --git a/metrics/syslog.go b/metrics/syslog.go index b6f041ffea..0bc4ed0da5 100644 --- a/metrics/syslog.go +++ b/metrics/syslog.go @@ -56,7 +56,7 @@ func Syslog(r Registry, d time.Duration, w *syslog.Writer) { m.Rate15(), m.RateMean(), )) - case Timer: + case *Timer: t := metric.Snapshot() ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) w.Info(fmt.Sprintf( diff --git a/metrics/timer.go b/metrics/timer.go index 6e8e6397d5..9df15c967a 100644 --- a/metrics/timer.go +++ b/metrics/timer.go @@ -5,51 +5,30 @@ import ( "time" ) -type TimerSnapshot interface { - HistogramSnapshot - Count() int64 - Rate1() float64 - Rate5() float64 - Rate15() float64 - RateMean() float64 -} - -// Timer capture the duration and rate of events. -type Timer interface { - Snapshot() TimerSnapshot - Stop() - Time(func()) - UpdateSince(time.Time) - Update(time.Duration) -} - // GetOrRegisterTimer returns an existing Timer or constructs and registers a -// new StandardTimer. +// new Timer. // Be sure to unregister the meter from the registry once it is of no use to // allow for garbage collection. -func GetOrRegisterTimer(name string, r Registry) Timer { +func GetOrRegisterTimer(name string, r Registry) *Timer { if nil == r { r = DefaultRegistry } - return r.GetOrRegister(name, NewTimer).(Timer) + return r.GetOrRegister(name, NewTimer).(*Timer) } -// NewCustomTimer constructs a new StandardTimer from a Histogram and a Meter. +// NewCustomTimer constructs a new Timer from a Histogram and a Meter. // Be sure to call Stop() once the timer is of no use to allow for garbage collection. -func NewCustomTimer(h Histogram, m *Meter) Timer { - if !Enabled { - return NilTimer{} - } - return &StandardTimer{ +func NewCustomTimer(h Histogram, m *Meter) *Timer { + return &Timer{ histogram: h, meter: m, } } -// NewRegisteredTimer constructs and registers a new StandardTimer. +// NewRegisteredTimer constructs and registers a new Timer. // Be sure to unregister the meter from the registry once it is of no use to // allow for garbage collection. -func NewRegisteredTimer(name string, r Registry) Timer { +func NewRegisteredTimer(name string, r Registry) *Timer { c := NewTimer() if nil == r { r = DefaultRegistry @@ -58,60 +37,47 @@ func NewRegisteredTimer(name string, r Registry) Timer { return c } -// NewTimer constructs a new StandardTimer using an exponentially-decaying +// NewTimer constructs a new Timer using an exponentially-decaying // sample with the same reservoir size and alpha as UNIX load averages. // Be sure to call Stop() once the timer is of no use to allow for garbage collection. -func NewTimer() Timer { - if !Enabled { - return NilTimer{} - } - return &StandardTimer{ +func NewTimer() *Timer { + return &Timer{ histogram: NewHistogram(NewExpDecaySample(1028, 0.015)), meter: NewMeter(), } } -// NilTimer is a no-op Timer. -type NilTimer struct{} - -func (NilTimer) Snapshot() TimerSnapshot { return (*emptySnapshot)(nil) } -func (NilTimer) Stop() {} -func (NilTimer) Time(f func()) { f() } -func (NilTimer) Update(time.Duration) {} -func (NilTimer) UpdateSince(time.Time) {} - -// StandardTimer is the standard implementation of a Timer and uses a Histogram -// and Meter. -type StandardTimer struct { +// Timer captures the duration and rate of events, using a Histogram and a Meter. +type Timer struct { histogram Histogram meter *Meter mutex sync.Mutex } // Snapshot returns a read-only copy of the timer. -func (t *StandardTimer) Snapshot() TimerSnapshot { +func (t *Timer) Snapshot() *TimerSnapshot { t.mutex.Lock() defer t.mutex.Unlock() - return &timerSnapshot{ + return &TimerSnapshot{ histogram: t.histogram.Snapshot(), meter: t.meter.Snapshot(), } } // Stop stops the meter. -func (t *StandardTimer) Stop() { +func (t *Timer) Stop() { t.meter.Stop() } // Time record the duration of the execution of the given function. -func (t *StandardTimer) Time(f func()) { +func (t *Timer) Time(f func()) { ts := time.Now() f() t.Update(time.Since(ts)) } // Update the duration of an event, in nanoseconds. -func (t *StandardTimer) Update(d time.Duration) { +func (t *Timer) Update(d time.Duration) { t.mutex.Lock() defer t.mutex.Unlock() t.histogram.Update(d.Nanoseconds()) @@ -120,67 +86,67 @@ func (t *StandardTimer) Update(d time.Duration) { // UpdateSince update the duration of an event that started at a time and ends now. // The record uses nanoseconds. -func (t *StandardTimer) UpdateSince(ts time.Time) { +func (t *Timer) UpdateSince(ts time.Time) { t.Update(time.Since(ts)) } -// timerSnapshot is a read-only copy of another Timer. -type timerSnapshot struct { +// TimerSnapshot is a read-only copy of another Timer. +type TimerSnapshot struct { histogram HistogramSnapshot meter *MeterSnapshot } // Count returns the number of events recorded at the time the snapshot was // taken. -func (t *timerSnapshot) Count() int64 { return t.histogram.Count() } +func (t *TimerSnapshot) Count() int64 { return t.histogram.Count() } // Max returns the maximum value at the time the snapshot was taken. -func (t *timerSnapshot) Max() int64 { return t.histogram.Max() } +func (t *TimerSnapshot) Max() int64 { return t.histogram.Max() } // Size returns the size of the sample at the time the snapshot was taken. -func (t *timerSnapshot) Size() int { return t.histogram.Size() } +func (t *TimerSnapshot) Size() int { return t.histogram.Size() } // Mean returns the mean value at the time the snapshot was taken. -func (t *timerSnapshot) Mean() float64 { return t.histogram.Mean() } +func (t *TimerSnapshot) Mean() float64 { return t.histogram.Mean() } // Min returns the minimum value at the time the snapshot was taken. -func (t *timerSnapshot) Min() int64 { return t.histogram.Min() } +func (t *TimerSnapshot) Min() int64 { return t.histogram.Min() } // Percentile returns an arbitrary percentile of sampled values at the time the // snapshot was taken. -func (t *timerSnapshot) Percentile(p float64) float64 { +func (t *TimerSnapshot) Percentile(p float64) float64 { return t.histogram.Percentile(p) } // Percentiles returns a slice of arbitrary percentiles of sampled values at // the time the snapshot was taken. -func (t *timerSnapshot) Percentiles(ps []float64) []float64 { +func (t *TimerSnapshot) Percentiles(ps []float64) []float64 { return t.histogram.Percentiles(ps) } // Rate1 returns the one-minute moving average rate of events per second at the // time the snapshot was taken. -func (t *timerSnapshot) Rate1() float64 { return t.meter.Rate1() } +func (t *TimerSnapshot) Rate1() float64 { return t.meter.Rate1() } // Rate5 returns the five-minute moving average rate of events per second at // the time the snapshot was taken. -func (t *timerSnapshot) Rate5() float64 { return t.meter.Rate5() } +func (t *TimerSnapshot) Rate5() float64 { return t.meter.Rate5() } // Rate15 returns the fifteen-minute moving average rate of events per second // at the time the snapshot was taken. -func (t *timerSnapshot) Rate15() float64 { return t.meter.Rate15() } +func (t *TimerSnapshot) Rate15() float64 { return t.meter.Rate15() } // RateMean returns the meter's mean rate of events per second at the time the // snapshot was taken. -func (t *timerSnapshot) RateMean() float64 { return t.meter.RateMean() } +func (t *TimerSnapshot) RateMean() float64 { return t.meter.RateMean() } // StdDev returns the standard deviation of the values at the time the snapshot // was taken. -func (t *timerSnapshot) StdDev() float64 { return t.histogram.StdDev() } +func (t *TimerSnapshot) StdDev() float64 { return t.histogram.StdDev() } // Sum returns the sum at the time the snapshot was taken. -func (t *timerSnapshot) Sum() int64 { return t.histogram.Sum() } +func (t *TimerSnapshot) Sum() int64 { return t.histogram.Sum() } // Variance returns the variance of the values at the time the snapshot was // taken. -func (t *timerSnapshot) Variance() float64 { return t.histogram.Variance() } +func (t *TimerSnapshot) Variance() float64 { return t.histogram.Variance() } diff --git a/metrics/writer.go b/metrics/writer.go index a6e225d864..2a41f8e1fe 100644 --- a/metrics/writer.go +++ b/metrics/writer.go @@ -67,7 +67,7 @@ func WriteOnce(r Registry, w io.Writer) { fmt.Fprintf(w, " 5-min rate: %12.2f\n", m.Rate5()) fmt.Fprintf(w, " 15-min rate: %12.2f\n", m.Rate15()) fmt.Fprintf(w, " mean rate: %12.2f\n", m.RateMean()) - case Timer: + case *Timer: t := metric.Snapshot() ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) fmt.Fprintf(w, "timer %s\n", namedMetric.name)