diff --git a/promql/functions.go b/promql/functions.go index abdabda2645..43feab1ec12 100644 --- a/promql/functions.go +++ b/promql/functions.go @@ -338,7 +338,10 @@ func extendedRate(vals []parser.Value, args parser.Expressions, enh *EvalNodeHel // yIncrease is a utility function for yincrease/yrate/ydelta. // It calculates the increase of the range (allowing for counter resets if isCounter is true), // taking into account the sample at the end of the previous range (just before rangeStartMsec). -// It returns the result across the range [rangeStartMsec, rangeEndMsec). +// It returns the result across the range (rangeStartMsec, rangeEndMsec]. The left-open, +// right-closed convention matches the Prometheus 3.x range-selector semantics +// (see prometheus/prometheus#13213) so that a sample whose timestamp lands exactly on +// a range boundary is attributed to the later range, never to both or neither. // It always extends the preceding sample's value until the next sample, including the // unwritten origin sample value at the start of every time series. // @@ -357,8 +360,8 @@ func yIncrease(points []FPoint, rangeStartMsec, rangeEndMsec int64, isCounter bo // The points are in time order, so we can just walk the list once and remember the last values // seen "before" and "in" range. If there are no values in range, we use the last value before range // so that the increase is 0. - for i := 0; i < len(points) && points[i].T < rangeEndMsec; i++ { // Only consider points in [rangeStartMsec, rangeEndMsec). - if points[i].T >= rangeStartMsec { + for i := 0; i < len(points) && points[i].T <= rangeEndMsec; i++ { // Only consider points in (rangeStartMsec, rangeEndMsec]. + if points[i].T > rangeStartMsec { if isCounter && points[i].F < lastInRange { // Counter reset (process restart). inRangeRestartSkew += lastInRange } @@ -373,8 +376,8 @@ func yIncrease(points []FPoint, rangeStartMsec, rangeEndMsec int64, isCounter bo // rangeFromSelectors extracts points, rangeStartMsec, rangeEndMsec, and rangeSeconds // from the common (Matrix, MatrixSelector) arguments supplied to yincrease/yrate/ydelta. -// The range is [rangeStartMsec, rangeEndMsec). That is, every sample in range has the property: -// rangeStartMsec <= sample.T < rangeEndMsec. +// The range is (rangeStartMsec, rangeEndMsec]. That is, every sample in range has the property: +// rangeStartMsec < sample.T <= rangeEndMsec. func rangeFromSelectors(vals []parser.Value, args parser.Expressions, enh *EvalNodeHelper) ([]FPoint, int64, int64, float64) { ms := args[0].(*parser.MatrixSelector) vs := ms.VectorSelector.(*parser.VectorSelector) diff --git a/promql/promqltest/testdata/functions.test b/promql/promqltest/testdata/functions.test index 7695f36abcb..8739fc8824c 100644 --- a/promql/promqltest/testdata/functions.test +++ b/promql/promqltest/testdata/functions.test @@ -25,7 +25,7 @@ eval instant at 25s xrate(http_requests[50s]) eval instant at 25s yrate(http_requests[50s]) {path="/foo"} 0.04 - {path="/bar"} 0.42 + {path="/bar"} 0.52 # 2. Eval 1 second earlier compared to (1). # * path="/foo" rate should be same or fractionally higher ("shorter" sample, same actual increase); @@ -74,8 +74,8 @@ eval instant at 75s xrate(http_requests[50s]) {path="/bar"} 0.8 eval instant at 75s yrate(http_requests[50s]) - {path="/foo"} 0.06 - {path="/bar"} 1.32 + {path="/foo"} 0.02 + {path="/bar"} 0.8 # 5. Eval 1s earlier compared to (4). # * path="/foo" rate should be same or fractionally lower ("longer" sample, same actual increase).