PromQL: resets() function considers start timestamp resets (#18627)

* PromQL: `resets()` function considers start timestamp resets

Signed-off-by: vpranckaitis <vpranckaitis@gmail.com>

* PR comment

Signed-off-by: vpranckaitis <vpranckaitis@gmail.com>

---------

Signed-off-by: vpranckaitis <vpranckaitis@gmail.com>
This commit is contained in:
Vilius Pranckaitis 2026-05-11 16:50:36 +03:00 committed by GitHub
parent c591287db3
commit 129ee0d317
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 34 additions and 4 deletions

View file

@ -2100,7 +2100,7 @@ func (ev *evaluator) eval(ctx context.Context, expr parser.Expr) (parser.Value,
var chkIter chunkenc.Iterator
var startTimestamps *StartTimestamps
if ev.useStartTimestamps && (e.Func.Name == "rate" || e.Func.Name == "irate" || e.Func.Name == "increase") {
if ev.useStartTimestamps && (e.Func.Name == "rate" || e.Func.Name == "irate" || e.Func.Name == "increase" || e.Func.Name == "resets") {
// TODO: consider pooling this.
startTimestamps = &StartTimestamps{}
}

View file

@ -1957,41 +1957,61 @@ func funcResets(_ []Vector, matrixVal Matrix, args parser.Expressions, enh *Eval
return enh.Out, nil
}
var prevSample, curSample Sample
var (
prevSample, curSample Sample
prevST int64
floatSTs, histogramSTs []int64
)
if sts := enh.StartTimestamps; sts != nil {
floatSTs = sts.Floats
histogramSTs = sts.Histograms
}
firstSampleIndex, found := pickFirstSampleIndex(floats, args, enh)
if !found {
return enh.Out, nil
}
for iFloat, iHistogram := firstSampleIndex, 0; iFloat < len(floats) || iHistogram < len(histograms); {
var curST int64
switch {
// Process a float sample if no histogram sample remains or its timestamp is earlier.
// Process a histogram sample if no float sample remains or its timestamp is earlier.
case iHistogram >= len(histograms) || iFloat < len(floats) && floats[iFloat].T < histograms[iHistogram].T:
curSample.T = floats[iFloat].T
curSample.F = floats[iFloat].F
curSample.H = nil
if iFloat < len(floatSTs) {
curST = floatSTs[iFloat]
}
iFloat++
case iFloat >= len(floats) || iHistogram < len(histograms) && floats[iFloat].T > histograms[iHistogram].T:
curSample.T = histograms[iHistogram].T
curSample.H = histograms[iHistogram].H
if iHistogram < len(histogramSTs) {
curST = histogramSTs[iHistogram]
}
iHistogram++
}
// Skip the comparison for the first sample, just initialize prevSample.
if iFloat+iHistogram == 1+firstSampleIndex {
prevSample = curSample
prevST = curST
continue
}
switch {
case prevSample.H == nil && curSample.H == nil:
if curSample.F < prevSample.F {
if curSample.F < prevSample.F || isStartTimestampReset(prevST, prevSample.T, curST, curSample.T) {
resets++
}
case prevSample.H != nil && curSample.H == nil, prevSample.H == nil && curSample.H != nil:
resets++
case prevSample.H != nil && curSample.H != nil:
if curSample.H.DetectReset(prevSample.H) {
if isStartTimestampReset(prevST, prevSample.T, curST, curSample.T) || curSample.H.DetectReset(prevSample.H) {
resets++
}
}
prevSample = curSample
prevST = curST
}
return append(enh.Out, Sample{F: float64(resets)}), nil

View file

@ -38,6 +38,12 @@ eval range from 7m to 15m step 1m increase(cumulative[5m:1m])
{type="st_resets"} 300x8
{type="normalized_resets"} 420 780 675 675 1275 900 900 1725 1125
# Resets function also considers start timestamp resets.
eval range from 7m to 15m step 1m resets(cumulative[5m])
{type="no_st"} 0x8
{type="st_resets"} 1 2 1 1 2 1 1 2 1
{type="normalized_resets"} 0x8
clear
# Tests for rate(), irate() and increase() on deltas. Includes various approaches for start timestamps:
@ -131,6 +137,10 @@ eval range from 7m to 15m step 1m rate(cumulative[5m])
eval range from 7m to 15m step 1m irate(cumulative[5m])
{} {{count:1 sum:1}}x8
# Resets function also considers start timestamp resets.
eval range from 7m to 15m step 1m resets(cumulative[5m])
{} 0x8
clear
# Tests for rate(), irate() and increase() on delta histograms.