Skip to content

refactor(plan-selection): delete the old Stage 3 engine; select_plan runs only the group selection MILP - #1187

Merged
zzylol merged 2 commits into
mainfrom
refactor/delete-old-stage3-engine
Nov 3, 2026
Merged

zzylol merged 2 commits into
mainfrom
refactor/delete-old-stage3-engine

Conversation

@zzylol

@zzylol zzylol commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Why

#1144 made the group selection MILP (#1095) Stage 3's default search. The old engine (candidate graph, cost bounds, the evaluator-backed MILP refinement loop and the bounded objective traversal) stayed behind ASAP_STAGE3_SEARCH=old. It is no longer the plan we run, it is about 21.8k lines, and keeping it doubles every Stage 3 change.

What

  • Deletes crates/plan-selection/src/search/ (the old engine and its tests) and the ASAP_STAGE3_SEARCH switch.
  • select_plan moves into crates/plan-selection/src/select.rs and always runs the group selection MILP. Its signature is unchanged.
  • The whole-plan selector (selector.rs, WholePlanSelection) is now compiled only for tests and the reference-pricer feature (its remaining users: stage_pipeline --exhaustive/--price-enumerated, planner_fuzz, integration tests).
  • Comments and docs that referred to "today's engine", "the new search" or the switch are updated; planner-layering.md's "How the search uses the memo" paragraph and code locations now describe the group selection MILP; the crate's cost-model docs say select_plan supports cost model 1 only (Memo search solver: MILP over the memo for all cost models #888).

Capability lost, and where it is tracked

Old-engine capability New implementation or issue
Stage 3 search under cost model 1 select::select_plan (group MILP, #1095)
Cost model 2 (peak) and cost model 3 (cluster capacity) search #888 (MILP over the memo for all cost models); today these return UnsupportedCostModel, as on main since #1144
Measured operation cost table without a frozen calibration factor #888; UnsupportedCostModel as on main since #1144
ASAP_STAGE3_SEARCH=old escape hatch none (removed by request)

How

Move the entry point and its doc comment from search/select.rs into select.rs (cost-input validation first, then the existing MILP path), drop the env switch, git rm search/, and gate the whole-plan selector on cfg(any(test, feature = "reference-pricer")) so the default build has no dead code.

Key code interfaces added / modified

crates/plan-selection/src/select.rs, crates/plan-selection/src/lib.rs

/// Stage 3's only entry: select one plan among the physical memo's candidates
/// with the group selection MILP. Errs on invalid or unsupported cost inputs
/// (cost models 2/3), an infeasible catalog, no plan by the deadline, or a
/// realizer rejection (a Stage 2 catalog bug).
pub fn select_plan(
    physical: &PhysicalASAPMemoCandidates,
    entries: &[QueryWorkloadEntry],
    data: &DataWorkload,
    accuracy_checks: &AccuracyCheckInputs<'_>,
    cost_inputs: &CostInputs,
    realizer: &dyn AssignmentRealizer,
    deadline: Option<Instant>,
) -> Result<Selection, ErrorSelectingPlan>; // moved from search/select.rs; signature unchanged

/// Env switch to the old engine.
pub(crate) const STAGE3_SEARCH_ENV: &str = "ASAP_STAGE3_SEARCH"; // removed
pub(crate) fn milp_search_enabled() -> bool; // removed
pub(crate) fn select_plan_with_milp(/* ... */); // removed: now select_plan

crates/plan-selection/src/report.rs

/// The whole-plan selector's result; stays, now only with the
/// `reference-pricer` feature or in tests.
#[cfg(any(test, feature = "reference-pricer"))] // new gate
pub struct WholePlanSelection { /* fields unchanged */ }

Usage (unchanged for callers): the facade's run_planning calls asap_plan_selection::select_plan(&physical, &entries, &data, &checks, &cost_inputs, &realizer, deadline) and gets back the Selection (selected DAG, PlanCost, accepted accuracy per query, pruned candidates, SearchReport) exactly as on main with the default switch.

Major algorithm changes

None. Main already ran the group MILP by default; this removes the non-default engine.

Before this PR

select_plan ran the group MILP unless ASAP_STAGE3_SEARCH=old was set, which ran the old candidate-graph engine (and was the only way to search under cost models 2/3).

After this PR

select_plan always runs the group MILP. ASAP_STAGE3_SEARCH is ignored. Cost models 2/3 return UnsupportedCostModel until #888.

Verification

  • cargo check --workspace --tests: clean, no warnings; also cargo check -p asap-plan-selection --tests --features reference-pricer.
  • cargo test -p asap-plan-selection --lib: 142 passed, 0 failed.
  • cargo test -p asap-devtools --test devtools stage_pipeline: 5 passed, 0 failed, 46 ignored (already #[ignore] on main: wall-clock or slow tests).
  • cargo fmt --all.
  • Screenshots / measurements: not applicable (no behavior change on the default path).

Limitations and follow-ups

🤖 Generated with Claude Code

…lection MILP is select_plan's only search (#1095)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@zzylol zzylol added optimizer Workload optimization and plan selection refactor search Candidate-plan search space and algorithms labels Nov 2, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@zzylol
zzylol merged commit 538df9c into main Nov 3, 2026
1 check failed
@zzylol
zzylol deleted the refactor/delete-old-stage3-engine branch November 3, 2026 02:16
zzylol added a commit that referenced this pull request Nov 3, 2026
… selector (main build after #1164 and #1187)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
milindsrivastava1997 added a commit that referenced this pull request Nov 3, 2026
…ector.rs

#1164 imports crate::AnalyticalCostError, which #1187 moved into lib.rs's
test module; the two merged in that order, so main does not compile.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
zzylol added a commit that referenced this pull request Nov 3, 2026
…y and cost exactly (#1185, part 1) (#1188)

* fix(plan-selection): run the group selection MILP to proven optimality and cost exactly (#1185)

The group selection MILP stopped at wall-clock deadlines and costed some
candidates inexactly past hardcoded caps, so the selected plan depended on
machine speed and could differ from the optimum of the model.

Deadlines removed:
- select_plan no longer takes a deadline; the facade stops passing one
  (its own round budgets and the Pass 2 deadline are unchanged).
- The warm start has no time budget: it tries workload order, its reverse
  and MORE_ORDERS pseudo-random orders, every run.
- No per-part time split and no HiGHS time_limit: every part is solved to
  proven optimality. Any HiGHS status other than Optimal or Infeasible is
  a solver error.
- Deleted WARM_START_MOST_TIME, MOMENT_TO_READ_THE_START_S, LP_TIME_LIMIT_S
  (the LP diagnosis's LPs and one-node root solve end on their own),
  SelectionMilpStatus, SelectionMilpError::{DeadlineReached, NoSolution},
  SearchStatus::DeadlineReached, ErrorSelectingPlan::NoPlanByDeadline and
  the solution's relative_gap (always 0 now).

Caps removed (every combination is costed and encoded exactly):
- MAX_CONTEXTS, MAX_PRODUCER_CHOICES and UsageContext::TooManyContexts.
- MAX_WINDOW_KINDS: every union of a scan run's window kinds is costed
  and encoded with exact indicators; the widest-window fallback is gone.
- MAX_COMPOSITIONS: every composition is built and checked, so no set of
  approximate candidates is marked NotChecked for its size.

HiGHS threads are now SelectionMilpOptions::highs_threads, default 1
(was 4), so ties between plans of equal cost break the same way.

Snapshot changes: only the search status. o11y_bench q05, q19, q25 and
the sketch workloads shared-quantiles-1h-mvp and quantile-single-mvp
used to stop at the time limit with the optimal plan; they are now proven
optimal (proven_optimal true, search_status optimal). Plans and costs
are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(plan-selection): guard window-union enumeration against shift overflow; update deadline docs (review)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(plan-selection): name why candidates could not be costed when the selection MILP is infeasible

The dag-viewer test test_a_plan_without_a_rate_fact_names_the_missing_fact
failed on main since the group MILP became the default: candidates whose
usage asap-cost could not price (e.g. a source without an arrival rate)
were left out silently and the error said only 'infeasible'.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(plan-selection): import AnalyticalCostError from asap-cost in the selector (main build after #1164 and #1187)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

optimizer Workload optimization and plan selection refactor search Candidate-plan search space and algorithms

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant