Skip to content

feat(promql): lower limitk / limit_ratio as series sampling (#86) - #95

Merged
zzylol merged 1 commit into
mainfrom
feat/86-limitk-limit-ratio-sampling
Jul 5, 2026
Merged

zzylol merged 1 commit into
mainfrom
feat/86-limitk-limit-ratio-sampling

Conversation

@zzylol

@zzylol zzylol commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Closes #86.

What

limitk(k, v) and limit_ratio(r, v) parsed but were rejected as UnsupportedAggregateOp (33 corpus rejections). They are series-sampling selection operators — they keep a subset of whole series unchanged (deterministic but unordered), not a ranking or a reduction — so they get a dedicated IR node rather than being forced into topk's Sort → Limit (which would rank by value and silently change which series survive).

How

L3 (asap-ir)

  • QueryExpr::Sample { by, kind, child } with SampleKind::{ LimitK(usize), LimitRatio(f64) }. Schema derivation is pass-through — a subset of whole series preserves the child's schema and row-uniqueness — so it's grouped with Filter/Sort/Limit. Added to the canonicalize child walk.

L2 (asap-l2)

  • matching Sample { keys, kind, input } node + walk/leaf_source/converter arms (keys resolve positionally; the child passes through). The binder seeds Sample.keys like group-by keys.

Front end (asap-frontend-promql)

  • T_LIMITKSample{ LimitK(k) } (count_param); T_LIMIT_RATIOSample{ LimitRatio(r) }. New ratio_param rejects non-finite ratios and clamps to [-1, 1] (Prometheus semantics); a negative ratio (complementary fraction) is preserved, not normalised away. Routed through the generic base construction — a bare selector is sampled directly (label-preserving, no reducing aggregate), a range-vector argument reduces per series first. limitk by (group) grouping works.

Honest partial support

A dynamic k/ratio (limitk(scalar(foo), …), limit_ratio(time() % 17 / 17, …)) isn't a compile-time constant, so num_param can't fold it — those stay rejected (as does limitk(NaN, …)), rather than being mislowered. Constant forms lower.

Tests

Conformance §S:

  • limitk/limit_ratioSample with the right SampleKind and a preserved (ts, value) schema.
  • negative-ratio complement kept; out-of-range magnitude clamped to [-1, 1].
  • limitk by (group) carries its grouping; count(limitk(2, v) and v) still lowers — the exact corpus case that turns on the identity of the surviving series (which topk would have broken).
  • dynamic / NaN params rejected.

Flipped the limitk_and_limit_ratio_are_rejected__GAP pin to positive. Full workspace suite green (corpus totality ratchet picks up the 33 newly-lowering queries); clippy clean.

`limitk`/`limit_ratio` parsed but were rejected (UnsupportedAggregateOp).
They are series-*sampling selection* operators — a subset of whole series kept
unchanged, deterministic-but-unordered — so they need a dedicated node, not
`topk`'s `Sort → Limit` (which would rank by value and change *which* series
survive).

L3 (asap-ir):
- `QueryExpr::Sample { by, kind, child }` + `SampleKind::{LimitK(k),
  LimitRatio(r)}`. Schema derivation is pass-through (a subset of whole series
  → the child's schema and row-uniqueness are preserved), grouped with
  Filter/Sort/Limit. Added to the `canonicalize` children walk.

L2 (asap-l2):
- `QueryExpr::Sample { keys, kind, input }` + walk/leaf_source arms and the
  converter arm (resolves `keys` positionally, passes the child through).
- Binder seeds the `Sample.keys` grouping labels, like group-by keys.

Front end (asap-frontend-promql):
- `T_LIMITK` → `Sample{LimitK(k)}` (k via `count_param`); `T_LIMIT_RATIO` →
  `Sample{LimitRatio(r)}` (`ratio_param` rejects non-finite and clamps to
  [-1, 1] like Prometheus; a negative ratio's complement is preserved). Routed
  through the generic base construction (label-preserving, no reducing
  aggregate) so a bare selector is sampled directly and a range-vector arg
  reduces per series first. `limitk by (group)` grouping works.

A dynamic k/ratio (`limitk(scalar(foo), …)`, `limit_ratio(time()%17/17, …)`)
isn't a compile-time constant, so `num_param` can't fold it — those stay
rejected, as does a NaN.

Tests: conformance §S — limitk/limit_ratio → Sample with the right kind and a
preserved schema; negative-ratio complement + out-of-range clamp; `limitk by`
grouping and preservation under `count(… and …)` (the corpus case that turns
on series identity); dynamic/NaN rejection. Flipped the
`limitk_and_limit_ratio_are_rejected__GAP` pin.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@zzylol
zzylol merged commit df02550 into main Jul 5, 2026
1 check passed
@zzylol
zzylol deleted the feat/86-limitk-limit-ratio-sampling branch July 5, 2026 16:04
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.

PromQL: limitk / limit_ratio series-sampling operators unsupported

1 participant