From a5b9a057458e44037e2d1453630c897505d709e7 Mon Sep 17 00:00:00 2001 From: Paurush Garg Date: Fri, 22 May 2026 14:33:08 -0700 Subject: [PATCH] fix(queryrange): Handle native histogram responses in minTime() sort ordering for split_by_interval merge Signed-off-by: Paurush Garg --- pkg/querier/tripperware/merge.go | 11 ++- pkg/querier/tripperware/merge_test.go | 105 ++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 4 deletions(-) diff --git a/pkg/querier/tripperware/merge.go b/pkg/querier/tripperware/merge.go index 3ebf099f67b..bd021382da2 100644 --- a/pkg/querier/tripperware/merge.go +++ b/pkg/querier/tripperware/merge.go @@ -25,16 +25,19 @@ func (a byFirstTime) Less(i, j int) bool { return a[i].minTime() < a[j].minTime( func (resp *PrometheusResponse) minTime() int64 { data := resp.GetData() res := data.GetResult() - // minTime should only be called when the response is fron range query. + // minTime should only be called when the response is from range query. matrix := res.GetMatrix() sampleStreams := matrix.GetSampleStreams() if len(sampleStreams) == 0 { return -1 } - if len(sampleStreams[0].Samples) == 0 { - return -1 + if len(sampleStreams[0].Samples) > 0 { + return sampleStreams[0].Samples[0].TimestampMs + } + if len(sampleStreams[0].Histograms) > 0 { + return sampleStreams[0].Histograms[0].GetTimestampMs() } - return sampleStreams[0].Samples[0].TimestampMs + return -1 } // MergeResponse merges multiple Response into one. diff --git a/pkg/querier/tripperware/merge_test.go b/pkg/querier/tripperware/merge_test.go index 705f75d2c3f..b4064b390f9 100644 --- a/pkg/querier/tripperware/merge_test.go +++ b/pkg/querier/tripperware/merge_test.go @@ -681,3 +681,108 @@ func Test_sortPlanForQuery(t *testing.T) { }) } } + +func TestMinTime(t *testing.T) { + t.Parallel() + for _, tc := range []struct { + name string + resp *PrometheusResponse + expected int64 + }{ + { + name: "empty matrix", + resp: &PrometheusResponse{ + Data: PrometheusData{ + ResultType: "matrix", + Result: PrometheusQueryResult{ + Result: &PrometheusQueryResult_Matrix{ + Matrix: &Matrix{SampleStreams: []SampleStream{}}, + }, + }, + }, + }, + expected: -1, + }, + { + name: "float samples only", + resp: &PrometheusResponse{ + Data: PrometheusData{ + ResultType: "matrix", + Result: PrometheusQueryResult{ + Result: &PrometheusQueryResult_Matrix{ + Matrix: &Matrix{SampleStreams: []SampleStream{ + { + Labels: cortexpb.FromLabelsToLabelAdapters(labels.FromMap(map[string]string{"foo": "bar"})), + Samples: []cortexpb.Sample{{TimestampMs: 1000}, {TimestampMs: 2000}}, + }, + }}, + }, + }, + }, + }, + expected: 1000, + }, + { + name: "histograms only", + resp: &PrometheusResponse{ + Data: PrometheusData{ + ResultType: "matrix", + Result: PrometheusQueryResult{ + Result: &PrometheusQueryResult_Matrix{ + Matrix: &Matrix{SampleStreams: []SampleStream{ + { + Labels: cortexpb.FromLabelsToLabelAdapters(labels.FromMap(map[string]string{"foo": "bar"})), + Histograms: []SampleHistogramPair{{TimestampMs: 3000, Histogram: testHistogram1}, {TimestampMs: 4000, Histogram: testHistogram1}}, + }, + }}, + }, + }, + }, + }, + expected: 3000, + }, + { + name: "both samples and histograms - samples first", + resp: &PrometheusResponse{ + Data: PrometheusData{ + ResultType: "matrix", + Result: PrometheusQueryResult{ + Result: &PrometheusQueryResult_Matrix{ + Matrix: &Matrix{SampleStreams: []SampleStream{ + { + Labels: cortexpb.FromLabelsToLabelAdapters(labels.FromMap(map[string]string{"foo": "bar"})), + Samples: []cortexpb.Sample{{TimestampMs: 1000}}, + Histograms: []SampleHistogramPair{{TimestampMs: 5000, Histogram: testHistogram1}}, + }, + }}, + }, + }, + }, + }, + expected: 1000, + }, + { + name: "stream with empty samples and empty histograms", + resp: &PrometheusResponse{ + Data: PrometheusData{ + ResultType: "matrix", + Result: PrometheusQueryResult{ + Result: &PrometheusQueryResult_Matrix{ + Matrix: &Matrix{SampleStreams: []SampleStream{ + { + Labels: cortexpb.FromLabelsToLabelAdapters(labels.FromMap(map[string]string{"foo": "bar"})), + }, + }}, + }, + }, + }, + }, + expected: -1, + }, + } { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + assert.Equal(t, tc.expected, tc.resp.minTime()) + }) + } +}