Skip to content

Commit aaa7876

Browse files
zzylolclaude
andcommitted
fix: preserve typed plan transports and index installed metadata
Preserve YAML enum tags before decoding, while keeping JSON numeric binding keys valid. Build a temporary decoded DAG/family lookup for each validation or installation pass and reuse installed family maps during revision recovery. Derive non-TopK item labels from the same Planner update as runtime. Refresh ownership and v2 deployment-only materialization documentation. Validation: YAML regression fails on the original JSON buffer; 99 shared-type tests pass after the fix. Nonempty JSON/YAML DAG roundtrip and keyed-label regressions pass. Independent review by mixed_placement agent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 30adee8 commit aaa7876

10 files changed

Lines changed: 269 additions & 163 deletions

File tree

‎control_plane/src/physical/compiler.rs‎

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4758,22 +4758,21 @@ fn collect_selected_materializations_with(
47584758
}
47594759
if let Some(readout) = readout {
47604760
let mut parameters = sketch_params_json(kind.params());
4761-
let mut item_label = None;
4761+
use planner_types::post_asap::SummaryInputExpr;
4762+
let item_label = match &input.item {
4763+
Some(SummaryInputExpr::Column(
4764+
planner_types::pre_asap::ColumnRef::Named(label),
4765+
)) => Some(label.clone()),
4766+
Some(SummaryInputExpr::Column(
4767+
planner_types::pre_asap::ColumnRef::Qualified { name, .. },
4768+
)) => Some(name.clone()),
4769+
// Legacy/direct TopK plans did not carry an item
4770+
// projection. Keep their generic item-key behavior;
4771+
// typed Planner plans name the inner aggregate
4772+
// identity explicitly through SummaryUpdate.item.
4773+
_ => None,
4774+
};
47624775
if matches!(readout, SketchQuery::TopK { .. }) {
4763-
use planner_types::post_asap::SummaryInputExpr;
4764-
item_label = match &input.item {
4765-
Some(SummaryInputExpr::Column(
4766-
planner_types::pre_asap::ColumnRef::Named(label),
4767-
)) => Some(label.clone()),
4768-
Some(SummaryInputExpr::Column(
4769-
planner_types::pre_asap::ColumnRef::Qualified { name, .. },
4770-
)) => Some(name.clone()),
4771-
// Legacy/direct TopK plans did not carry an item
4772-
// projection. Keep their generic item-key behavior;
4773-
// typed Planner plans name the inner aggregate
4774-
// identity explicitly through SummaryUpdate.item.
4775-
_ => None,
4776-
};
47774776
let mode = match &input.weight {
47784777
SummaryInputExpr::Constant(value) if *value == 1.0 => "count",
47794778
SummaryInputExpr::Column(
@@ -9174,6 +9173,44 @@ pub(crate) mod tests {
91749173
}
91759174
}
91769175

9176+
// Both plan transports retain nonempty typed DAGs and numeric binding keys.
9177+
#[test]
9178+
fn installed_dag_plan_round_trips_in_json_and_yaml() {
9179+
let plan = DeploymentPlanCompiler
9180+
.compile_promql(
9181+
request("roundtrip", "sum_over_time(m[1m])"),
9182+
environment(10_000),
9183+
)
9184+
.unwrap()
9185+
.precompute_plan;
9186+
assert!(!plan.executable_dags.is_empty());
9187+
let json = serde_json::to_value(&plan).unwrap();
9188+
let from_json: PrecomputePlan = serde_json::from_value(json.clone()).unwrap();
9189+
let from_yaml: PrecomputePlan =
9190+
serde_yaml::from_str(&serde_yaml::to_string(&plan).unwrap()).unwrap();
9191+
assert_eq!(serde_json::to_value(from_json).unwrap(), json);
9192+
assert_eq!(serde_json::to_value(from_yaml).unwrap(), json);
9193+
}
9194+
9195+
// Non-TopK keyed sketches retain the same item dimension as their Planner update.
9196+
#[test]
9197+
fn keyed_non_topk_materialization_preserves_item_label() {
9198+
let request = request("keyed", "quantile_over_time(0.90, m[1m])");
9199+
let mut root = request.queries[0].selected_plan_root.as_ref().clone();
9200+
let SummaryExpr::SummaryEstimate { summary_input, .. } = &mut root.expr else {
9201+
panic!("expected estimate");
9202+
};
9203+
let SummaryExpr::SummaryAgg { input, .. } = &mut Rc::make_mut(summary_input).expr else {
9204+
panic!("expected summary state");
9205+
};
9206+
input.item = Some(planner_types::post_asap::SummaryInputExpr::Column(
9207+
planner_types::pre_asap::ColumnRef::Named("customer".into()),
9208+
));
9209+
let root = Rc::new(root);
9210+
let selected = collect_selected_materializations(&root, false).unwrap();
9211+
assert_eq!(selected[0].item_label.as_deref(), Some("customer"));
9212+
}
9213+
91779214
#[test]
91789215
fn multiple_readouts_share_one_precompute_materialization() {
91799216
let mut compilation_request = request("q-p90", "quantile_over_time(0.90, m[1m])");

0 commit comments

Comments
 (0)