Repository navigation
feat(planner): add asap-planner --planner legacy|milp #808
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
45734b3
2639950
ac73396
ce4483f
0c4bcf5
dcdd2c5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,9 @@ | ||
| use asap_planner::optimizer::{ | ||
| parse_weight, plan_milp, plan_to_planner_output, reject_unwritable_queries, MilpInputs, | ||
| }; | ||
| use asap_planner::{ | ||
| Controller, ElasticController, ElasticRuntimeOptions, RuntimeOptions, SQLController, | ||
| SQLRuntimeOptions, StreamingEngine, | ||
| Controller, ControllerConfig, ElasticController, ElasticRuntimeOptions, RuntimeOptions, | ||
| SQLController, SQLRuntimeOptions, StreamingEngine, | ||
| }; | ||
| use asap_types::enums::QueryLanguage; | ||
| use clap::Parser; | ||
|
|
@@ -53,6 +56,36 @@ struct Args { | |
| #[arg(long = "clickhouse-database", required = false)] | ||
| clickhouse_database: Option<String>, | ||
|
|
||
| /// `milp` plans with sketch-bench's rqe-optimizer: PromQL with | ||
| /// --input_config only, labels from its `metrics:` hints. | ||
| #[arg(long, value_enum, default_value = "legacy")] | ||
| planner: PlannerArg, | ||
|
|
||
| /// MILP only. YAML workload facts: per metric, positive `value_range` and | ||
| /// `cardinality` per label set, including all labels (the series count), | ||
| /// plus the `shape` of each grouping sketches may serve. | ||
| #[arg(long = "workload-facts", required_if_eq("planner", "milp"))] | ||
| workload_facts: Option<PathBuf>, | ||
|
|
||
| /// MILP only. The flat cost table sketch-bench's | ||
| /// `study_saturation.py --phase optimizer-cost` writes | ||
| /// (`rqe_atomic_costs.json`). | ||
| #[arg(long = "atomic-costs", required_if_eq("planner", "milp"))] | ||
| atomic_costs: Option<PathBuf>, | ||
|
|
||
| /// MILP only. sketch-bench's saturation-study directory: sketch accuracy | ||
| /// is read off its error-vs-N curves at each grouping's `shape`. | ||
| #[arg(long = "saturation-dir", required_if_eq("planner", "milp"))] | ||
| saturation_dir: Option<PathBuf>, | ||
|
|
||
| /// MILP only. Objective weight on CPU-sec/sec. Default: rqe-optimizer's. | ||
| #[arg(long = "w-cpu", value_parser = parse_weight)] | ||
| w_cpu: Option<f64>, | ||
|
|
||
| /// MILP only. Objective weight on memory GiB. Default: rqe-optimizer's. | ||
| #[arg(long = "w-mem", value_parser = parse_weight)] | ||
| w_mem: Option<f64>, | ||
|
|
||
| #[arg(short, long, action = clap::ArgAction::Count)] | ||
| verbose: u8, | ||
| } | ||
|
|
@@ -62,6 +95,12 @@ enum EngineArg { | |
| Precompute, | ||
| } | ||
|
|
||
| #[derive(clap::ValueEnum, Debug, Clone, Copy, PartialEq, Eq)] | ||
| enum PlannerArg { | ||
| Legacy, | ||
| Milp, | ||
| } | ||
|
|
||
| fn main() -> anyhow::Result<()> { | ||
| let args = Args::parse(); | ||
|
|
||
|
|
@@ -77,6 +116,21 @@ fn main() -> anyhow::Result<()> { | |
| EngineArg::Precompute => StreamingEngine::Precompute, | ||
| }; | ||
|
|
||
| if args.planner == PlannerArg::Milp { | ||
| run_milp(&args)?; | ||
| println!("Generated configs in {}", args.output_dir.display()); | ||
| return Ok(()); | ||
| } | ||
| anyhow::ensure!( | ||
| args.workload_facts.is_none() | ||
| && args.atomic_costs.is_none() | ||
| && args.saturation_dir.is_none() | ||
| && args.w_cpu.is_none() | ||
| && args.w_mem.is_none(), | ||
| "--workload-facts, --atomic-costs, --saturation-dir, --w-cpu and --w-mem require \ | ||
| --planner milp" | ||
| ); | ||
|
|
||
| match args.query_language { | ||
| QueryLanguage::promql => { | ||
| let scrape_interval_ms = args.data_ingestion_interval_ms.ok_or_else(|| { | ||
|
|
@@ -158,3 +212,65 @@ fn main() -> anyhow::Result<()> { | |
| println!("Generated configs in {}", args.output_dir.display()); | ||
| Ok(()) | ||
| } | ||
|
|
||
| fn run_milp(args: &Args) -> anyhow::Result<()> { | ||
| anyhow::ensure!( | ||
| matches!(args.query_language, QueryLanguage::promql), | ||
| "--planner milp supports only --query-language promql" | ||
| ); | ||
| anyhow::ensure!( | ||
| args.query_log.is_none(), | ||
| "--planner milp needs --input_config: query logs have no `metrics:` hints" | ||
| ); | ||
| anyhow::ensure!( | ||
| args.prometheus_url.is_none(), | ||
| "--planner milp takes labels from the config's `metrics:` hints, not --prometheus-url" | ||
| ); | ||
| anyhow::ensure!( | ||
| !args.enable_punting && args.range_duration_ms == 0 && args.step_ms == 0, | ||
| "--enable-punting, --range-duration-ms and --step-ms don't apply to --planner milp" | ||
| ); | ||
| anyhow::ensure!( | ||
| args.clickhouse_url.is_none() && args.clickhouse_database.is_none(), | ||
| "--clickhouse-url and --clickhouse-database don't apply to --planner milp" | ||
| ); | ||
| let config_path = args | ||
| .input_config | ||
| .as_deref() | ||
| .ok_or_else(|| anyhow::anyhow!("--planner milp requires --input_config"))?; | ||
| let scrape_interval_ms = args | ||
| .data_ingestion_interval_ms | ||
| .ok_or_else(|| anyhow::anyhow!("--planner milp requires --data-ingestion-interval-ms"))?; | ||
| // Per-series sample rates divide by it. | ||
| anyhow::ensure!( | ||
| scrape_interval_ms > 0, | ||
| "--data-ingestion-interval-ms must be positive" | ||
| ); | ||
| let config: ControllerConfig = serde_yaml::from_str(&std::fs::read_to_string(config_path)?)?; | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This parses the config with
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Addressed in 0c4bcf5 by rejecting |
||
| // Fail before solving: the plan is always written. | ||
| reject_unwritable_queries(&config)?; | ||
| let plan = plan_milp( | ||
| &config, | ||
| &MilpInputs { | ||
| workload_facts: args | ||
| .workload_facts | ||
| .as_deref() | ||
| .expect("clap requires --workload-facts with --planner milp"), | ||
| atomic_costs: args | ||
| .atomic_costs | ||
| .as_deref() | ||
| .expect("clap requires --atomic-costs with --planner milp"), | ||
| saturation_dir: args | ||
| .saturation_dir | ||
| .as_deref() | ||
| .expect("clap requires --saturation-dir with --planner milp"), | ||
| scrape_interval_ms, | ||
| w_cpu: args.w_cpu, | ||
| w_mem: args.w_mem, | ||
| allow_undeployable_families: false, | ||
| }, | ||
| )?; | ||
| plan_to_planner_output(&config, &plan.workload, &plan.solution)? | ||
| .write_to_dir(&args.output_dir)?; | ||
| Ok(()) | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With
--planner milp,--clickhouse-url,--clickhouse-databaseand--streaming_engineare silently ignored, while--prometheus-url,--enable-punting,--step-msetc. are explicitly rejected inrun_milp. Should these be rejected too for consistency?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 0c4bcf5:
--clickhouse-urland--clickhouse-databaseare now rejected under--planner milp. I left--streaming_engineas is: its only value isprecompute, which is what MILP emits, so it isn't ignored.