You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Instant quantile by (host, job) next to windowed quantiles: no scan sharing, no panes, no ingest-time variant, 100-group default #840
No scan sharing. All three queries read data, but the only sharing offered is shared Scan (Q1, Q2). Should Q3's scan be shareable with theirs?
No panes. Q3 has no Pass 2 pane variant. Is that expected for an instant query?
No ingestion-time variant. Q3 repeats, is predictable, and reads ingesting data, yet Stage 2 offers no plan that maintains its state at ingestion time. Should it?
The 100-group default. Q3 is planned with 100 (host, job) groups, which isn't declared anywhere (series_count covers series only). Where does 100 come from, and should the workload be able to declare a grouping's cardinality?
Run with the same command and --max-displayed 100000. Stage 1 goes from 150 to 450 logical plans and Stage 2 from 294 to 882 physical plans. Like Q3, Q4 only adds its own choice of exact, KLL or DDSketch.
Q3 and Q4 don't share their scan. Both are instant scans of data with a 15 s lookback, and their Scan and TimeRange nodes appear identical. Even so, none of the 450 logical plans shares anything except shared Scan (Q1, Q2). Should Q3 and Q4 share these nodes?
Reproduced on 14394ada with the issue's workload and command, without and with Q4:
Workload
Logical plans
Physical plans
Selected plan
Cost per second
Q1–Q3
150
294
Q1 KLL · Q2 KLL · Q3 KLL
0.1622
Q1–Q4
450
882
Q1 KLL · Q2 KLL · Q3 KLL · Q4 KLL
0.1700
The only sharing in either run is shared Scan (Q1, Q2). Q3 and Q4 are both priced at 100 groups.
Q1 and Q5: no scan sharing with Q3 or Q4
Gap; will be fixed. The PromQL frontend projects into each Scan only the labels the query uses: Q1/Q2 read ts, value, $promql_series_identity, Q3 adds host, job, Q4 adds job. Structural CSE shares two nodes only when their schemas are equal, so these scans (and Q3's and Q4's instant TimeRange nodes above them) never match. Other rules already treat such scans as the same rows; the identical-expression rule doesn't yet.
Fix: widen each scan of a metric to the union of the labels its readers use, and add a Project above each reader to restore its own columns. Structural CSE then finds the shared scans and time ranges unchanged. This comes right after #839's Stage 2 fix (a shared scan built once at query time), in the same line of work. Before that fix a shared scan is charged once per reader, at the longest window, so sharing would only add plans that lose.
Q2: no panes for Q3
Intended. Q3 is an instant selector: it keeps only the latest sample of each series within the 15 s lookback. Panes split a range window into slices whose states merge; merging panes would keep every sample in the lookback, not only the latest per series, and change the answer. The reason will be stated in the window-composition rule's docs.
Q3: no ingestion-time variant for Q3
Intended. When a new sample arrives, it replaces that series' previous value in Q3's answer, and series drop out once they go stale. An ingestion-time summary per (host, job) would have to delete old values; KLL cannot, and a DDSketch could only next to a per-series last-value table, which is the raw store itself. The gain would also be small: Q3's whole query-time path costs about 0.009 per second of 0.17. Today the plan is rejected with the generic WindowNotFixed reason, which stage_pipeline does not show. It will get its own reason ("instant selector: the latest sample per series replaces earlier ones") and one sentence in the Stage 2 and planner-layering.md materialization docs.
Q4: the 100-group default
It is a placeholder: DEFAULT_GROUP_COUNT = 100 in plan selection, used for every by (...) with at least one label and for partitioned sorts and limits. Nothing in the workload can override it today.
Fix (PR in progress): the data workload declares the number of distinct values per label:
Stage 3 estimates the groups of by (K) as min(product of distinct_values over K, series_count). If a label in K is undeclared, it falls back to 100, capped at series_count.
Q6: no rollup from (host, job) to (job)
Out of the current scope; tracked in #719. The rollup rule handles exact accumulators (sum, count, min, max) only; sketches have no regrouping merge in the IR yet. #719 adds one, after the cardinality work in Q4, since a rollup cannot be priced fairly while both groupings count as 100 groups. For this workload the benefit would be small anyway: Q3 keeps no state between evaluations (Q3 above), and merging ten-sample KLLs per (host, job) costs about as much as inserting the samples directly.
New finding: exact grouped quantile plans don't compile
Every plan with Q3 or Q4 exact (490 of the 882) is rejected with executor_cannot_compile: aggregate intent has no native implementation. The executor's grouped aggregate has no exact quantile, so the exact baseline for quantile by (...) is never compared. #846 tracks adding it; a PR is in progress.
Q3's scan shared with Q1/Q2: fixed. Scans of one metric that project different labels can now be shared (feat(pass2): share scans of one metric that project different labels #868). In this workload the shared variant is offered but loses on cost (0.2068/s vs 0.1702/s), so Q1/Q2 keep their shared scan.
No panes for Q3: intended. An instant selector reads only the latest sample.
No ingestion-time variant for Q3: intended. A sketch can't drop a series' old value.
Activity
milindsrivastava1997 commentedon Oct 21, 2026
Adding Q4
quantile by (job) (0.5, data)Add this query to
repeating_queriesin the workload above:{"query": "quantile by (job) (0.5, data)", "demand": {"fixed_interval": 10000}, "requirements": {"accuracy": {"max_error": 0.01, "failure_probability": 0.001}}, "predictability": {"predictable": {"known_at": null}}}Run with the same command and
--max-displayed 100000. Stage 1 goes from 150 to 450 logical plans and Stage 2 from 294 to 882 physical plans. Like Q3, Q4 only adds its own choice of exact, KLL or DDSketch.datawith a 15 s lookback, and theirScanandTimeRangenodes appear identical. Even so, none of the 450 logical plans shares anything exceptshared Scan (Q1, Q2). Should Q3 and Q4 share these nodes?jobto answer Q4, but no plan does that. Should the summary-capability rule offer it? fix: one finest-grain stored summary serves several groupings (quantiles at the estimate level, sums at the state level) #804 covers stored summaries, but Q3 has no plan that stores state at ingestion time (question 3).zzylol commentedon Oct 21, 2026
Reproduced on
14394adawith the issue's workload and command, without and with Q4:The only sharing in either run is
shared Scan (Q1, Q2). Q3 and Q4 are both priced at 100 groups.Q1 and Q5: no scan sharing with Q3 or Q4
Gap; will be fixed. The PromQL frontend projects into each
Scanonly the labels the query uses: Q1/Q2 readts, value, $promql_series_identity, Q3 addshost, job, Q4 addsjob. Structural CSE shares two nodes only when their schemas are equal, so these scans (and Q3's and Q4's instantTimeRangenodes above them) never match. Other rules already treat such scans as the same rows; the identical-expression rule doesn't yet.Fix: widen each scan of a metric to the union of the labels its readers use, and add a
Projectabove each reader to restore its own columns. Structural CSE then finds the shared scans and time ranges unchanged. This comes right after #839's Stage 2 fix (a shared scan built once at query time), in the same line of work. Before that fix a shared scan is charged once per reader, at the longest window, so sharing would only add plans that lose.Q2: no panes for Q3
Intended. Q3 is an instant selector: it keeps only the latest sample of each series within the 15 s lookback. Panes split a range window into slices whose states merge; merging panes would keep every sample in the lookback, not only the latest per series, and change the answer. The reason will be stated in the window-composition rule's docs.
Q3: no ingestion-time variant for Q3
Intended. When a new sample arrives, it replaces that series' previous value in Q3's answer, and series drop out once they go stale. An ingestion-time summary per (host, job) would have to delete old values; KLL cannot, and a DDSketch could only next to a per-series last-value table, which is the raw store itself. The gain would also be small: Q3's whole query-time path costs about 0.009 per second of 0.17. Today the plan is rejected with the generic
WindowNotFixedreason, which stage_pipeline does not show. It will get its own reason ("instant selector: the latest sample per series replaces earlier ones") and one sentence in the Stage 2 andplanner-layering.mdmaterialization docs.Q4: the 100-group default
It is a placeholder:
DEFAULT_GROUP_COUNT = 100in plan selection, used for everyby (...)with at least one label and for partitioned sorts and limits. Nothing in the workload can override it today.Fix (PR in progress): the data workload declares the number of distinct values per label:
Stage 3 estimates the groups of
by (K)asmin(product of distinct_values over K, series_count). If a label inKis undeclared, it falls back to 100, capped atseries_count.Q6: no rollup from (host, job) to (job)
Out of the current scope; tracked in #719. The rollup rule handles exact accumulators (sum, count, min, max) only; sketches have no regrouping merge in the IR yet. #719 adds one, after the cardinality work in Q4, since a rollup cannot be priced fairly while both groupings count as 100 groups. For this workload the benefit would be small anyway: Q3 keeps no state between evaluations (Q3 above), and merging ten-sample KLLs per (host, job) costs about as much as inserting the samples directly.
New finding: exact grouped quantile plans don't compile
Every plan with Q3 or Q4 exact (490 of the 882) is rejected with
executor_cannot_compile: aggregate intent has no native implementation. The executor's grouped aggregate has no exact quantile, so the exact baseline forquantile by (...)is never compared. #846 tracks adding it; a PR is in progress.Order
fix(executor): compile an exact instant quantile by (G) to the groupe…
fix(executor): answer a NaN quantile φ as Prometheus; address review
feat(plan-selection): price a grouping from declared per-label distin…
fix(executor): compile an exact instant quantile by (G) to the groupe…
feat(plan-selection): price a grouping from declared per-label distin…
feat(plan-selection): price groupings from declared per-label distinc…
feat(pass2): share scans of one metric that project different labels
feat(pass2): share scans of one metric that project different labels (#…
test(devtools): the #840 workload selects Q4's KLL rolled up from Q3's
test(devtools): the #840 workload selects Q4's KLL rolled up from Q3's
test(devtools): the #840 workload selects Q4's KLL rolled up from Q3's
feat(plan-selection): memo over logical sub-expressions with a brute-…
zzylol commentedon Oct 23, 2026
Status of the six questions on main:
Also found here: exact
quantile byplans did not compile. That is fixed by #847 (issue #846).plan-selection: memo MILP stays a lower bound with shared slots and w…
plan-selection: memo MILP lower bound across groups and rollups
feat(plan-selection): experimental MILP search over the sub-expressio…