Skip to content

feat(devtools): add interactive SQL/PromQL Pre/Post-ASAP workload planner and DAG viewer - #283

Merged
zzylol merged 28 commits into
mainfrom
feat/dag-post-asap-viz
Aug 26, 2026
Merged

zzylol merged 28 commits into
mainfrom
feat/dag-post-asap-viz

Conversation

@zzylol

@zzylol zzylol commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds an interactive SQL/PromQL workload planner and DAG viewer for comparing the original pre-ASAP IR with the selected post-ASAP plan.

The browser can edit queries and table schemas, run the real planner pipeline, visualize one query or a multi-query workload union, and inspect the exported IR, schemas, sharing decisions, and replacement strategies without reconstructing planner intent in JavaScript.

Purpose of the demo

This demo is intended to serve three related purposes:

  1. Develop new replacement strategies. Strategy authors can add representative SQL/PromQL workloads, run the complete planner, and verify where a new strategy matches, what candidate it produces, how it composes with other replacements, and whether workload sharing is preserved in the final post-ASAP DAG.
  2. Debug planning and mapping. Developers can compare pre-ASAP and post-ASAP IR, inspect exact node and edge schemas, follow explicit target/decision mappings, read the selected strategy rationale and cost, and see phase timings without guessing from logs or frontend-generated signatures.
  3. Explain ASAP mappings to users. The same visualization can potentially be used as a user-facing explanation of which parts of a query are mapped to ASAP primitives—such as sketches, shared summaries, rollups, grouping structures, and semantic rewrites—and why the planner selected those implementations.

Planner and export model

  • Adds dag_export --post-asap and runs parsing/lowering, pre-ASAP DAG generation, ASAP-aware replacement search and cost selection, and post-ASAP DAG generation.
  • Exports a complete graph and post_graph for every named query, plus ranked per-target replacements for diagnostics.
  • Preserves concrete IR content in JSON: sources and predicates, projections, aggregate measures and reductions/group-by keys, sort keys, limits, schemas, and post-ASAP summary details.
  • Attaches an explicit decision to every post-ASAP node produced or carried by a selected replacement. It contains the real strategy name, rationale, rank, estimated cost, role, and workload-level decision ID.
  • Links post-ASAP nodes to pre-ASAP targets through exported decision.id / replacement.decision_id metadata. The viewer does not infer a replacement from labels, hashes, or client-side node signatures.
  • Assigns workload_node_id in the exporter. Pre/post workload unions and shared-node highlighting consume this ID directly, so shared work is represented by one workload node.
  • Preserves shared scans and replacement sub-DAGs when constructing the merged post-ASAP graph.
  • Reports elapsed time for parsing/lowering, pre-ASAP generation, ASAP-aware mapping, post-ASAP generation, and the complete planner run.

Replacement strategies

  • Uses the names declared by the actual replacement implementations, including SketchAlgorithmStrategy, SharedSubtreeStrategy, HydraGroupingStrategy, AvgToSumOverCountStrategy, RollupStrategy, and TopKLimitReuseStrategy.
  • Adds TopKLimitReuseStrategy for compatible top-k queries over the same ordered input. A smaller result can be derived from a larger selected limit, such as Limit(5) -> Limit(10), while retaining the shared ordered/summary input.
  • Skips trivial top-level KeepPreAsap winners when splicing the post-ASAP graph, preventing recursive no-op substitution.

Interactive viewer

  • Adds a local Python HTTP server with a real /api/plan backend; it invokes dag_export with an argv array rather than a shell command.
  • Adds an editable query and table-schema UI with built-in metrics and hosts schemas, per-query schema selection, custom tables/columns/nullability/time indexes, SQL binding, and inferred PromQL metric schemas.
  • Uses one Pre/Post-ASAP visualization:
    • selecting one query shows that query's complete pre/post DAGs;
    • selecting multiple queries shows the unioned pre/post workload DAGs.
  • Keeps pre-ASAP nodes limited to the original IR. Strategy explanations appear only on post-ASAP nodes.
  • Shows compact, text-first DAG cards without symbolic operator icons. Aggregate cards retain measures and grouping reductions; Project cards retain output columns; the sidebar shows the lossless node detail.
  • Clicking a post-ASAP node shows its selected strategy, role, rationale, rank/cost, and explicit pre-ASAP target mapping.
  • Clicking an edge shows its schema, source and target nodes, target operation, and how the operation derives its output schema.
  • Shows human-readable input schemas for the selected workload and provides a horizontally resizable detail sidebar.
  • Loads the real-planner demo at startup and preselects a root node so DAG and schema details are immediately visible.
  • Removes the obsolete Single/Compare/Union code paths and legacy icon/schema-label code. Merged workload nodes aggregate explicit decision metadata from every owning query.
  • Supports standalone HTML generation through tools/dag-viewer/render.py.

Bundled workload

The generated demo covers five queries:

  • q1: grouped COUNT(*) over metrics.
  • q2: grouped AVG(latency), exercising AvgToSumOverCountStrategy and sketch mapping while sharing the metrics input with q1.
  • q3: topk(5, rate(http_requests_total[5m])).
  • q4: topk(10, rate(http_requests_total[5m])); q3 derives its smaller limit from q4's larger limit, and both share the ordered summary sub-DAG.
  • q6: a real metrics JOIN hosts grouped-count query whose exported workload mapping shares Scan(metrics) with the other compatible SQL queries.

dag.example.json is generated from these real queries through dag_export; it is not manually edited viewer output.

Run

python3 tools/dag-viewer/server.py

Open the URL printed by the server, normally http://127.0.0.1:8000. When the server runs on a remote host, forward that port over SSH or through the editor's Ports panel.

To produce a directly-openable standalone page:

python3 tools/dag-viewer/render.py tools/dag-viewer/dag.example.json -o /tmp/asap-dag.html

Verification

  • ASAP-aware mapping and exporter unit tests, including post-ASAP substitution, workload IDs, join sharing, and top-k reuse.
  • Python renderer tests for semantic node labels, embedded workloads, and standalone HTML generation.
  • Rust formatting and Clippy checks.
  • Browser tests covering startup, single-query and multi-query unions, node/edge details, sidebar resizing, and interactive /api/plan submission.

zzylol and others added 4 commits August 25, 2026 20:11
…ost-asap

Add a --post-asap flag to the dag_export devtools binary that runs
asap-aware-mapping's replacement search (default_strategies() plus the
unwired AvgToSumOverCountStrategy) over the lowered workload and surfaces
what it found, two ways:

- NamedGraph.replacements: Vec<TargetReplacement> - one small,
  self-contained before/after pair per independently-discovered
  replacement site (sketch mapping, CSE share/recompute, workload-aware
  rollup, Hydra grouping, avg -> sum/count rewrite), each with a strategy
  label, rationale, rank/cost, and an after tagged
  {"kind": "Summary"|"Rewrite", "graph": {...}}.
- NamedGraph.post_graph: Option<DagGraph> - one merged, whole-query graph
  per query with every winning candidate spliced directly into the query's
  own pre-ASAP shape in place, built via a new
  asap_types::dag_export::export_post_asap.

asap_types::dag_export gains the generic, crate-agnostic shapes for this
(SummaryDagNode/SummaryDagGraph/export_summary, TargetReplacement/
TargetReplacementAfter, PostAsapSubstitution/export_post_asap) following
the same layering discipline DagNode::notes already established: this
crate never runs the search or picks a winner, only defines shapes a
higher layer (the dag_export binary) populates after the fact. DagNode's
source_expr becomes Option<QueryExpr> (None for a post-ASAP-originated
node with no corresponding QueryExpr).

Without --post-asap, output is byte-identical to before (both new fields
are empty/None and skipped from JSON).

Sample outputs covering all four --post-asap replacement kinds saved to
/tmp/post_asap_samples/*.json for the frontend dag-viewer track.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a fourth view mode alongside Single/Compare/Union that lets a user
pick one of a query's --post-asap `replacements[]` entries and see its
`before`/`after` subtrees side by side, plus a whole-query option
(query.graph vs the whole-query `post_graph`), so a claimed replacement
can be visually confirmed instead of just trusted.

- index.html: new "Before/After" mode button and a secondary #baPicker
  list (grouped by target node, sorted by rank, winner marked) shown
  below the tabs while the mode is active.
- viewer.js: renderBeforeAfter/laneElements reuse Compare mode's
  compound-lane pattern for exactly two fixed lanes; renderBaPicker
  builds the per-target and whole-query picker rows; showBeforeAfterDetail
  renders a selected lane node's detail (degrades cleanly on
  SummaryDagNode's missing hash/notes fields); computeHashOwners now
  skips hash-less nodes instead of bucketing them under `undefined`.
- node-style.js: new 'summary' category (neutral gray, distinct from the
  existing 9 hues) covering the 7 post-ASAP-only SummaryDagNode kinds;
  KeepPreAsap gets an extra muted/dashed override plus its own
  pass-through icon so 'unchanged' visually reads differently from
  'the planner did something here'.
- README.md: new "Before/After mode" section.
- post_asap_fixture.json: fixture used to build/test against (3 queries
  covering Sketch/HydraGrouping rank comparison, AvgToSumRewrite, and a
  SharedSubtree CSE share; p95_pktlen hand-extended with a post_graph
  since the Rust track's --post-asap output doesn't exist yet).

Verified via Playwright screenshots (light + dark) that Single/Compare/
Union are unregressed and all three fixture queries render correctly in
Before/After mode, including the whole-query toggle and its
disabled-when-absent state.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…tree matches

Integration fix after merging the post-ASAP export (--post-asap) and
dag-viewer Before/After tracks: export_post_asap's post-ASAP-originated
nodes (SummaryAgg, SummaryEstimate, etc. — anything with no
corresponding QueryExpr) were emitting hash: 0 as a placeholder. The
viewer's shared-subtree highlighting only treats hash === undefined as
'no hash' (by design, per the frontend track's own report), so every
post-ASAP-originated node across every query/graph would collide on
hash 0 and get spuriously rung as 'shared' with each other.

Fixed at the source: DagNode::hash is now Option<u64>, None (and
omitted from JSON) for nodes with no QueryExpr to hash — mirroring how
source_expr already handles the same situation. Verified real
--post-asap output flows through the merged dag-viewer correctly with
no false-shared highlighting and no regressions: full workspace test
suite (asap-types, asap-aware-mapping, asap-devtools, and everything
else) passes unchanged, fmt/clippy clean, and manually confirmed via
Playwright against real generated JSON (sketch mapping and
avg-to-sum-rewrite queries) in both Before/After per-target and
whole-query views.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
default_strategies() already includes it as of #282 — this binary no
longer needs its own custom strategy list, just plain
search_workload(). Also reworded the 'unmatched winner' diagnostic:
with AvgToSumOverCountStrategy now running by default, its rewrite
output routinely exposes new sum/count descendants that the same
search pass independently sketch-ranks — real winners, but ones with
no node in any query's original pre-rewrite graph to attach a flat
replacements[] entry to (expected, not a bug: still fully represented
in that query's post_graph). The old wording read like an error for
what is normally an expected case.

Verified: cargo test --workspace, fmt, and clippy all clean; manually
re-ran --post-asap on an AVG query and confirmed the JSON output is
identical (only the diagnostic wording changed).
@zzylol
zzylol force-pushed the feat/dag-post-asap-viz branch from 34967ce to c1c6b84 Compare August 26, 2026 02:18
zzylol and others added 13 commits August 25, 2026 20:35
…t_graph

Found via manual testing against real corpus queries (not the toy
examples used so far): a single, standalone STDDEV_POP aggregate
crashed dag_export --post-asap with a stack overflow, no sharing or
rewriting involved at all.

Root cause: STDDEV_POP (like AVG) dispatches to
Implementation::PassThrough with no alternative strategy of its own,
so SketchAlgorithmStrategy's only candidate for it is
keep_pre_asap()'s conservative fallback -- Replacement::Summary
wrapping the entire target as SummaryExpr::KeepPreAsap. run_post_asap
was treating this like any other winner and handing it to
export_post_asap, whose find_winner re-checks every node inside a
spliced KeepPreAsap payload by design (so a target nested underneath
one still gets found) -- but this payload structurally is the
enclosing target, so the fresh check finds the identical winner again,
unconditionally, forever.

This is the exact "no-op candidate" concept explanation.rs's own
sketch_finding_reason already excludes from being reported as a
finding. run_post_asap's own winner selection just wasn't applying
that same filter. Fixed by skipping a candidate whose
Replacement::Summary is a bare top-level KeepPreAsap when building
winners, with a regression test exercising the exact repro.

Also verified against several real corpus queries end to end
(previously only exercised with hand-written toy SQL): PR-PKT/FT-PKT
(#44/#49 in synthetic_packet_trace_queries.sql) now correctly show a
genuine Rollup relationship, and P-LEN-AVG (#14) shows the real
avg -> sum/count rewrite -- both screenshotted for the PR description.

Verified: cargo test --workspace, fmt, and clippy all clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
# Conflicts:
#	crates/devtools/src/bin/dag_export.rs
#	tools/dag-viewer/README.md
#	tools/dag-viewer/test_render.py
#	tools/dag-viewer/viewer.js
@zzylol zzylol changed the title feat(devtools,dag-viewer): post-ASAP replacement visualization (before/after) feat(devtools): interactive Pre/Post-ASAP workload DAG planner and visualization Aug 26, 2026
@zzylol zzylol changed the title feat(devtools): interactive Pre/Post-ASAP workload DAG planner and visualization feat(devtools): add interactive SQL/PromQL Pre/Post-ASAP workload planner and DAG viewer Aug 26, 2026
@zzylol
zzylol merged commit 747c66a into main Aug 26, 2026
0 of 3 checks passed
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