feat(plan): L3→L4 binding pass + sketch-vs-exact boundary decision (#98) - #100
Merged
Merged
Conversation
Implements the two halves of #98 in asap-plan: - boundary.rs — the per-intent sketch-vs-exact decision. One exhaustive AggIntent → Realization match (a new intent cannot compile without an explicit decision): Quantile→KLL (DDSketch candidate), Cardinality→HLL (Theta/KMV candidates), TopK→CMS-with-heap, approximate Count→CMS; exact mergeable accumulators for Sum/Count/Min-Max/Rate/Increase; explicit pass-through for the non-mergeable reducers (Avg/StdDev/ Variance), classic-bucket HistogramQuantile (#79: not re-sketchable), and the ~30 per-series transform intents. Sketch parameters are sized by inverting each family's error bound against the AccuracyTarget (ε=0.01 → KLL k=200; default_cardinality → HLL p=14; δ → CMS depth). - bind.rs — the binding pass. Walks the L3 QueryExpr, fires the boundary decision per node over nested aggregate spines, and emits the L4 SummaryExpr/L4Node DAG: sketches become SummaryAgg + SummaryEstimate readout (the Sketch(kind, params) column type stops at the estimate), exact accumulators a bare SummaryAgg, everything else Logical(...). Schemas reuse the canonical L3 derivation with the summary state column retyped, so per-series label preservation and output_names overrides carry over for free. Conservative fallbacks (logical parents, multi-intent nodes, HAVING) are documented; rewriting through logical operators is the rule engine's job (#6/#33). Tests: the AggIntent→SummaryKind coverage matrix (all ~44 variants pinned), boundary decisions per accuracy target, bind-pass unit tests, and an end-to-end PromQL-string→L4 pin in asap-e2e (quantile(0.99, rate(m[5m])) → Estimate ∘ SummaryAgg(KLL) ∘ SummaryAgg(Rate)). Closes #98 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
zzylol
force-pushed
the
feat/98-l4-binding-boundary
branch
from
July 6, 2026 00:28
385c065 to
f565309
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #98.
Implements the L3→L4 binding and the sketch-vs-exact boundary in
asap-plan(which now depends onasap-sketch— L3 in, L4 out, still no front-end dependency).boundary.rs— the per-intent accuracy decisionrealize(&AggIntent) -> Realizationis one exhaustive match over the intent vocabulary, so a newAggIntentvariant cannot compile without an explicit decision — no silent fall-through, per the issue's coverage requirement. Three outcomes:Sketch { kind, params }— for the approximate-capable intents under anEpsilon/EpsilonDeltatarget, per the issue's map:Quantile→KLL (DDSketch as the listed alternate),Cardinality→HLL (Theta/KMV alternates),TopK→CMS-with-heap, approximateCount→CMS.sketch_candidates()keeps the full per-intent candidate list in one place for the future cost model (Wire workload-level CSE into a cost model #6/Add logic to detect which optimizations are applicable to a query workload #33);realizebinds the head.ExactAccumulator { kind, params }— the mergeable exact summaries:Sum/Count(Exact)/Min+Max→MinMax/Rate/Increase. Gated onagg_is_mergeable(the previously caller-less helper —Avg/StdDev/Varianceneed richer partial state and pass through).PassThrough— everything with no summary form, explicitly: exact quantile/top-k/cardinality, classic-bucketHistogramQuantile(PromQL: histogram_quantile classic-bucket vs sketch-able quantile is a structural heuristic — drive it from sample type/metadata #79 — pre-aggregatedlebuckets are not re-sketchable; the genericQuantilepath is),Group/CountValues, and the per-series transform families (PromQL: native-histogram accessor functions (histogram_*) unsupported #43–PromQL: presence functions (absent/absent_over_time/present_over_time) unsupported #47, PromQL: extended aggregation operators (limitk/limit_ratio/count_values/group) unsupported #49, PromQL: long-tail functions (extra *_over_time reducers, sort family, min_of/max_of) unsupported #51).Parameters are sized by inverting each family's error bound against the
AccuracyTarget: ε=0.01 → KLL k=200 (matching the design-doc worked example),default_cardinalityinverts back to HLL precision 14 exactly, δ→CMS depth⌈ln 1/δ⌉(0.01 → 5), with aDEFAULT_DELTAfor ε-only targets and saturation for degenerate ε.bind.rs— the binding passbind(&QueryExpr) -> Rc<L4Node>(plusbind_inwith an explicitBindingScopefor CSE'd roots) walks the L3 tree and fires the boundary decision per node over nested aggregate spines:SummaryAgg(kind+params committed,colresolved frominput_col()orSampleValue,bycarried) wrapped in theSummaryEstimatereadout — theSketch(kind, params)column type appears on the agg edge and stops at the estimate, per the L4 type invariant;SummaryAgg(its state ≡ the value; no readout query exists or is needed);Logical(...)with the schema lifted to all-Primitive.Edge schemas reuse the canonical L3
output_schemaderivation with only the summary-state column retyped, so per-series label preservation (rate),output_namesoverrides, and time-index tracking carry over for free.Conservative fallbacks (documented in the module docs):
SummaryExpr::Logicalboxes a whole L3 subtree with no L4 children, so a logical operator above a bindable aggregate (Filter/BinaryOpover a quantile) subsumes it unbound; multi-intentAggregates andHAVINGalso stay logical. Rewriting through logical parents is the L4 rule engine's job (#6/#33), not this pass's.Tests
boundary.rs): all ~44 intent variants pinned to their category, plus assertions that every accumulator pick isagg_is_mergeableand every sketch pick is on a genuinely approximate target.Exactvs ε=0.01 vs ε=0.05 → three decisions; HLL p=14 inversion; (ε,δ) CMS sizing; TopK heap=k; degenerate-ε saturation.asap-e2e/tests/l4_binding.rs):quantile(0.99, rate(http_requests_total[5m]))from query string →Estimate ∘ SummaryAgg(Kll{200}) ∘ SummaryAgg(Rate) ∘ Logical(TimeRange→Scan), node by node with edge types.Docs: reconciled the four stale "#98 is tracked/stub" references in
design.mdand theasap-plancrate docs.Verified:
cargo test --workspace(326 tests, 0 failures),cargo clippy --all-targetsclean.🤖 Generated with Claude Code