From 6c302c4435d0cba262ce51c68594fce3758d534b Mon Sep 17 00:00:00 2001 From: Patrick Begley Date: Mon, 1 Jun 2026 13:56:27 -0400 Subject: [PATCH] Refactor metrics emitter interface Use a single metrics emitter abstraction for gauge, count, and histogram samples so metric helpers share one testable client contract. --- go/base/context.go | 2 +- go/logic/migrator_test.go | 6 ++++++ go/metrics/binlog_backlog.go | 2 +- go/metrics/client.go | 13 +++++++++++-- go/metrics/go_runtime.go | 2 +- go/metrics/go_runtime_test.go | 6 ++++++ go/metrics/lag.go | 2 +- go/metrics/progress.go | 2 +- 8 files changed, 28 insertions(+), 7 deletions(-) diff --git a/go/base/context.go b/go/base/context.go index 5a06d0d4d..4d0c3b09d 100644 --- a/go/base/context.go +++ b/go/base/context.go @@ -238,7 +238,7 @@ type MigrationContext struct { AbortError error abortMutex *sync.Mutex - Metrics metrics.MemStatsGaugeEmitter + Metrics metrics.Emitter OriginalTableColumnsOnApplier *sql.ColumnList OriginalTableColumns *sql.ColumnList diff --git a/go/logic/migrator_test.go b/go/logic/migrator_test.go index 74b09a47c..d939ae97d 100644 --- a/go/logic/migrator_test.go +++ b/go/logic/migrator_test.go @@ -419,6 +419,12 @@ func (s *progressGaugeSpy) Gauge(name string, value float64, tags ...string) { s.tags = append(s.tags, append([]string(nil), tags...)) } +func (s *progressGaugeSpy) Count(name string, value int64, tags ...string) { +} + +func (s *progressGaugeSpy) Histogram(name string, value float64, tags ...string) { +} + func TestReportStatusEmitsProgressGaugesEveryTick(t *testing.T) { spy := &progressGaugeSpy{} ctx := base.NewMigrationContext() diff --git a/go/metrics/binlog_backlog.go b/go/metrics/binlog_backlog.go index 2d6ea5a4b..6ce102ae6 100644 --- a/go/metrics/binlog_backlog.go +++ b/go/metrics/binlog_backlog.go @@ -7,7 +7,7 @@ package metrics // EmitBinlogBacklogGauges emits apply-events queue depth gauges (namespace is applied by the client): // gh_ost.binlog.backlog_size, gh_ost.binlog.backlog_capacity, gh_ost.binlog.backlog_utilization. -func EmitBinlogBacklogGauges(emit MemStatsGaugeEmitter, backlogSize, backlogCapacity int) { +func EmitBinlogBacklogGauges(emit Emitter, backlogSize, backlogCapacity int) { if emit == nil { return } diff --git a/go/metrics/client.go b/go/metrics/client.go index 4600df8e9..1d78df216 100644 --- a/go/metrics/client.go +++ b/go/metrics/client.go @@ -21,9 +21,11 @@ type Client struct { sd *statsd.Client } -// MemStatsGaugeEmitter is implemented by *Client; used for tests without UDP. -type MemStatsGaugeEmitter interface { +// Emitter is implemented by *Client; used for tests without UDP. +type Emitter interface { Gauge(name string, value float64, tags ...string) + Count(name string, value int64, tags ...string) + Histogram(name string, value float64, tags ...string) } // NewClient connects to addr for StatsD. If addr is empty, returns Noop and nil error. @@ -65,6 +67,13 @@ func (c *Client) Count(name string, value int64, tags ...string) { _ = c.sd.Count(name, value, tags, 1.0) } +func (c *Client) Histogram(name string, value float64, tags ...string) { + if c.sd == nil { + return + } + _ = c.sd.Histogram(name, value, tags, 1.0) +} + // Close flushes buffered metrics; safe for Noop. func (c *Client) Close() error { if c.sd == nil { diff --git a/go/metrics/go_runtime.go b/go/metrics/go_runtime.go index 021e987bf..92526b339 100644 --- a/go/metrics/go_runtime.go +++ b/go/metrics/go_runtime.go @@ -13,7 +13,7 @@ import ( // EmitGoRuntimeGauges emits gh_ost.go_runtime.* gauges (namespace is applied by the client). // m and numGoroutine are typically from runtime.ReadMemStats and runtime.NumGoroutine. -func EmitGoRuntimeGauges(emit MemStatsGaugeEmitter, m *runtime.MemStats, numGoroutine int) { +func EmitGoRuntimeGauges(emit Emitter, m *runtime.MemStats, numGoroutine int) { if emit == nil || m == nil { return } diff --git a/go/metrics/go_runtime_test.go b/go/metrics/go_runtime_test.go index 0a8a5d7e2..02657b860 100644 --- a/go/metrics/go_runtime_test.go +++ b/go/metrics/go_runtime_test.go @@ -24,6 +24,12 @@ func (g *gaugeSpy) Gauge(name string, value float64, tags ...string) { g.tags = append(g.tags, append([]string(nil), tags...)) } +func (g *gaugeSpy) Count(name string, value int64, tags ...string) { +} + +func (g *gaugeSpy) Histogram(name string, value float64, tags ...string) { +} + func TestEmitGoRuntimeGauges(t *testing.T) { spy := &gaugeSpy{} m := &runtime.MemStats{ diff --git a/go/metrics/lag.go b/go/metrics/lag.go index b6dc6995a..9a03d4f85 100644 --- a/go/metrics/lag.go +++ b/go/metrics/lag.go @@ -13,7 +13,7 @@ import "fmt" // These are point-in-time readings each status tick (not a distribution), so gauges are used // rather than histograms — DogStatsD histogram aggregation exposes count/max series that do not // match the log line lag values in Prometheus/Grafana. -func EmitLagGauges(emit MemStatsGaugeEmitter, replicationLagSeconds, heartbeatLagSeconds float64, throttled bool) { +func EmitLagGauges(emit Emitter, replicationLagSeconds, heartbeatLagSeconds float64, throttled bool) { if emit == nil { return } diff --git a/go/metrics/progress.go b/go/metrics/progress.go index b6b3361b6..bc525bc8b 100644 --- a/go/metrics/progress.go +++ b/go/metrics/progress.go @@ -7,7 +7,7 @@ package metrics // EmitProgressGauges emits row-copy and DML progress gauges (namespace is applied by the client): // gh_ost.row_copy.rows_copied, gh_ost.row_copy.rows_estimate, gh_ost.dml.events_applied. -func EmitProgressGauges(emit MemStatsGaugeEmitter, rowsCopied, rowsEstimate, dmlEventsApplied int64) { +func EmitProgressGauges(emit Emitter, rowsCopied, rowsEstimate, dmlEventsApplied int64) { if emit == nil { return }