Add yrate/yincrease/ydelta (2.55)#22
Draft
ColinDKelley wants to merge 6 commits into
Draft
Conversation
Ported from invoca-2.39.2-extensions and adapted to the 2.53 FPoint API. yIncrease computes the per-range increase using a simple linear algorithm that assumes one extended-range sample is kept before the range start (mirrors the mechanism xrate already uses via ExtRange: true). Handles counter-reset skew when isCounter is true. rangeFromSelectors consolidates the boilerplate for unpacking the matrix-selector arguments shared by yincrease/yrate/ydelta. Both are unexported and not yet wired up; the follow-up commit adds the funcY* wrappers and parser entries. Made-with: Cursor
Adds three new range-vector functions that mirror the shape of the existing xrate/xincrease/xdelta family: - ydelta(m[r]) for gauges: last-in-range minus last-before-range - yincrease(m[r]) for counters: yIncrease with isCounter=true - yrate(m[r]) same as yincrease, divided by range seconds The funcY* wrappers delegate to the yIncrease helper added in the previous commit. Parser entries use ExtRange: true to opt into the extended-range window populated by matrixIterSlice. Smoke-verified with a small loaded-storage test: yrate/yincrease/ ydelta match expected values derived from the yIncrease algorithm (last_in_range - last_before_range + counter_reset_skew), without requiring any engine changes beyond what xrate already relies on. No REPLACE_RATE_FUNCS wiring yet; that lands in a follow-up commit. Made-with: Cursor
Extends the existing REPLACE_RATE_FUNCS init-time hook to also recognise "x"/"X" and "2"/"y"/"Y" values (matching the invoca-2.39.2 extension). The "x" and "y" cases repoint rate/increase/delta at the xrate or yrate implementations respectively, while keeping the x*/y* names available under their original spellings. Also introduces repointParserFunctions and repointFunction as small helpers so the dispatch-table rewrites read cleanly. Made-with: Cursor
Ports the full set of yrate test expectations from
invoca-2.39.2-extensions, split across three existing sections of
promql/promqltest/testdata/functions.test:
- Rate-vs-xrate showcase block: yrate() evals added in parallel with
each rate/xrate variant, so readers can see yrate's pre-origin-
treated-as-zero behaviour next to the other two semantics.
- "Tests for xincrease/xrate" block: replaced the 0+10x10 /
0+10x5 0+10x4 load with the 1000+10x10 / 2000+10x5 5+10x4 load
from 2.39.2. Absolute offsets make the yrate family's per-range
values visibly diverge from xrate (e.g. yincrease[50s] returns
1090 for /foo while xincrease[50s] returns 90). Re-adds
increase() coverage for the same data as a standard-Prometheus
reference.
- Counter-reset block: switched from 0 1 2 3 2 3 4 to the 2.39.2
1000 1001 1003 1006 4 9 16 load, added yincrease coverage at
the 30m / 10m / 5m ranges and at evaluation times that straddle
the counter-reset boundary.
- New ydelta block: exercises ydelta on two mirrored gauge series
(one increasing, one decreasing) at 30m / 25m / 5m ranges with
reference delta()/xdelta() comparisons.
Eval times are shifted one second (49s instead of 50s) or one minute
(29m instead of 30m) off the sample cadence so that sample timestamps
land strictly inside the half-open [rangeStart, rangeEnd) interval.
When samples align exactly on a range boundary, matrixIterSlice treats
the sample as the retained pre-range sample, which hides the pre-range
counter value from yIncrease; the shifted eval times mirror the
+321ms offset the legacy promql.NewTest framework applied for the
same reason. All ported 2.39.2 expected values reproduce exactly
under the new promqltest framework with this offset.
Made-with: Cursor
This was referenced Apr 19, 2026
The yrate/yincrease/ydelta family is designed so that two adjacent
windows over a series partition a wider window without double-counting
any sample. Restated as an invariant: for any three timestamps
T_0 < T_1 < T_2 with r_1 = T_1 - T_0 and r_2 = T_2 - T_1,
yincrease(m[r_1]) @ T_1 + yincrease(m[r_2]) @ T_2
==
yincrease(m[r_1 + r_2]) @ T_2
This property is what makes yincrease safe to aggregate across
adjacent query ranges and what distinguishes it from stock
rate/increase. Until now it was only covered implicitly through the
1000+/2000+ numeric regression block, which made silent semantic
regressions plausible (e.g. a boundary flip that preserves individual
queries' values but breaks additivity).
Add three direct scenarios that assert LHS and RHS separately so any
drift is visible side-by-side:
1. uniform counter, no resets
2. counter reset inside the earlier window
3. counter reset inside the later window
All three pick T_0, T_1, T_2 off-cadence so no sample lands on a
range boundary. That makes the expected values stable across both
boundary conventions (left-inclusive on the add-yrate line,
right-inclusive after align-yrate-to-3x-range-boundary) and lets the
block cherry-pick cleanly up the yrate branch stack.
Made-with: Cursor
Match the upstream-facing proposal vocabulary by retiring "linear" /
"linearity" from yrate-related internal artifacts in favor of:
* "additive over adjacent ranges" -- the precise mathematical anchor
(finite additivity over a partition of disjoint intervals)
* "composable" -- the user-facing benefit derived from additivity
(already the term PROM-52 names as a goal for `anchored`)
Two atomic edits, no behavior change:
* promql/functions.go: yIncrease docstring now says "additive over
adjacent periods, and therefore composable across any partitioning
of a wider range into contiguous sub-ranges". The formula stays
the same.
* promql/promqltest/testdata/functions.test: rename the test-data
block header from "Linearity invariant" to "Additivity invariant",
expand the introduction to mention composability, and rename the
three test series from linearity_{uniform,reset_early,reset_late}
to additivity_{uniform,reset_early,reset_late}.
Strict "linearity" overclaimed (the property at issue is finite
additivity over a partition of adjacent intervals, not full linearity
over a vector space) and the two-term split keeps the proof anchor
distinct from the user-facing benefit.
Unrelated upstream references to "linear" (predict_linear,
linearRegression, "linear search" complexity comments) are left
untouched.
Co-authored-by: Cursor <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Forward-port of #17 onto
invoca-2.55.1/fix-xrate-extended-range.All four commits cherry-picked cleanly from
invoca-2.53.1/add-yrate;promql/functions.go,promql/parser/functions.go, andpromql/promqltest/testdata/functions.testall auto-merged. No code changes from the 2.53.1 version — the 2.53→2.55 upstream delta is entirely in other subsystems (OTLP, remote write, UI) and does not touch the range-extension path.yIncrease+rangeFromSelectorshelpers.yrate/yincrease/ydeltaPromQL functions.REPLACE_RATE_FUNCSenv switch for yrate; run once at init — supportsx/X(point rate→xrate, keep x* names available) and2/y/Y(point rate→yrate, keep y* names available).[rangeStart, rangeEnd)interval.Range-boundary reminder: 2.55 still uses the 2.x closed-closed
[start, end]range selector semantics, so this port requires no expected-value changes. Prometheus 3.0 flips to left-open(start, end](upstream issue prometheus#13213), which will require a re-examination ofyIncreaseat that cutover.Test plan
go test -count=1 ./promql/...passes on Go 1.26.2 (with and withoutREPLACE_RATE_FUNCS=y)invoca-2.53.1/add-yrateRelated
invoca-2.55.1/fix-xrate-extended-range→invoca-2.55.1-base.Once #21 lands on
invoca-2.55.1-base, this PR's base can be retargeted toinvoca-2.55.1-basedirectly.