Skip to content

feat(control-plane): inspect compiled physical plans - #736

Open
milindsrivastava1997 wants to merge 5 commits into
mainfrom
codex/compiled-plan-inspection
Open

milindsrivastava1997 wants to merge 5 commits into
mainfrom
codex/compiled-plan-inspection

Conversation

@milindsrivastava1997

@milindsrivastava1997 milindsrivastava1997 commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Goal

When evaluating a deployment candidate, an engineer should be able to answer two practical questions without reading Rust debug output:

  1. What summaries and state will the backend maintain?
  2. How will each query use that maintained state?

This PR makes compile_workload_artifact a practical inspection tool for those answers.

What changes

The command now prints the complete CompiledPhysicalPlan as JSON instead of a hand-built subset. With --dot PLAN.dot, it also produces a diagram showing:

  • the PrecomputePlan DAG that creates and maintains summaries;
  • the QueryPlan DAG that serves each query;
  • dashed links from a query read to the exact maintained materialization it consumes.

Before this PR

The command emitted a custom JSON wrapper, and seeing the relationship between a query and its maintained state required manually correlating several nested JSON documents. There was no visual plan view.

After this PR

compile_workload_artifact SNAPSHOT.json --dot selected.dot > selected.json
dot -Tsvg selected.dot -o selected.svg

selected.json is the full selected physical plan. selected.svg gives a compact map of the maintenance and serving paths for that same candidate.

Verification

  • Focused serialization and DOT-renderer tests.
  • Built the example.
  • Ran it with a temporary fully quoted fixture and rendered its DOT output to SVG.

Scope

The diagram is a developer-inspection aid, not a new deployment API or versioned wire format.

@milindsrivastava1997
milindsrivastava1997 marked this pull request as ready for review September 17, 2026 17:38
@milindsrivastava1997

Copy link
Copy Markdown
Contributor Author

Example: inspect one priced workload

This diagram was generated from the checked-in planning snapshot with the query:

{
  "query": "quantile_over_time(0.99, m[1m])",
  "demand": {"fixed_interval_at": {"interval": 10000, "evaluation_phase": 0}},
  "requirements": {"accuracy": {"explicit": {"EpsilonDelta": {"epsilon": 0.01, "delta": 0.01}}}}
}

The full base input is docs/examples/asapquery-planning-snapshot.json. For this example, I added complete synthetic cost quotes derived from calibration_candidates; those quotes exercise selection only and are not measurement evidence for deployment.

compile_workload_artifact SNAPSHOT.json --dot selected.dot > selected.json
dot -Tsvg selected.dot -o selected.svg

The selected materialization is a sliding 60-second window advanced every 10 seconds and stored as 10-second panes. The dashed reads edge shows the query DAG reading that exact maintained materialization.

Generated compiled physical plan

Open the SVG directly.

@milindsrivastava1997

Copy link
Copy Markdown
Contributor Author

@zzylol Few questions in this CompiledPhysicalPlan:

  • What does Fallback #0 mean?
  • Why does PrecomputePlan have SummaryEstimate?

@zzylol

zzylol commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Fallback #0 means semantic DAG node 0. The planner exports KeepPreAsap (an original expression retained without a summary rewrite) as Fallback. Here its binding is MaintenanceInput: it supplies the input used by SummaryAgg to maintain the summary for m. It does not indicate a query failure or a switch to exact execution.

SummaryEstimate appears under PrecomputePlan because the renderer draws every node in precompute_plan.executable_dags inside that box. These are complete semantic DAGs with backend placement bindings, not exclusively maintenance operations. This node is explicitly bound as Query { query_node: QueryNodeId(0) }, so the quantile estimate runs at query time.

For a more descriptive display, I suggest:

  • Label this input Source expression: m, with Maintenance input beneath it. Keep Fallback / KeepPreAsap, semantic node #0 as secondary detail. Derive the expression label from the payload rather than hard-coding m.
  • Put the complete DAG in a separate Semantic plan section: Source expression → SummaryAgg → SummaryEstimate, marking each node with its backend binding (maintenance input, materialization, or query readout).
  • Reserve PrecomputePlan (maintenance) for the maintenance path and maintained materializations. Put SummaryEstimate (quantile = 0.99) in QueryPlan (read time), after ReadMaterialization, retaining the dashed materialization-read link.

This makes the semantic-to-physical mapping visible without implying that the estimate runs during precomputation. A smaller first step would be to rename the current inner DAG box Semantic DAG (maintenance + query) and explicitly label the estimate Query-time readout.

BTW, it requires some code reviewing, but https://github.com/ProjectASAP/ASAPPlanner/blob/029ff2fe041172c94c2d32c90b185bc83c5e8a57/crates/types/src/post_asap/executable_dag.rs#L446 as here, where the nodes are defined and visualized, the node definitions themselves might just be not that clear.

@milindsrivastava1997

Copy link
Copy Markdown
Contributor Author

@zzylol some more questions:

  1. What exactly does MaintenanceInput mean? is there a "non-maintenance" input?
  2. "These are complete semantic DAGs with backend placement bindings, not exclusively maintenance operations." -- But it's inside precompute_plan.executable_dags so why should it have SummaryEstimate in the first place?
  3. "This makes the semantic-to-physical mapping visible without implying that the estimate runs during precomputation." -- Related to 2. If the SummaryEstimate node is a part of PrecomputePlan, the visualization should depict that. The question then would be, why is this node part of the PrecomputePlan
  4. "Put the complete DAG in a separate Semantic plan section" -- I don't want to do this because I specifically wanna understand the separate PrecomputePlan and QueryPlan
  5. Is there a difference between Precompute and maintenance? This is related to 1. Idk what exactly this mean.

@zzylol

zzylol commented Sep 18, 2026

Copy link
Copy Markdown
Contributor

@zzylol some more questions:

  1. What exactly does MaintenanceInput mean? is there a "non-maintenance" input?
  2. "These are complete semantic DAGs with backend placement bindings, not exclusively maintenance operations." -- But it's inside precompute_plan.executable_dags so why should it have SummaryEstimate in the first place?
  3. "This makes the semantic-to-physical mapping visible without implying that the estimate runs during precomputation." -- Related to 2. If the SummaryEstimate node is a part of PrecomputePlan, the visualization should depict that. The question then would be, why is this node part of the PrecomputePlan
  4. "Put the complete DAG in a separate Semantic plan section" -- I don't want to do this because I specifically wanna understand the separate PrecomputePlan and QueryPlan
  5. Is there a difference between Precompute and maintenance? This is related to 1. Idk what exactly this mean.
  1. So in ASAPQuery-backend (not ASAPPlanner):
Binding Meaning
Materialization Maintenance-time node whose output is a stored summary
MaintenanceInput Maintenance-time node that participates in producing a summary, but the output of this node is not a stored summary; example: KLL(sum(data)), then sum() is the maintainanceInput node. KLL is the materialization node.
  1. the entire DAG can be "Input → Build summary → SummaryEstimate → Query result", and the SummaryEstimate node is being stored in the serialized PrecomputePlan, but not being executed during precomputation, but excuted in query plan. It is a mismatch between the abstraction and the content inside.

  2. Then I believe we should update the physical plan, to make it has a split point stored for different sub plans -- precomputePlan, queryPlan. And then visualization will natually show it. -> hope this answers 4.

  3. Precompute names the backend plan/engine responsible for producing and maintaining summary state.
    Maintenance names an execution phase: work that constructs or updates state, rather than answering a query. Maintenance does not imply incremental updates only. It can include initial batch construction, full rebuilding, merging, or deriving a new summary from completed state.

@milindsrivastava1997

Copy link
Copy Markdown
Contributor Author

@zzylol
Yes, we should update PhysicalPlan.

I also think we need to reword "maintenance". I see atleast 3 concepts

  • Maintenance-time node
  • Binding=MaintenanceInput
  • Binding=Materialization

Are there other concepts/terms?

Questions:

  • Is maintenance-time related to ingest-time? Or we can just call it materialization-phase?
  • Why do we need a MaintenanceInput node? How is this different from any other input node? Are there other input nodes?
  • Rename MaintenanceInput to InputForMaterialization
  • Rename Materialization to Materialize

@milindsrivastava1997

Copy link
Copy Markdown
Contributor Author

Filed #740

@milindsrivastava1997

Copy link
Copy Markdown
Contributor Author

@zzylol can we merge this?

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