Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions rust-core/verisim-planner/src/explain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,8 @@ mod tests {
},
optimization_hint: Some("HNSW ANN search (k=10)".to_string()),
pushed_predicates: vec!["Similarity { k: 10 }".to_string()],
projections: vec![],
early_limit: None,
},
PlanStep {
step: 2,
Expand All @@ -266,6 +268,8 @@ mod tests {
io_cost: 135.0,
cpu_cost: 90.0,
},
projections: vec![],
early_limit: None,
optimization_hint: Some("Graph traversal: relates_to (depth=2)".to_string()),
pushed_predicates: vec!["Traversal { predicate: relates_to }".to_string()],
},
Expand Down Expand Up @@ -334,6 +338,8 @@ mod tests {
},
optimization_hint: Some("ZKP verification — expensive".to_string()),
pushed_predicates: vec![],
projections: vec![],
early_limit: None,
}],
strategy: ExecutionStrategy::Sequential,
total_cost: CostEstimate {
Expand Down
73 changes: 73 additions & 0 deletions rust-core/verisim-planner/src/optimizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,12 @@ impl Planner {
cost: cost.clone(),
optimization_hint: hint.clone(),
pushed_predicates,
// Carry both through to the physical plan. Dropping them here
// was not merely lossy: CostModel::estimate discounts a node
// that has an early_limit, so the plan was priced for a
// pushdown the executor could never see.
projections: node.projections.clone(),
early_limit: node.early_limit,
});

cost_estimates.push(cost.clone());
Expand Down Expand Up @@ -352,4 +358,71 @@ mod tests {
assert!(explain.text_output.contains("Strategy"));
assert!(explain.text_output.contains("vector"));
}

/// Projection and early-limit pushdown must survive optimization.
///
/// Before 2026-07-28 both were computed on the logical node and dropped at
/// the physical boundary. That was worse than lossy: `CostModel::estimate`
/// discounts a node carrying an `early_limit` (scaling `selectivity` and
/// taking `time_ms` to as low as 50% of base), so plans were priced for a
/// pushdown the executor could not see. Nothing failed loudly — the plan
/// just quietly cost less than it ran.
#[test]
fn test_pushdown_survives_optimization() {
let logical = LogicalPlan {
source: QuerySource::Octad,
nodes: vec![
PlanNode {
modality: Modality::Vector,
conditions: vec![ConditionKind::Similarity { k: 10 }],
projections: vec!["id".to_string(), "label".to_string()],
early_limit: Some(25),
},
PlanNode {
modality: Modality::Graph,
conditions: vec![ConditionKind::Traversal {
predicate: "relates_to".to_string(),
depth: Some(2),
}],
projections: vec!["id".to_string()],
early_limit: None,
},
],
post_processing: vec![],
};

let planner = Planner::new(PlannerConfig::default());
let physical = planner.optimize(&logical).expect("optimize");

// Match on modality rather than position: the optimizer reorders by
// execution priority, so asserting on steps[0] would be asserting on
// the sort order, not on the pushdown.
let vector_step = physical
.steps
.iter()
.find(|s| s.modality == Modality::Vector)
.expect("vector step present");
assert_eq!(
vector_step.projections,
vec!["id".to_string(), "label".to_string()],
"projection pushdown was dropped at the physical boundary"
);
assert_eq!(
vector_step.early_limit,
Some(25),
"early-limit pushdown was dropped, but the cost model already \
discounted the plan for it"
);

let graph_step = physical
.steps
.iter()
.find(|s| s.modality == Modality::Graph)
.expect("graph step present");
assert_eq!(graph_step.projections, vec!["id".to_string()]);
assert_eq!(
graph_step.early_limit, None,
"a node without an early limit must not acquire one"
);
}
}
20 changes: 20 additions & 0 deletions rust-core/verisim-planner/src/plan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,24 @@ pub struct PlanStep {
pub optimization_hint: Option<String>,
/// Pushed-down predicates.
pub pushed_predicates: Vec<String>,
/// Fields to project, carried through from [`PlanNode::projections`]
/// (empty = all fields).
///
/// Until 2026-07-28 this was absent, so projection pushdown was computed
/// on the logical node and then discarded at the physical boundary — the
/// executor had no way to see it.
#[serde(default)]
pub projections: Vec<String>,
/// Early row limit pushed down to the store, carried through from
/// [`PlanNode::early_limit`].
///
/// Also absent until 2026-07-28, which was the more consequential half of
/// the same gap: [`crate::cost::CostModel::estimate`] *already* discounts a node
/// carrying an early limit (it scales `selectivity` and takes `time_ms`
/// down to as low as 50% of base), so a plan was scored cheaper for a
/// pushdown the physical plan could not actually perform.
#[serde(default)]
pub early_limit: Option<usize>,
}

/// An optimized physical plan ready for execution.
Expand Down Expand Up @@ -182,6 +200,8 @@ mod tests {
},
optimization_hint: Some("HNSW ANN".to_string()),
pushed_predicates: vec!["k=10".to_string()],
projections: vec![],
early_limit: None,
}],
strategy: ExecutionStrategy::Sequential,
total_cost: CostEstimate {
Expand Down
2 changes: 2 additions & 0 deletions rust-core/verisim-planner/src/prepared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,8 @@ mod tests {
},
optimization_hint: Some("depth-limited BFS".to_string()),
pushed_predicates: vec!["relates_to".to_string()],
projections: vec![],
early_limit: None,
}],
strategy: ExecutionStrategy::Sequential,
total_cost: CostEstimate {
Expand Down
4 changes: 4 additions & 0 deletions rust-core/verisim-planner/src/profiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,8 @@ mod tests {
},
optimization_hint: Some("HNSW ANN search (k=10)".to_string()),
pushed_predicates: vec!["Similarity { k: 10 }".to_string()],
projections: vec![],
early_limit: None,
},
PlanStep {
step: 2,
Expand All @@ -475,6 +477,8 @@ mod tests {
io_cost: 135.0,
cpu_cost: 90.0,
},
projections: vec![],
early_limit: None,
optimization_hint: Some("Graph traversal: relates_to (depth=2)".to_string()),
pushed_predicates: vec!["Traversal { predicate: relates_to }".to_string()],
},
Expand Down
2 changes: 2 additions & 0 deletions rust-core/verisim-planner/src/slow_query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,8 @@ mod tests {
},
optimization_hint: None,
pushed_predicates: vec![],
projections: vec![],
early_limit: None,
})
.collect(),
strategy: if steps.len() >= 2 {
Expand Down
Loading