From 59c00e3e9cdfef82a5616aae8abaa053ec657208 Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Tue, 1 Aug 2023 17:11:17 +0100 Subject: [PATCH] Fix data-race in metric without code and method but with `WithLabelFromCtx` (#1318) This commit fixes a data race that exists when the metric used in any `promhttp` middleware doesn't collect the `code` and `method` but uses `WithLabelFromCtx` to collect values from context. The problem happens because when no `code` and `method` tags are collected, the `labels` function returns a pre-initialized map `emptyLabels` for every request. When one or multipe `WithLabelFromCtx` options are configured, the returned map from the `labels` function call is used to collect the metrics from context which creates a multi-write data race. Signed-off-by: Tiago Silva --- prometheus/promhttp/instrument_server.go | 11 ++++------- prometheus/promhttp/instrument_server_test.go | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/prometheus/promhttp/instrument_server.go b/prometheus/promhttp/instrument_server.go index 3793036..356edb7 100644 --- a/prometheus/promhttp/instrument_server.go +++ b/prometheus/promhttp/instrument_server.go @@ -389,16 +389,13 @@ func isLabelCurried(c prometheus.Collector, label string) bool { return true } -// emptyLabels is a one-time allocation for non-partitioned metrics to avoid -// unnecessary allocations on each request. -var emptyLabels = prometheus.Labels{} - func labels(code, method bool, reqMethod string, status int, extraMethods ...string) prometheus.Labels { - if !(code || method) { - return emptyLabels - } labels := prometheus.Labels{} + if !(code || method) { + return labels + } + if code { labels["code"] = sanitizeCode(status) } diff --git a/prometheus/promhttp/instrument_server_test.go b/prometheus/promhttp/instrument_server_test.go index 6c64443..8aef09f 100644 --- a/prometheus/promhttp/instrument_server_test.go +++ b/prometheus/promhttp/instrument_server_test.go @@ -250,7 +250,7 @@ func TestLabels(t *testing.T) { }{ "empty": { varLabels: []string{}, - wantLabels: emptyLabels, + wantLabels: prometheus.Labels{}, reqMethod: "GET", respStatus: 200, ok: true,