Skip to content

docs: clean up L3 QueryExpr; add schema flow + L4 SketchExpr + system I/O - #1

Merged
zzylol merged 10 commits into
mainfrom
docs/l3-ir-cleanup-and-schema
May 5, 2026
Merged

zzylol merged 10 commits into
mainfrom
docs/l3-ir-cleanup-and-schema

Conversation

@zzylol

@zzylol zzylol commented May 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Address review feedback on the L3 IR (QueryExpr + AggIntent) and add the missing input/output, schema, and system-I/O specs.

L3 deduplicated and made language-orthogonal

  • Drop TopK (= Sort + Limit), WindowedAgg (= Window + Aggregate), Dedup (renamed to Distinct{cols})
  • Move sketch-bound nodes out of L3: SketchAgg, JoinSketch now live in a new L4 IR SketchExpr
  • Drop language-specific nodes: HistogramQuantile, PromQLSubquery lower in PromQL L1→L2 instead
  • AggIntent loses TopK and QuantileOverTime (surrounding Window covers the latter)

New L4 sketch-bound IR SketchExpr

  • Adds SketchAgg, SketchJoin, SketchSubtract, SketchDelete, SketchEstimate, SketchMerge + Logical(QueryExpr) pass-through
  • L4 rule signature is now fn apply(&QueryExpr) -> Option<SketchExpr> — the L3/L4 boundary is type-enforced

Per-node input/output specs

  • New Schema / Field / HasSchema types — every L3 edge carries a typed schema
  • Per-node input/output table for all QueryExpr variants
  • Three-schemas subsection distinguishing DAG schema vs DB/source schema vs sketch catalog metadata

Sketch catalog metadata

  • SketchEntry expanded with mergeable, deletable, subtractable, accuracy, aggregated_keys, param_ranges, cost_model, supported_intents

System I/O contract

  • New §3 subsection: input is QueryWorkload, output is per-executor sub-DAG assignments
  • Premise: every executor (edge / gateway / backend / DataFusion session) has full capability; the controller's job is placement on the L4-bound DAG
  • Symbolic-vs-concrete-plan distinction made explicit (L3/L4 = symbolic, L5 = stage-assigned)

§12 Open questions

  • New "Resolved during the L3 IR cleanup" subsection answering each Slack question with the cleanup decision

Test plan

  • Doc renders cleanly on GitHub (markdown tables, code fences)
  • No stale references to removed nodes (TopK, WindowedAgg, JoinSketch, HistogramQuantile, PromQLSubquery, Dedup, QuantileOverTime) outside the cleanup-rationale tables — verified via grep before push
  • Cross-reference check: §3 layer table, §6 enums, §12 resolved-questions list all agree on which nodes live at which layer

🤖 Generated with Claude Code

zzylol and others added 10 commits May 2, 2026 10:28
… I/O

Address review feedback on the QueryExpr/AggIntent design:

- Drop redundant L3 nodes (TopK = Sort+Limit; WindowedAgg = Window+Aggregate)
- Move sketch-bound nodes (SketchAgg, JoinSketch) out of L3 into a new L4
  IR (SketchExpr) with SketchSubtract/Delete/Estimate/Merge added
- Drop language-shaped nodes from L3 (HistogramQuantile, PromQLSubquery
  lower in PromQL L1->L2 instead)
- Rename Dedup -> Distinct{cols} (SQL DISTINCT, generalised to N cols)
- Drop QuantileOverTime/TopK from AggIntent; surrounding Window covers it
- Add Schema/Field/HasSchema with per-node input/output spec table
- Distinguish three schemas: DAG schema vs DB/source schema vs sketch
  catalog metadata
- Expand SketchCatalog with mergeability, accuracy, supported intents,
  aggregated-key shape, parameter ranges
- New "System I/O contract" subsection: input is QueryWorkload, output
  is per-executor sub-DAG assignments under the premise that every
  executor has full capability and the controller chooses placement
- Refresh resolved-questions list in Section 12

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- TopK is a first-class AggIntent at L3 (heavy-hitter sketches like
  SpaceSaving / CMS-with-heap / Misra-Gries serve it as a single
  primitive). Generic QueryExpr::Sort + QueryExpr::Limit stays for
  non-heavy-hitter cases (ORDER BY name LIMIT 10). L1→L2→L3 lowering
  picks the intent form when it recognises a heavy-hitter pattern.
- Clarify CTE / let-binding "reuse edges" wording: one producer + N
  Ref(name) consumer edges, which is what makes the IR a DAG.
- Per-executor split: sub-tree -> sub-tree / sub-DAG.
- Cut-edge transports: drop Kafka mention, replace with
  "sketch-merge / compute-from-raw over the precompute engine".
- serialise -> serialize.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reframe "DAG, not tree" around the operational distinction: a producer
node can have multiple parents that share its precomputed intermediate
state. List the three fan-in sources: explicit CTE / recording-rule
references, L4 reuse rules introducing shared producers across queries,
and L4 stage / shard structure. The cost-model implication (credit
build cost once across consumers) is what makes the DAG shape a Pareto
win over a tree.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drop the S / L / R / S_i shorthand; spell out each input and output in
plain language so every row reads independently. Also split Sort and
Limit into separate rows now that each gets its own constraint
description (Sort requires keys' fields be present in input; Limit has
no field-level constraint).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a self-contained table covering the seven SketchExpr variants
(Logical pass-through, SketchAgg, SketchJoin, SketchSubtract,
SketchDelete, SketchEstimate, SketchMerge) with input and output
schemas, and introduces DataType::Sketch(SketchKind, SketchParams) as
the L4 field type that carries sketch state.

Two type-system invariants spelled out: sketch-family mismatch is a
plan-time error, and catalog capability flags (subtractable, deletable,
mergeable) gate which nodes can fire.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…glossary

- Rename scenario→deployment model across all three docs. Crate names get
  an asap- prefix (deployment-model-asaplifecycle / -asapquery / -asapfusion).
  "Scenario" understated the bundle; "deployment model" names the choice —
  rule selection + L5 topology + emitter all reflect deployment constraints.

- Distinguish stage from executor as first-class concepts. Stage (StageId)
  is a categorical lifecycle tier; Executor is a concrete runtime instance
  occupying a stage with its own id / address / capabilities. StageAllocator
  stays stage-granularity; PhysicalPlanner fans out per executor via
  DeploymentConstraints::executors().

- Rename §6 "Three distinct schemas" → "DAG schema, DB schema, sketch
  catalog — three distinct metadata sources" and clarify the sketch catalog
  is a registry of available primitives, not a schema.

- Add §14 glossary covering architecture roles, layer drivers, IR by
  layer, metadata sources, and workload identity. Renumber Success
  criteria to §15.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Naming was inverted: L3 was called "sketch algebra" but contains no
sketches (intent-only); L4 is where sketches actually get bound. Split
the module accordingly so the names match the contents:

- L3: `core::intent_algebra` — QueryExpr + AggIntent (intent only,
  no sketch type, no params).
- L4: `core::sketch_algebra` — SketchExpr (sketch-bound: kind + params
  committed). Promoted from a #### subsection of the old L3 section to
  a sibling ### section.

Touches the §3 layer table, §6 module sections, the directory tree,
the §14 glossary L3/L4 entries, and ~10 cross-references across
README.md, design.md, migration-plan.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…lanning

The previous doc comment described unique_keys as a "sketch-reuse driver"
without flagging that it's optional infrastructure for one specific
feature (`CostModel::workload_cost` cross-query reuse credit). Tighten
the comment to call out: who populates it (per-node spec), who reads it
(workload_cost only), and what doesn't read it (Bind* rules, push-down,
L5 emitters, single-query plans).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Worked example for `quantile_over_time(0.99, http_request_duration_seconds{service="api"}[5m])`
showing the L1→L5 transformation under each of the three deployment models, so readers can
see how the layered IR + topology-as-parameter design plays out concretely.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@zzylol
zzylol merged commit 2435250 into main May 5, 2026
@zzylol
zzylol deleted the docs/l3-ir-cleanup-and-schema branch May 5, 2026 18:23
zzylol added a commit that referenced this pull request May 27, 2026
…iguate joins (L2 review #1)

The issue-#7 qualifier fix reached scalar predicates but not the grouping
channels: `Aggregate.keys`, `TopK.by`, and `WindowFunc.partition_by` were plain
`Vec<String>`, and `expr_to_group_name` discarded the relation qualifier, so
`resolve_named_keys` resolved by first-match `column_id`. On a self-join,
`GROUP BY a.k` and `GROUP BY b.k` both bound to column 0 → `b.k` silently grouped
by `a.k` (wrong result). Same bug class #7 closed for predicates, still open for
keys.

Fix: the three group-key channels now carry `ColumnRef` (qualified-capable) like
the scalar path, and resolve via `resolve_column_refs` → `column_id_qualified`
with the bare-name fallback. `expr_to_group_ref` (SQL) preserves `col.relation`;
PromQL emits `ColumnRef::Named` (labels have no qualifier). The Binder seeds the
bare names; `resolve_named_keys` is removed (subsumed by `resolve_column_refs`).

Confirmed by a self-join test: `GROUP BY b.service` → `Aggregate.by = [5]`,
`GROUP BY a.service` → `[1]`. Full suite green (133), clippy + fmt clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
zzylol added a commit that referenced this pull request Aug 24, 2026
Restructure the flat 19-section developer guide into the three-part
structure the doc owner asked for: Part 1 - Code Architecture, Part 2
- Interfaces and Definitions, Part 3 - How to Add X, Y, Z (each ending
in how to verify). Content is moved, not rewritten:

Part 1 (Mental model first, per doc-owner follow-up, then a new
whole-PR architecture diagram, then "How the current pieces fit
together"):
- old #1 Mental model -> Part 1 #1
- new: whole-PR architecture diagram (TargetSubDAG's two entry points
  through ReplacementStrategy, PlanSpace/cost_sorted, explanation.rs,
  to a downstream consumer) -> Part 1 #2
- old #3 How the current pieces fit together -> Part 1 #3

Part 2:
- old Terminology's "Implementation" definition merged into the
  Glossary as one more entry (### Implementation), next to
  ReplacementStrategy
- old #2 Glossary -> Part 2 #1 (plus the merged Implementation entry
  and old #10 Matcher, retitled to match glossary-entry style)
- old #10 Matcher (implementation.rs) -> ### Matcher inside the
  Glossary; implementation.rs no longer exists, so the stale title
  is fixed
- old #19's definitional content (ReplacementExplanation/
  ExplanationKind shapes, node_hash, why there's no ExplanationRule
  trait, location-text ownership) -> Part 2 #2

Part 3:
- old #4, #5, #6, #7, #13, #14 -> Part 3 #1, Adding a new
  ReplacementStrategy (ending in Testing a new strategy)
- old #8, #9, #15 -> Part 3 #2, Adding or customizing a CostModel
  (ending in Testing a new cost model)
- old #12 -> Part 3 #3, Adding a new sketch algorithm, with its
  stale implementation.rs/binder references fixed to replacement.rs/
  construct_summary vocabulary, plus a new "Verifying a new sketch
  algorithm" close grounded in the existing coverage-matrix tests
- old #11, #16, #17, #18 -> Part 3 #4-#7 (capstone + closing
  reference material); #18's extension-map table's implementation.rs
  row fixed to replacement.rs
- old #19's "Using it"/"Adding a new kind" content -> Part 3 #8,
  Using and extending explanation.rs

cargo build --workspace --all-targets is clean (docs-only change).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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.

1 participant