diff --git a/promql/functions.go b/promql/functions.go index 7b3fd2d363..6ca32c4a81 100644 --- a/promql/functions.go +++ b/promql/functions.go @@ -447,6 +447,12 @@ func isStartTimestampReset(prevStartTimestamp, prevTimestamp, currStartTimestamp // This should be treated as a reset for deltas, but it is not a reset for cumulative series // with unknown start timestamp. Thus we have to check whether the start timestamp // of the previous datapoint is known. + // + // A previous start timestamp greater than the previous sample timestamp is invalid; treat it + // as unknown to avoid a spurious reset. + if prevStartTimestamp > prevTimestamp { + return false + } return prevStartTimestamp != 0 } diff --git a/promql/promqltest/testdata/start_timestamps.test b/promql/promqltest/testdata/start_timestamps.test index c7148e5cf3..3ad31e85ba 100644 --- a/promql/promqltest/testdata/start_timestamps.test +++ b/promql/promqltest/testdata/start_timestamps.test @@ -94,17 +94,21 @@ clear # Test for cumulative with unknow start timestamp, which is described in OTel spec. It is easy to incorrectly treat # second datapoint in such timeseries as delta, producing incorrect result. These can be distinguished by: -# * ST_curr == T_prev && ST_prev == 0 -> cumulative with unknown start, no reset -# * ST_curr == T_prev && ST_prev != 0 -> delta datapoint, treat as a reset +# * ST_curr == T_prev && ST_prev == 0 -> cumulative with unknown start, no reset +# * ST_curr == T_prev && ST_prev != 0 -> delta datapoint, treat as a reset +# * ST_curr == T_prev && ST_prev > T_prev (invalid) -> must not be mistaken for the delta case above load 1m - series{type="otel_cumulative_unknown_start"}@st _ _ _ -1m - series{type="otel_cumulative_unknown_start"} _ _ 1 1 - series{type="otel_delta"}@st _ _ -1m -1m - series{type="otel_delta"} _ _ 1 1 + series{type="otel_cumulative_unknown_start"}@st _ _ _ -1m + series{type="otel_cumulative_unknown_start"} _ _ 1 1 + series{type="otel_delta"}@st _ _ -1m -1m + series{type="otel_delta"} _ _ 1 1 + series{type="invalid_prev_st"}@st _ _ +1m -1m + series{type="invalid_prev_st"} _ _ 1 1 eval instant at 3m round(increase(series[1m1ms])) {type="otel_cumulative_unknown_start"} 0 {type="otel_delta"} 1 + {type="invalid_prev_st"} 0 clear