From 2c14970acbbd963c66c5b49ddf3c9c3bb02e18c6 Mon Sep 17 00:00:00 2001 From: Ecattea <147585044+Ecattea@users.noreply.github.com> Date: Wed, 25 Feb 2026 22:03:27 +0800 Subject: [PATCH] fix(metric): avoid metrics mux conflict across instances Use per-instance ServeMux in Run and add regression test for multi-instance panic. --- metric/metric.go | 7 ++++--- metric/metric_run_test.go | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 metric/metric_run_test.go diff --git a/metric/metric.go b/metric/metric.go index cc7980495..d868f5524 100644 --- a/metric/metric.go +++ b/metric/metric.go @@ -103,10 +103,11 @@ func (m *metric) Run(ctx context.Context) (err error) { return } if m.config.Addr != "" { - var errCh = make(chan error) - http.Handle("/metrics", promhttp.HandlerFor(m.registry, promhttp.HandlerOpts{})) + var errCh = make(chan error, 1) + mux := http.NewServeMux() + mux.Handle("/metrics", promhttp.HandlerFor(m.registry, promhttp.HandlerOpts{})) go func() { - errCh <- http.ListenAndServe(m.config.Addr, nil) + errCh <- http.ListenAndServe(m.config.Addr, mux) }() select { case err = <-errCh: diff --git a/metric/metric_run_test.go b/metric/metric_run_test.go new file mode 100644 index 000000000..5fe24e7f3 --- /dev/null +++ b/metric/metric_run_test.go @@ -0,0 +1,37 @@ +package metric + +import ( + "context" + "testing" + + "github.com/anyproto/any-sync/app" + "github.com/prometheus/client_golang/prometheus" +) + +func TestRun_DoesNotPanicWithMultipleInstances(t *testing.T) { + defer func() { + if r := recover(); r != nil { + t.Fatalf("metric.Run should not panic with multiple metric instances: %v", r) + } + }() + + first := newTestMetric() + if err := first.Run(context.Background()); err == nil { + t.Fatal("first metric run should return listen error with invalid test address") + } + + second := newTestMetric() + if err := second.Run(context.Background()); err == nil { + t.Fatal("second metric run should return listen error with invalid test address") + } +} + +func newTestMetric() *metric { + return &metric{ + registry: prometheus.NewRegistry(), + // Intentionally invalid address to avoid creating long-lived listeners in unit tests. + config: Config{Addr: "127.0.0.1"}, + a: &app.App{}, + syncMetrics: map[string]SyncMetric{}, + } +}