From b09cf6be8d5650fd5287d7a7699b2fd75184f7fc Mon Sep 17 00:00:00 2001 From: Harry John Date: Sun, 20 Jul 2025 23:54:54 -0700 Subject: [PATCH] fix(promql): Ensure native histogram values copied in subqueries (#16879) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 🌲 Harry 🌊 John 🏔 --- promql/engine_test.go | 46 +++++++++++++++++++++++++++++++++++++++++++ promql/value.go | 8 ++++++-- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/promql/engine_test.go b/promql/engine_test.go index ce5ef6efd7..0369a285b2 100644 --- a/promql/engine_test.go +++ b/promql/engine_test.go @@ -3985,3 +3985,49 @@ func TestInconsistentHistogramCount(t *testing.T) { require.Equal(t, countFromHistogram, countFromFunction, "histogram_count function should return the same count as the histogram itself") } + +// TestSubQueryHistogramsCopy reproduces a bug where native histogram values from subqueries are not copied. +func TestSubQueryHistogramsCopy(t *testing.T) { + start := time.Unix(0, 0) + end := time.Unix(144, 0) + step := time.Second * 3 + + load := `load 2m + http_request_duration_seconds{pod="nginx-1"} {{schema:0 count:110 sum:818.00 buckets:[1 14 95]}}+{{schema:0 count:110 buckets:[1 14 95]}}x20 + http_request_duration_seconds{pod="nginx-2"} {{schema:0 count:210 sum:1598.00 buckets:[1 19 190]}}+{{schema:0 count:210 buckets:[1 19 190]}}x30` + + subQuery := `min_over_time({__name__="http_request_duration_seconds"}[1h:1m])` + testQuery := `rate({__name__="http_request_duration_seconds"}[3m])` + ctx := context.Background() + + for i := 0; i < 100; i++ { + queryable := promqltest.LoadedStorage(t, load) + engine := promqltest.NewTestEngine(t, false, 0, promqltest.DefaultMaxSamplesPerQuery) + + q, err := engine.NewRangeQuery(ctx, queryable, nil, subQuery, start, end, step) + require.NoError(t, err) + q.Exec(ctx) + q.Close() + queryable.Close() + } + + for i := 0; i < 100; i++ { + queryable := promqltest.LoadedStorage(t, load) + engine := promqltest.NewTestEngine(t, false, 0, promqltest.DefaultMaxSamplesPerQuery) + + q, err := engine.NewRangeQuery(ctx, queryable, nil, testQuery, start, end, step) + require.NoError(t, err) + result := q.Exec(ctx) + + mat, err := result.Matrix() + require.NoError(t, err) + + for _, s := range mat { + for _, h := range s.Histograms { + require.NotEmpty(t, h.H.PositiveBuckets) + } + } + q.Close() + queryable.Close() + } +} diff --git a/promql/value.go b/promql/value.go index dc59b9e9cc..2e387117e5 100644 --- a/promql/value.go +++ b/promql/value.go @@ -475,8 +475,12 @@ func (ssi *storageSeriesIterator) AtHistogram(*histogram.Histogram) (int64, *his panic(errors.New("storageSeriesIterator: AtHistogram not supported")) } -func (ssi *storageSeriesIterator) AtFloatHistogram(*histogram.FloatHistogram) (int64, *histogram.FloatHistogram) { - return ssi.currT, ssi.currH +func (ssi *storageSeriesIterator) AtFloatHistogram(fh *histogram.FloatHistogram) (int64, *histogram.FloatHistogram) { + if fh == nil { + return ssi.currT, ssi.currH.Copy() + } + ssi.currH.CopyTo(fh) + return ssi.currT, fh } func (ssi *storageSeriesIterator) AtT() int64 {