Skip to content

feat(promql): lower histogram_quantiles as a per-φ Merge fan-out (#109) - #121

Merged
zzylol merged 1 commit into
mainfrom
feat/109-histogram-quantiles
Jul 10, 2026
Merged

zzylol merged 1 commit into
mainfrom
feat/109-histogram-quantiles

Conversation

@zzylol

@zzylol zzylol commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Part of #109 — the histogram_quantiles half. The non-literal scalar k/φ half is split out to #120.

Left open for review, not merged.

Change

histogram_quantiles(v, "label", φ₀, φ₁, …) is the experimental multi-quantile form: histogram_quantile(φᵢ, v) evaluated once per φ, each output series tagged with label = φᵢ. It parsed but was rejected as UnsupportedFunction (19 corpus lines).

It lowers to a Merge of one Relabel-wrapped quantile branch per φ:

Merge {
  Relabel { q = "0.5", Aggregate{[Quantile(0.5)]} over <v> },
  Relabel { q = "0.9", Aggregate{[Quantile(0.9)]} over <v> },
}

Each branch reuses the single-quantile decision — classic le-buckets interpolate (HistogramQuantile), native histograms and raw samples take the sketch-able Quantile (#43 / #79) — so the two functions cannot diverge. The choice is a property of the argument, not of φ, so it is made once.

This is the first front end to emit L2::Merge, previously marked reserved.

Two details that were easy to get wrong

Label formatting. Prometheus uses labels.FormatOpenMetricsFloat, not Go’s default: 1 → "1.0", 0 → "0.0", and %g exponent form below 1e-4 (0.00001 → "1e-05"). I read model/labels/float.go rather than guess; Rust’s Display would have produced 1, 0, and 0.00001.

Union compatibility. Each branch aliases its value column to value rather than the intent-keyed name (quantile_0_5, quantile_0_9, …). Merge derives its schema from the first child, so branches disagreeing on a column name would make the merged schema silently misdescribe every branch but one. My first version had exactly that bug — merged schema = ["quantile_0_5", "q"] while branch 2 was ["quantile_0_9", "q"]. The quantile belongs in the label, which is where Prometheus puts it.

Corpus

testdata lowered 1502 → 1512; rejected 86 → 76.

Of the 19 histogram_quantiles lines, 10 now lower. The 9 that remain are pre-existing, deliberate rejections and not regressions:

The coverage tripwire is ratcheted from 1485 to 1495, which is what its own comment asks for when coverage lands.

Tests (6 new)

test pins
histogram_quantiles_fans_out_over_native_histograms one branch per φ, sketch-able Quantile
histogram_quantiles_over_classic_buckets_interpolates _bucketHistogramQuantile, never a sketch
histogram_quantiles_branches_are_union_compatible the bug above
histogram_quantiles_uses_the_given_label_name "phi", not a hardcoded "q"
histogram_quantiles_formats_small_quantiles_like_prometheus 1e-05
histogram_quantiles_rejects_an_out_of_range_quantile one bad φ fails the call

Verification

cargo test --workspace      # 379 passed, 0 failed
cargo clippy --all-targets  # clean

cargo fmt --all not run — main carries pre-existing rustfmt diffs. Each touched file has the same violation count as on main.

🤖 Generated with Claude Code

`histogram_quantiles(v, "label", φ₀, φ₁, …)` is the experimental multi-quantile
form: `histogram_quantile(φᵢ, v)` evaluated once per φ, each output series
tagged with `label = φᵢ`. It parsed but was rejected as UnsupportedFunction.

Lower it to a `Merge` of one `Relabel`-wrapped quantile branch per φ. Each
branch reuses the single-quantile decision — classic `le`-buckets interpolate
(`HistogramQuantile`), native histograms and raw samples take the sketch-able
`Quantile` (#43 / #79) — so the two functions cannot diverge. The choice is a
property of the argument, not of φ, so it is made once.

Two details that are easy to get wrong:

- Label values use Prometheus's `labels.FormatOpenMetricsFloat`, not Rust's
  `Display`: `1 → "1.0"`, `0 → "0.0"`, and Go's `%g` exponent form below 1e-4
  (`0.00001 → "1e-05"`).
- Each branch aliases its value column to `value` instead of taking the
  intent-keyed name (`quantile_0_5`, `quantile_0_9`, …). `Merge` derives its
  schema from the first child, so branches that disagree on a column name would
  make the merged schema silently misdescribe every branch but one. The quantile
  is carried by the label column, which is where Prometheus puts it.

This is the first front end to emit `L2::Merge`, previously reserved.

Corpus: testdata lowered 1502 → 1512. The 9 remaining `histogram_quantiles`
lines are pre-existing deliberate rejections — 7 out-of-range φ (identical to
`histogram_quantile(1.001, …)`) and 2 `__name__` regex selectors (#67). The
coverage tripwire is ratcheted accordingly, per its own instructions.

The other half of #109 — non-literal scalar `k` / φ parameters — is split out
to #120: it needs a scalar subtree in the intent and at the L4 sketch readout.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@zzylol

zzylol commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

@milindsrivastava1997 @Selvomega Interesting "Merge" node.

@zzylol
zzylol merged commit cb2f354 into main Jul 10, 2026
1 check passed
@zzylol
zzylol deleted the feat/109-histogram-quantiles branch July 10, 2026 02:31
@milindsrivastava1997

Copy link
Copy Markdown
Collaborator

Not sure I understand. Can you give some context?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants