From 554ea9ebfeffe0a42f58944923ac3bee9ddd812a Mon Sep 17 00:00:00 2001 From: Julien Pivotto <291750+roidelapluie@users.noreply.github.com> Date: Wed, 5 Nov 2025 11:57:24 +0100 Subject: [PATCH] promql: fix resets/changes to return empty for anchored selectors when samples outside range The funcResets and funcChanges functions now correctly return no result when all float samples are at or before the range start for anchored selectors, consistent with the behavior of rate/increase functions. Signed-off-by: Julien Pivotto <291750+roidelapluie@users.noreply.github.com> --- promql/functions.go | 22 ++++++++++++++----- .../promqltest/testdata/extended_vectors.test | 2 -- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/promql/functions.go b/promql/functions.go index 2ff25c3acf..2602c25c1d 100644 --- a/promql/functions.go +++ b/promql/functions.go @@ -1709,15 +1709,19 @@ func funcHistogramQuantile(vectorVals []Vector, _ Matrix, args parser.Expression // pickFirstSampleIndex returns the index of the last sample before // or at the range start, or 0 if none exist before the range start. -// If the vector selector is not anchored, it always returns 0. -func pickFirstSampleIndex(floats []FPoint, args parser.Expressions, enh *EvalNodeHelper) int { +// If the vector selector is not anchored, it always returns 0, true. +// The second return value is false if there are no samples in range (for anchored selectors). +func pickFirstSampleIndex(floats []FPoint, args parser.Expressions, enh *EvalNodeHelper) (int, bool) { ms := args[0].(*parser.MatrixSelector) vs := ms.VectorSelector.(*parser.VectorSelector) if !vs.Anchored { - return 0 + return 0, true } rangeStart := enh.Ts - durationMilliseconds(ms.Range+vs.Offset) - return max(0, sort.Search(len(floats)-1, func(i int) bool { return floats[i].T > rangeStart })-1) + if len(floats) == 0 || floats[len(floats)-1].T <= rangeStart { + return 0, false + } + return max(0, sort.Search(len(floats)-1, func(i int) bool { return floats[i].T > rangeStart })-1), true } // === resets(Matrix parser.ValueTypeMatrix) (Vector, Annotations) === @@ -1730,7 +1734,10 @@ func funcResets(_ []Vector, matrixVal Matrix, args parser.Expressions, enh *Eval } var prevSample, curSample Sample - firstSampleIndex := pickFirstSampleIndex(floats, args, enh) + firstSampleIndex, found := pickFirstSampleIndex(floats, args, enh) + if !found { + return enh.Out, nil + } for iFloat, iHistogram := firstSampleIndex, 0; iFloat < len(floats) || iHistogram < len(histograms); { switch { // Process a float sample if no histogram sample remains or its timestamp is earlier. @@ -1776,7 +1783,10 @@ func funcChanges(_ []Vector, matrixVal Matrix, args parser.Expressions, enh *Eva } var prevSample, curSample Sample - firstSampleIndex := pickFirstSampleIndex(floats, args, enh) + firstSampleIndex, found := pickFirstSampleIndex(floats, args, enh) + if !found { + return enh.Out, nil + } for iFloat, iHistogram := firstSampleIndex, 0; iFloat < len(floats) || iHistogram < len(histograms); { switch { // Process a float sample if no histogram sample remains or its timestamp is earlier. diff --git a/promql/promqltest/testdata/extended_vectors.test b/promql/promqltest/testdata/extended_vectors.test index 8e116b1ac5..8f431dcfd3 100644 --- a/promql/promqltest/testdata/extended_vectors.test +++ b/promql/promqltest/testdata/extended_vectors.test @@ -319,7 +319,6 @@ eval instant at 2m changes(metric[1m] anchored) {id="2"} 1 eval instant at 3m changes(metric[1m] anchored) - {id="1"} 1 {id="2"} 1 eval instant at 8m changes(metric[1m] anchored) @@ -342,7 +341,6 @@ eval instant at 2m resets(metric[1m] anchored) {id="2"} 1 eval instant at 3m resets(metric[1m] anchored) - {id="1"} 1 {id="2"} 1 eval instant at 8m resets(metric[1m] anchored)