Repository navigation
fix: absent_over_time is the series valued 1 at the steps without data, after a parser, under an aggregation and on Loki's step grid - #737
Conversation
⚪ Performance A/B: no change beyond noise264 shape×range — 264 ⚪ same · results vs Loki: 242 same, 0 new difference(s), 22 pre-existing Loki profiles compared (like for like):
Every shape is within noise of the base build (±30% and 100 ms). 264 unchanged shape×range (within noise)
Cold (first-run) timings — what a user opening a panel sees
† Loki's results cache answered this cold run (its own counters, or identical answers for windows a minute apart): not a computed time, so it is in no ratio. ↺ Loki cold taken from the first set that ran the same query; Loki's cache answered this one. 17 shape×range above 2× Loki already (listed, or as slow on the base build)
2 shape×range×mode where Loki did not answer 200: not compared (not a proxy defect). Why these shapes
base 🧭 New baseline candidateThis pull request got the baseline run. Every shape of every set was measured at the smoke's ranges (1h, 4 runs). Nothing was committed. The
The committed column is the newest daily run (7 runs, ranges up to 24h): the direction is comparable, the rows are not the same ones. Above-2× list: 182 rows after this run, 17 added, 0 not above 2× in this run's profile (the daily run's two-run rule decides for the committed list). A profile that did not run leaves its committed rows as they are. Added:
10 min. |
PR Quality ReportCompared against base branch Coverage and tests
Compatibility
Performance smokeLower CPU cost (
State
|
✅ Visual smoke: passed with warningsBase Fix proofCaptures of the exact cases this pull request adds or fixes (registry case, base | PR | Loki). The verdict compares the PR with Loki where the base did not match. The gate fails a case unless one capture is fixed or improved, or, beyond the Loki data window, the base failed where Loki and the PR both answered with data (the label does not excuse it; only
Regression check
Gate: a base-vs-PR data difference passes when the PR is closer to Loki, and fails when the PR diverges from Loki or Loki cannot decide it (at a range Loki does not hold, a difference passes when the same page proved that query to answer like Loki at a shorter range; otherwise label the pull request dd-fields-1h: history-dependent difference, not gated (1): resource /api/datasources/uid/*/resources/patterns?end=2026-10-29T15%3A08%3
dd-service-logs-1h: history-dependent difference, not gated (1): resource /api/datasources/uid/*/resources/patterns?end=2026-10-29T15%3A08%3
dd-field-pipeline-1h: history-dependent difference, not gated (1): resource /api/datasources/uid/*/resources/patterns?end=2026-10-29T15%3A08%3
dd-field-service_version-1h: history-dependent difference, not gated (1): resource /api/datasources/uid/*/resources/patterns?end=2026-10-29T15%3A08%3
dd-labels-1h: history-dependent difference, not gated (1): resource /api/datasources/uid/*/resources/patterns?end=2026-10-29T15%3A08%3
dd-labels-namespace-1h: history-dependent difference, not gated (1): resource /api/datasources/uid/*/resources/patterns?end=2026-10-29T15%3A08%3
dd-patterns-1h: history-dependent difference, not gated (1): resource /api/datasources/uid/*/resources/patterns?end=2026-10-29T15%3A08%3
explore-absent-over-time-6h: no Loki data at this range; the same differences match Loki at a shorter range (1): query ('absent_over_time({service_na
lvl-dd-field-pipeline-pill-1h: history-dependent difference, not gated (1): resource /api/datasources/uid/*/resources/patterns?end=2026-10-29T15%3A08%3
lvl-dd-label-pod-pill-1h: history-dependent difference, not gated (1): resource /api/datasources/uid/*/resources/patterns?end=2026-10-29T15%3A08%3
lvl-dd-logs-pill-logfmt-1h: history-dependent difference, not gated (1): resource /api/datasources/uid/*/resources/patterns?end=2026-10-29T15%3A08%3
lvl-dd-logs-pill-multi-json-1h: history-dependent difference, not gated (1): resource /api/datasources/uid/*/resources/patterns?end=2026-10-29T15%3A08%3
lvl-dd-logs-pill-otel-1h: history-dependent difference, not gated (1): resource /api/datasources/uid/*/resources/patterns?end=2026-10-29T15%3A08%3
All montages of this run: 🧭 New baseline candidateThis pull request got the baseline run: every page of |
14eb3b1 to
13e0f3b
Compare
|
Proof for current HEAD
Current |
13e0f3b to
e5ea6a4
Compare
817f6ff to
377c2c0
Compare
…a, after a parser, under an aggregation and on Loki's step grid Loki answers one series, labelled by the selector's single equality matchers, at each step whose window (t-range, t] holds no line the pipeline keeps, and nothing where it does. With a parser stage the proxy sent the query to the raw-row bare-parser evaluator and returned a series per stream and parsed label set; without one it read VictoriaLogs buckets as Loki windows, ignored a range that is not the step, stepped on an epoch grid, let older data hide an instant absence and answered the inner count under a vector aggregation. The presence of samples now comes from an ungrouped sum(count_over_time) (sum(rate) with unwrap) over VictoriaLogs stats, inverted on start, start+step, ... end, folded through vector aggregations as Loki folds one series; degraded answers are passed on, not inverted.
…s an unconvertible unwrap like rate, and says which route reads rows A start with milliseconds put the float-second presence points off the exact nanosecond step grid, which read every step as absent. The differential now starts a third of its scenarios on a millisecond. An unwrapped value Loki rejects now answers Loki's 400 pipeline error for absent_over_time, as for rate; the proxy test table and the e2e conversion-error cases cover it.
…ith a grouping, and on a warned answer A sort around absent_over_time keeps the series whole and a label_replace rewrites the absent series' labels instead of the inner count. A grouping on it is Loki's 400 parse error. A multi-tenant request is absent only where no tenant has a line, merged from each tenant's answer with no __tenant_id__ label, as Loki merges the tenants' samples before evaluating. A presence answer whose body carries a warnings array is passed on, not inverted.
Use the current label replacement helper for absent series after rebasing and regenerate quality summaries from saved evidence.
0e60eab to
f134ef8
Compare



















Summary
Gap
semantics/absent-over-time-semantics. On Loki 3.7.7,absent_over_timereturns one series valued 1 at each step whose window has no sample. Its labels come from the selector's equality matchers, at most one per name. It returns nothing at steps with data. Sources:pkg/logql/evaluator.go:647-664,:754-778,:1320-1345.Causes in the proxy:
absent_over_timewas taken by the raw-row bare-parser evaluator. It answered thousands of series where Loki answers none.[T, T+step)as Loki windows(T-range, T], so a tumbling answer was one step early.start.sum(absent_over_time(...))and the other vector aggregations returned the inner count.Fix (new
internal/proxy/absent_over_time.go):sum(count_over_time(...)), orsum(rate(...))with unwrap, so parser stages, label filters and Loki's windows are applied upstream.start + k*step, with the matcher-derived labels.Tests
TestAbsentOverTimeDifferential, against a Loki reference evaluator. It covers:TestAbsentOverTimeUnderVectorAggregation, with 14 aggregation forms.All of these failed before the fix.
e2e vs Loki:
test/e2e-compat/absent_over_time_test.go, with fixtures 21h back in two bursts separated by a gap.absent_over_timeis removed from the lists of conversion-error shapes that aren't detected: the registry row, KNOWN_ISSUES and the translation reference.Verification on rebased head
Rebased onto
mainat14e13dc6e5cea0222aa51f8bb06f67d6348b2889after the v2.46.0 release; all twelve PR commits are signed. Current head:effafa8e2e4ec297c9500eb4a5adac5df5b9dcda.TestAbsentOverTime*Go tests, all 155 visual harness tests, 307 CI configuration tests, andactionlintpassed. Full proxy and translator tests passed on the preceding HEAD before the release-only rebase (319.894 s and 8.096 s). On the prior 231-capture fix: absent_over_time is the series valued 1 at the steps without data, after a parser, under an aggregation and on Loki's step grid #737 artifact, the trusted v2.46.0 base renderer selected exactly 15 inline montages; the new workflow's bounded selector staged those 15 and the trusted v2.46.0 publisher accepted all. Tests cover foreign URLs, path traversal, symlinks and oversized files.image/png(30,552 bytes) from the published branch. The full baseline candidate remains in the run artifact.TestCompat_AbsentOverTime*e2e stack passed. The prior HEAD also passed visual smoke, performance A/B, and quality report. These are historical controls; the current-head jobs above are the acceptance proof.Main | PR | Loki visual comparison
Current capture from
main14e13dc6e5vs PReffafa8e2e. At 1h, the base showed “No data”; the PR and Loki show the absence series at 1. The image URL uses immutablepr-visualscommite610898290ddf9a89379e92a7b9b6357dde1c800.The current 15m and 6h montages are also published; the full CI visual comment embeds 18 unique comparison images.