Skip to content

Commit 2ce2942

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 53f8380 commit 2ce2942

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
@@ -4741,22 +4741,21 @@ fn collect_selected_materializations_with(
47414741
}
47424742
if let Some(readout) = readout {
47434743
let mut parameters = sketch_params_json(kind.params());
4744-
let mut item_label = None;
4744+
use planner_types::post_asap::SummaryInputExpr;
4745+
let item_label = match &input.item {
4746+
Some(SummaryInputExpr::Column(
4747+
planner_types::pre_asap::ColumnRef::Named(label),
4748+
)) => Some(label.clone()),
4749+
Some(SummaryInputExpr::Column(
4750+
planner_types::pre_asap::ColumnRef::Qualified { name, .. },
4751+
)) => Some(name.clone()),
4752+
// Legacy/direct TopK plans did not carry an item
4753+
// projection. Keep their generic item-key behavior;
4754+
// typed Planner plans name the inner aggregate
4755+
// identity explicitly through SummaryUpdate.item.
4756+
_ => None,
4757+
};
47454758
if matches!(readout, SketchQuery::TopK { .. }) {
4746-
use planner_types::post_asap::SummaryInputExpr;
4747-
item_label = match &input.item {
4748-
Some(SummaryInputExpr::Column(
4749-
planner_types::pre_asap::ColumnRef::Named(label),
4750-
)) => Some(label.clone()),
4751-
Some(SummaryInputExpr::Column(
4752-
planner_types::pre_asap::ColumnRef::Qualified { name, .. },
4753-
)) => Some(name.clone()),
4754-
// Legacy/direct TopK plans did not carry an item
4755-
// projection. Keep their generic item-key behavior;
4756-
// typed Planner plans name the inner aggregate
4757-
// identity explicitly through SummaryUpdate.item.
4758-
_ => None,
4759-
};
47604759
let mode = match &input.weight {
47614760
SummaryInputExpr::Constant(value) if *value == 1.0 => "count",
47624761
SummaryInputExpr::Column(
@@ -9157,6 +9156,44 @@ pub(crate) mod tests {
91579156
}
91589157
}
91599158

9159+
// Both plan transports retain nonempty typed DAGs and numeric binding keys.
9160+
#[test]
9161+
fn installed_dag_plan_round_trips_in_json_and_yaml() {
9162+
let plan = DeploymentPlanCompiler
9163+
.compile_promql(
9164+
request("roundtrip", "sum_over_time(m[1m])"),
9165+
environment(10_000),
9166+
)
9167+
.unwrap()
9168+
.precompute_plan;
9169+
assert!(!plan.executable_dags.is_empty());
9170+
let json = serde_json::to_value(&plan).unwrap();
9171+
let from_json: PrecomputePlan = serde_json::from_value(json.clone()).unwrap();
9172+
let from_yaml: PrecomputePlan =
9173+
serde_yaml::from_str(&serde_yaml::to_string(&plan).unwrap()).unwrap();
9174+
assert_eq!(serde_json::to_value(from_json).unwrap(), json);
9175+
assert_eq!(serde_json::to_value(from_yaml).unwrap(), json);
9176+
}
9177+
9178+
// Non-TopK keyed sketches retain the same item dimension as their Planner update.
9179+
#[test]
9180+
fn keyed_non_topk_materialization_preserves_item_label() {
9181+
let request = request("keyed", "quantile_over_time(0.90, m[1m])");
9182+
let mut root = request.queries[0].selected_plan_root.as_ref().clone();
9183+
let SummaryExpr::SummaryEstimate { summary_input, .. } = &mut root.expr else {
9184+
panic!("expected estimate");
9185+
};
9186+
let SummaryExpr::SummaryAgg { input, .. } = &mut Rc::make_mut(summary_input).expr else {
9187+
panic!("expected summary state");
9188+
};
9189+
input.item = Some(planner_types::post_asap::SummaryInputExpr::Column(
9190+
planner_types::pre_asap::ColumnRef::Named("customer".into()),
9191+
));
9192+
let root = Rc::new(root);
9193+
let selected = collect_selected_materializations(&root, false).unwrap();
9194+
assert_eq!(selected[0].item_label.as_deref(), Some("customer"));
9195+
}
9196+
91609197
#[test]
91619198
fn multiple_readouts_share_one_precompute_materialization() {
91629199
let mut compilation_request = request("q-p90", "quantile_over_time(0.90, m[1m])");

0 commit comments

Comments
 (0)