Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
cc1d782
fix: sum without over an unwrap range function keeps a series per rem…
szibis Oct 12, 2026
d15e523
fix: apply the series limit to the folded answer of a without aggrega…
szibis Oct 12, 2026
40f9ff1
test(bench): A/B set, visual page and registry proofs for sum without…
szibis Oct 12, 2026
5ae4117
test(conformance): register three open shapes found around sum withou…
szibis Oct 12, 2026
934964c
test: an instant aggregation that fails after VictoriaLogs answers ke…
szibis Oct 12, 2026
39f83d2
chore(conformance): record #739 as the fix of unwrap-sum-without-coll…
szibis Oct 12, 2026
3eb06cb
fix: a without aggregation is folded with its own operator
szibis Oct 12, 2026
b950024
docs: trim the sum without changelog entry and register the operator …
szibis Oct 12, 2026
7986af8
fix: keep the tenant limit for an ungrouped sum fold and pin the fold…
szibis Oct 12, 2026
d104eea
docs: changelog entry for the sum without fix
szibis Oct 17, 2026
738f001
chore(conformance): regenerate the registry reports on the rebased br…
szibis Oct 17, 2026
64fae64
test(bench): A/B results of sum without on the rebased branch, replac…
szibis Oct 17, 2026
4e9de52
chore(conformance): refresh the perf evidence and drop the instant-fa…
szibis Oct 17, 2026
dc63838
test(bench): Grafana pages for sum without over rate, topk and count_…
szibis Oct 17, 2026
216f4a3
test(bench): a benchmark of the sum without shapes through the handlers
szibis Oct 17, 2026
d6a282a
fix: the sliding window evaluator leaves the tenant limit to the fold…
szibis Oct 17, 2026
3aa6130
fix: fold a without only for the aggregations the fold proves, when i…
szibis Oct 17, 2026
cc5c383
fix: tell the row-budget error of a without request by the request, n…
szibis Oct 17, 2026
3909a43
test: pin the label a label_replace adds, a comparison that drops ser…
szibis Oct 17, 2026
6e8fce5
test(conformance): register the shapes around without that still diff…
szibis Oct 17, 2026
07b8760
test: pin stddev and stdvar around a folded without
szibis Oct 17, 2026
ba4b37c
docs: narrow the changelog entry of the sum without fix to the wrappe…
szibis Oct 17, 2026
57194e6
fix: tell the series of a without over an unwrap by the labels a pars…
szibis Oct 17, 2026
6bf019c
test: pin the tenant series limit of the busiest-values ranking and t…
szibis Oct 17, 2026
4e32643
docs: the row-limit error of a without is bounded by the tenant's eff…
szibis Oct 17, 2026
d88e5c0
test(bench): prove the sum without fix with an extraction list, which…
szibis Oct 17, 2026
d34a045
test(conformance): reproduce the open rows around without on generato…
szibis Oct 17, 2026
72a7f78
test: pin topk over a folded without against Loki, and max over it as…
szibis Oct 17, 2026
c6b77b8
test(conformance): register the instant detected_level difference, a …
szibis Oct 17, 2026
ee10d7d
test(conformance): pin every query that is not a folded without to it…
szibis Oct 17, 2026
a34b806
test(bench): measure the sum without shapes with an extraction list o…
szibis Oct 17, 2026
98c963c
test(bench): keep the sum without pages whose answers Loki matches
szibis Oct 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Fixed

- **`sum without (...)` over a range function with `unwrap`, and over `rate` at
an instant, keeps a series per remaining stream label set instead of folding
every series into one.** `sum without (pod) (max_over_time({app="payment-service"}
| logfmt amount | unwrap amount [5m]))` answered one series on query_range and on an
instant query, where Loki 3.7.7 keeps every label but `pod` and folds each
group (`pkg/logql/evaluator.go:451-466`). The last stats pipe now keeps the
stream, VictoriaLogs answers a row per stream from stats, and the proxy drops
the excluded labels and folds the rows with the operator of the aggregation
(`sum`, `max`, `min`, `count` or `avg`, when it is the outermost aggregation of
the query). A `topk`, `bottomk`, `sort`, `stddev` or `stdvar` around it is
applied to the folded series, and so is a comparison around it and, on
query_range, a `label_replace` (on an instant query the label is not added:
`semantics/label-replace-instant-vector`); a binary expression folds each
operand on its own. `topk`, `bottomk`, `stddev` and `stdvar` with a `without`
clause, and a `without` under another aggregation, are answered as before.
`max_query_series` now applies to the folded answer, as on Loki
(`pkg/logql/engine.go:536-548`): four streams folded into two series fit a
limit of two, a plain client over the limit gets Loki's 400, and Logs
Drilldown gets the busiest series with Loki's warning; the per-stream rows read
before the fold are bounded by `-manual-range-metric-row-limit` (when it is not
below the tenant's effective `max_query_series`), whose error names that flag
and answers 400. For this fold only, a parser that names the keys it extracts
(`| logfmt amount`, `| logfmt amount, route`) makes the series of a stream the
stream plus those labels, as on Loki, so the unique values in a line no longer
split it: a `sum without (pod)` over `max_over_time` of such a pipeline was a
sum of per-line maxima. A bare `| logfmt` or `| json`, which extracts every key
the proxy does not expose, and a key that is also a label of the stream
selector (Loki names it `<key>_extracted`) keep the previous grouping. Every
other query, with or without an extraction list, is translated and answered
as before; what differs from Loki there is registered with
`semantics/unwrap-extraction-list-series-identity`,
`semantics/unwrap-sum-without-collapses` (fixed, #739) and the rows it names.

## [2.17.0] - 2026-10-17

### Performance
Expand Down
333 changes: 333 additions & 0 deletions bench/ab/results/2026-10-17-unwrapwithout-1h.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,333 @@
{
"label": "unwrapwithout-1h",
"date": "2026-10-17",
"set": "unwrap-sum-without",
"description": "`sum without` over a range function with unwrap (PR for semantics/unwrap-sum-without-collapses): Loki keeps a series per remaining label set; the last stats pipe had no grouping and VictoriaLogs answered one row, so the proxy answered one series. W1-W4 are range queries over a logfmt stream group (max_over_time, sum_over_time, rate, max without), parsed with `| logfmt amount` so a Loki series is the stream (a bare `| logfmt` makes every payment line a series of its own on Loki: thousands of series, and Loki cannot answer within the timeout; semantics/unwrap-per-series-identity-raw-rows), W5-W6 are instant, W7 wraps the aggregation in topk, W8 counts a stream-label function whose series limit now applies to the folded answer, W9 is the label_replace instant control that must not move. A sum without over an unwrap with no parser is left out: no field of the generator's data is numeric without a parser, so every build answers an empty result. Run with the control set.",
"baseline": "base",
"candidate": "pr",
"reference": "loki",
"runs": 5,
"valid": true,
"restart_before": "0",
"restart_after": "0",
"noise": 0.3,
"timing": "warm",
"verdicts": {
"same": 9
},
"result_differs_from_reference": 5,
"result_differs_preexisting": 5,
"stale_answers": {
"base": 0,
"pr": 0,
"loki": 0
},
"rows": [
{
"shape": "W1 sum without pod, max_over_time",
"range": "1h",
"p50": {
"base": 0.015,
"pr": 0.015,
"loki": 0.017
},
"p95": {
"base": 0.023,
"pr": 0.016,
"loki": 0.017
},
"cold": {
"base": 0.042,
"pr": 0.024,
"loki": 0.079
},
"status": {
"base": "200",
"pr": "200",
"loki": "200"
},
"vl_cpu_max": {
"base": 39.36,
"pr": 39.36,
"loki": 39.36
},
"parity_vs_reference": "differs",
"baseline_parity_vs_reference": "differs",
"parity_vs_baseline": "differs",
"verdict": "same",
"speedup": 1.0
},
{
"shape": "W2 sum without pod, sum_over_time",
"range": "1h",
"p50": {
"base": 0.015,
"pr": 0.017,
"loki": 0.009
},
"p95": {
"base": 0.017,
"pr": 0.017,
"loki": 0.011
},
"cold": {
"base": 0.019,
"pr": 0.016,
"loki": 0.059
},
"status": {
"base": "200",
"pr": "200",
"loki": "200"
},
"vl_cpu_max": {
"base": 14.6,
"pr": 14.6,
"loki": 14.6
},
"parity_vs_reference": "differs",
"baseline_parity_vs_reference": "differs",
"parity_vs_baseline": "differs",
"verdict": "same",
"speedup": 0.88
},
{
"shape": "W3 sum without pod, rate",
"range": "1h",
"p50": {
"base": 0.054,
"pr": 0.05,
"loki": 0.01
},
"p95": {
"base": 0.058,
"pr": 0.057,
"loki": 0.013
},
"cold": {
"base": 0.06,
"pr": 0.06,
"loki": 0.031
},
"status": {
"base": "200",
"pr": "200",
"loki": "200"
},
"vl_cpu_max": {
"base": 13.76,
"pr": 13.76,
"loki": 13.76
},
"parity_vs_reference": "same",
"baseline_parity_vs_reference": "same",
"parity_vs_baseline": "same",
"verdict": "same",
"speedup": 1.08
},
{
"shape": "W4 max without pod, max_over_time",
"range": "1h",
"p50": {
"base": 0.015,
"pr": 0.016,
"loki": 0.009
},
"p95": {
"base": 0.016,
"pr": 0.016,
"loki": 0.009
},
"cold": {
"base": 0.012,
"pr": 0.016,
"loki": 0.032
},
"status": {
"base": "200",
"pr": "200",
"loki": "200"
},
"vl_cpu_max": {
"base": 13.2,
"pr": 17.41,
"loki": 17.41
},
"parity_vs_reference": "differs",
"baseline_parity_vs_reference": "differs",
"parity_vs_baseline": "differs",
"verdict": "same",
"speedup": 0.94
},
{
"shape": "W5 sum without pod, max_over_time, instant",
"range": "instant",
"p50": {
"base": 0.005,
"pr": 0.004,
"loki": 0.009
},
"p95": {
"base": 0.006,
"pr": 0.005,
"loki": 0.01
},
"cold": {
"base": 0.004,
"pr": 0.004,
"loki": 0.009
},
"status": {
"base": "200",
"pr": "200",
"loki": "200"
},
"vl_cpu_max": {
"base": 17.41,
"pr": 17.41,
"loki": 17.41
},
"parity_vs_reference": "same",
"baseline_parity_vs_reference": "differs",
"parity_vs_baseline": "differs",
"verdict": "same",
"speedup": 1.25
},
{
"shape": "W6 sum without pod, rate, instant",
"range": "instant",
"p50": {
"base": 0.004,
"pr": 0.004,
"loki": 0.007
},
"p95": {
"base": 0.005,
"pr": 0.006,
"loki": 0.008
},
"cold": {
"base": 0.004,
"pr": 0.004,
"loki": 0.008
},
"status": {
"base": "200",
"pr": "200",
"loki": "200"
},
"vl_cpu_max": {
"base": 2.99,
"pr": 2.99,
"loki": 2.99
},
"parity_vs_reference": "same",
"baseline_parity_vs_reference": "differs",
"parity_vs_baseline": "differs",
"verdict": "same",
"speedup": 1.0
},
{
"shape": "W7 topk 5 of sum without pod",
"range": "1h",
"p50": {
"base": 0.015,
"pr": 0.015,
"loki": 0.009
},
"p95": {
"base": 0.016,
"pr": 0.018,
"loki": 0.01
},
"cold": {
"base": 0.014,
"pr": 0.013,
"loki": 0.036
},
"status": {
"base": "200",
"pr": "200",
"loki": "200"
},
"vl_cpu_max": {
"base": 13.36,
"pr": 17.12,
"loki": 17.12
},
"parity_vs_reference": "differs",
"baseline_parity_vs_reference": "differs",
"parity_vs_baseline": "differs",
"verdict": "same",
"speedup": 1.0
},
{
"shape": "W8 sum without pod, count_over_time (production)",
"range": "1h",
"p50": {
"base": 0.35,
"pr": 0.35,
"loki": 40.002
},
"p95": {
"base": 0.418,
"pr": 0.398,
"loki": 40.002
},
"cold": {
"base": 0.388,
"pr": 0.387,
"loki": 40.002
},
"status": {
"base": "200",
"pr": "200",
"loki": "0"
},
"vl_cpu_max": {
"base": 17.12,
"pr": 27.16,
"loki": 27.16
},
"parity_vs_reference": "differs",
"baseline_parity_vs_reference": "differs",
"parity_vs_baseline": "same",
"verdict": "same",
"speedup": 1.0
},
{
"shape": "W9 label_replace control, instant",
"range": "instant",
"p50": {
"base": 0.003,
"pr": 0.003,
"loki": 0.038
},
"p95": {
"base": 0.004,
"pr": 0.003,
"loki": 0.044
},
"cold": {
"base": 0.003,
"pr": 0.002,
"loki": 0.034
},
"status": {
"base": "200",
"pr": "200",
"loki": "200"
},
"vl_cpu_max": {
"base": 1.89,
"pr": 1.89,
"loki": 1.89
},
"parity_vs_reference": "same",
"baseline_parity_vs_reference": "same",
"parity_vs_baseline": "same",
"verdict": "same",
"speedup": 1.0
}
]
}
Loading
Loading