From 52190e04774457a2f194a3e1f0aa6b0a49e3a662 Mon Sep 17 00:00:00 2001 From: zzylol <50204836+zzylol@users.noreply.github.com> Date: Sun, 4 Oct 2026 22:48:45 +0000 Subject: [PATCH] chore(devtools): delete show_post_asap_ir, rename show_pre_asap_ir show_post_asap_ir ranked candidates with the legacy cost model; stage_pipeline shows how the planner plans a query. show_pre_asap_ir becomes show_logical_dag, the name of what it prints. Co-Authored-By: Claude Opus 5.5 --- crates/devtools/Cargo.toml | 6 +- ...how_pre_asap_ir.rs => show_logical_dag.rs} | 13 +- crates/devtools/src/bin/show_post_asap_ir.rs | 205 ------------------ docs/develop_docs/library-api.md | 2 +- docs/user_guide_docs/run-a-query.md | 53 +---- 5 files changed, 17 insertions(+), 262 deletions(-) rename crates/devtools/src/bin/{show_pre_asap_ir.rs => show_logical_dag.rs} (87%) delete mode 100644 crates/devtools/src/bin/show_post_asap_ir.rs diff --git a/crates/devtools/Cargo.toml b/crates/devtools/Cargo.toml index 944858a37..932630164 100644 --- a/crates/devtools/Cargo.toml +++ b/crates/devtools/Cargo.toml @@ -16,9 +16,9 @@ asap-logical-optimizer = { path = "../logical-optimizer" } # plans with. A devtool, not a stage crate, so the #572 guards allow it. asap-executor = { path = "../executor" } -# Used by the show_*_ir / stage_pipeline / variant_coverage bins (catalog schemas, -# async SQL path, JSON output) and by the topk_ir / canonical_examples -# examples. Regular deps, not dev-deps: `[[bin]]` targets can't see dev-deps. +# Used by the show_logical_dag / stage_pipeline / variant_coverage bins +# (catalog schemas, async SQL path, JSON output) and by the topk_ir / +# canonical_examples examples. Regular deps, not dev-deps: `[[bin]]` targets can't see dev-deps. asap-types = { path = "../types" } serde = { version = "1", features = ["derive"] } serde_json = "1" diff --git a/crates/devtools/src/bin/show_pre_asap_ir.rs b/crates/devtools/src/bin/show_logical_dag.rs similarity index 87% rename from crates/devtools/src/bin/show_pre_asap_ir.rs rename to crates/devtools/src/bin/show_logical_dag.rs index 1b3a3f724..ae2b2183e 100644 --- a/crates/devtools/src/bin/show_pre_asap_ir.rs +++ b/crates/devtools/src/bin/show_logical_dag.rs @@ -1,12 +1,11 @@ -// cargo run -p asap-devtools --bin show_pre_asap_ir -- queries.txt -// (or pipe via stdin: cargo run -p asap-devtools --bin show_pre_asap_ir < queries.txt) +// cargo run -p asap-devtools --bin show_logical_dag -- queries.txt +// (or pipe via stdin: cargo run -p asap-devtools --bin show_logical_dag < queries.txt) // // Lowers a batch of ad-hoc SQL/PromQL queries to **pre-ASAP IR** (the // sketch-agnostic intent algebra: an `OperatorNode` DAG of `NonASAPOp` -// operators with `AggIntent` measures) and prints them. -// See `show_post_asap_ir` for the post-ASAP sketch-bound IR one layer -// downstream — this tool never picks a sketch, it only shows what a query -// means. +// operators with `AggIntent` measures) and prints them. This tool never +// picks a sketch, it only shows what a query means; `stage_pipeline` shows +// how the planner plans it. // // File format: one query per line, prefixed with "sql>" or "promql>". // Blank lines and lines starting with '#' are ignored. @@ -49,7 +48,7 @@ async fn main() { assert_eq!( args.next().as_deref(), Some("--data-ingestion-interval-ms"), - "usage: show_pre_asap_ir --data-ingestion-interval-ms [queries.txt]" + "usage: show_logical_dag --data-ingestion-interval-ms [queries.txt]" ); let interval_ms = args .next() diff --git a/crates/devtools/src/bin/show_post_asap_ir.rs b/crates/devtools/src/bin/show_post_asap_ir.rs deleted file mode 100644 index 838bfd104..000000000 --- a/crates/devtools/src/bin/show_post_asap_ir.rs +++ /dev/null @@ -1,205 +0,0 @@ -// cargo run -p asap-devtools --bin show_post_asap_ir -- queries.txt -// (or pipe via stdin: cargo run -p asap-devtools --bin show_post_asap_ir < queries.txt) -// -// Lowers a batch of ad-hoc SQL/PromQL queries to pre-ASAP IR, then runs the -// `asap-logical-optimizer` pre-ASAP → post-ASAP binding pass and prints the -// resulting **post-ASAP IR** (the sketch-bound IR: an `OperatorNode` DAG in -// which `ASAPOp` operators — the concrete summary family/params committed per -// aggregate — replace the bound aggregates, while whatever the pass left -// untouched stays a plain `NonASAPOp` sub-DAG carrying an exact guarantee). -// See `show_pre_asap_ir` for the sketch-agnostic IR one layer upstream. -// -// File format: one query per line, prefixed with "sql>" or "promql>". -// Blank lines and lines starting with '#' are ignored. -// -// sql> SELECT service, COUNT(*) FROM metrics GROUP BY service -// promql> topk(5, rate(http_requests_total[5m])) -// -// Every query lowers at ACCURACY (ε = 0.01 below) rather than `Exact` — an -// exact target only ever exercises the mergeable-accumulator arm of the -// boundary decision, never a real sketch. SQL queries run against a fixed -// `metrics(ts, service, region, latency, bytes)` catalog — the same table -// used in cross_language.rs and topk_ir.rs. - -use asap_devtools::{lower_promql_with_data_ingestion_interval, lower_sql, SqlCatalog}; -use asap_logical_optimizer::pass1::replacement::retain_exact; -use asap_logical_optimizer::{ - ASAPStrategies, Replacement, ReplacementStrategy, ReplacementSubDAG, TargetSubDAG, -}; -use asap_types::ir::schema::{DataType, Field, Schema}; -use asap_types::ir::OperatorNode; -use asap_types::types::AccuracyTarget; -use std::io::Read; -use std::rc::Rc; - -const ACCURACY: AccuracyTarget = AccuracyTarget::Epsilon(0.01); - -/// `ASAPStrategies::replacements` returns every candidate. This -/// debug tool prints all of them so callers can inspect the planner's choices. -/// If the strategy has none, preserve the single pre-ASAP fallback output. -fn bind_all(root: &Rc) -> Result>, String> { - let target = TargetSubDAG::new(root); - let candidates = ASAPStrategies::default() - .replacements(&target) - .into_iter() - .filter_map(|candidate| match candidate { - ReplacementSubDAG { - replacement: Replacement::SubDAG(node), - .. - } => Some(node), - _ => None, - }) - .collect::>(); - - if candidates.is_empty() { - Ok(vec![retain_exact(root).map_err(|e| e.to_string())?]) - } else { - Ok(candidates) - } -} - -fn col(name: &str, dtype: DataType) -> Field { - Field::plain(name, dtype, false) -} - -fn catalog() -> SqlCatalog { - SqlCatalog::new().with_table( - "metrics", - Schema::with_time_index( - vec![ - col("ts", DataType::Timestamp), - col("service", DataType::Utf8), - col("region", DataType::Utf8), - col("latency", DataType::Float64), - col("bytes", DataType::Int64), - ], - 0, - vec![], - ), - ) -} - -#[tokio::main] -async fn main() { - let mut args = std::env::args().skip(1); - assert_eq!( - args.next().as_deref(), - Some("--data-ingestion-interval-ms"), - "usage: show_post_asap_ir --data-ingestion-interval-ms [queries.txt]" - ); - let interval_ms = args - .next() - .expect("missing interval") - .parse() - .expect("interval must be an unsigned integer"); - let input = match args.next() { - Some(path) => { - std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("failed to read {path}: {e}")) - } - None => { - let mut buf = String::new(); - std::io::stdin() - .read_to_string(&mut buf) - .expect("failed to read stdin"); - buf - } - }; - - let catalog = catalog(); - for line in input.lines() { - let line = line.trim(); - if line.is_empty() || line.starts_with('#') { - continue; - } - println!("━━━ {line} ━━━"); - let l3 = if let Some(q) = line.strip_prefix("sql>") { - lower_sql(q.trim(), &catalog, ACCURACY.clone()) - .await - .map_err(|e| e.to_string()) - } else if let Some(q) = line.strip_prefix("promql>") { - lower_promql_with_data_ingestion_interval(q.trim(), ACCURACY.clone(), interval_ms) - .map_err(|e| e.to_string()) - } else { - println!("ERR: line must start with 'sql>' or 'promql>'"); - println!(); - continue; - }; - match l3.and_then(|expr| bind_all(&expr)) { - Ok(candidates) => { - for (index, candidate) in candidates.iter().enumerate() { - println!("--- candidate {} ---", index + 1); - println!("{:#?}", candidate.operator); - } - } - Err(e) => println!("ERR: {e}"), - } - println!(); - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn bind_all_returns_every_sketch_candidate() { - let expr = lower_promql_with_data_ingestion_interval( - "quantile(0.99, rate(http_requests_total[5m]))", - ACCURACY.clone(), - 1_000, - ) - .expect("query lowers to pre-ASAP IR"); - let expected = ASAPStrategies::default() - .replacements(&TargetSubDAG::new(&expr)) - .len(); - - assert!(expected > 1, "fixture exposes alternative bindings"); - assert_eq!(bind_all(&expr).expect("binding succeeds").len(), expected); - } - - #[test] - fn bind_all_exposes_an_uncertified_ddsketch_ratio_by_default() { - let expr = lower_promql_with_data_ingestion_interval( - "quantile_over_time(0.9,data[5m])/quantile_over_time(0.5,data[5m])", - ACCURACY.clone(), - 1_000, - ) - .expect("query lowers to pre-ASAP IR"); - - let candidates = bind_all(&expr).expect("binding succeeds"); - assert_eq!(candidates.len(), 1); - assert!(matches!( - candidates[0].non_asap(), - Some(asap_types::ir::NonASAPOp::BinaryOp { .. }) - )); - assert!( - candidates[0].guarantee.is_none(), - "missing evidence must not claim a certified ratio bound" - ); - let timed = asap_types::ir::properties::timing::apply_materialization_timings( - &candidates[0], - &asap_types::ir::properties::timing::MaterializationAssignment::all_query_time(), - &mut asap_types::ir::properties::timing::TimingMemo::new(), - ) - .expect("the demo candidate has a legal default timing"); - asap_types::ir::physical_export::compile_physical_asap_dag(&timed) - .expect("the demo candidate remains executable"); - } - - #[test] - fn default_ratio_candidate_does_not_relax_other_approximate_divisions() { - let expr = lower_promql_with_data_ingestion_interval( - "avg_over_time(data[5m])/quantile_over_time(0.5,data[5m])", - ACCURACY.clone(), - 1_000, - ) - .expect("query lowers to pre-ASAP IR"); - - let candidates = bind_all(&expr).expect("binding succeeds"); - assert_eq!(candidates.len(), 1); - assert!( - !candidates[0].contains_asap(), - "the whole query is kept pre-ASAP (no summary bound anywhere)" - ); - } -} diff --git a/docs/develop_docs/library-api.md b/docs/develop_docs/library-api.md index 9dbd31f44..2c0b7ab0f 100644 --- a/docs/develop_docs/library-api.md +++ b/docs/develop_docs/library-api.md @@ -141,7 +141,7 @@ async lower_sql_dialect(query: &str, catalog: &SqlCatalog, | `ElasticSQL` | Returns `UnsupportedDialect` | The catalog is required and describes your tables. For a complete schema-building -example, see [the CLI frontend example](../../crates/devtools/src/bin/show_pre_asap_ir.rs). +example, see [the CLI frontend example](../../crates/devtools/src/bin/show_logical_dag.rs). ## Generate and rank candidates diff --git a/docs/user_guide_docs/run-a-query.md b/docs/user_guide_docs/run-a-query.md index 2efe403ce..98ed0ae63 100644 --- a/docs/user_guide_docs/run-a-query.md +++ b/docs/user_guide_docs/run-a-query.md @@ -15,8 +15,7 @@ tool on first use. | Command (`cargo run -p asap-devtools --bin … -- …`) | Input / options | Result | | --- | --- | --- | -| `show_pre_asap_ir --data-ingestion-interval-ms 1000 queries.txt` | File path, or stdin when omitted | Prints canonical Pre-ASAP IR | -| `show_post_asap_ir --data-ingestion-interval-ms 1000 queries.txt` | Same query file format | Prints all sketch-strategy Post-ASAP candidates using a fixed approximate target, in cost-model order | +| `show_logical_dag --data-ingestion-interval-ms 1000 queries.txt` | File path, or stdin when omitted | Prints canonical Pre-ASAP IR | | `analyze_corpora --corpora --data-ingestion-interval-ms 1000 --out-dir ` | Repository PromQL corpora, output directory | Writes successful/error IR dumps and summary reports | | `analyze_corpora --sql-corpora --out-dir ` | Repository SQL corpora, output directory | Writes SQL corpus reports | | `variant_coverage --data-ingestion-interval-ms 1000` | Repository corpora | Reports Pre-ASAP IR variant coverage | @@ -38,8 +37,8 @@ promql> quantile(0.99, rate(http_requests_total[5m])) sql> SELECT service, COUNT(*) FROM metrics GROUP BY service ``` -Blank lines and lines beginning with `#` are ignored. The two file/stdin tools -accept `sql>` and `promql>`; MetricsQL is available through the library frontend. +Blank lines and lines beginning with `#` are ignored. The file/stdin tool +accepts `sql>` and `promql>`; MetricsQL is available through the library frontend. SQL examples use the fixed catalog `metrics(ts: Timestamp, service: Utf8, region: Utf8, latency: Float64, bytes: Int64)`. For your own schema, provide a `SqlCatalog` through the library API. @@ -49,13 +48,13 @@ For your own schema, provide a `SqlCatalog` through the library API. Run: ```sh -cargo run -p asap-devtools --bin show_pre_asap_ir -- --data-ingestion-interval-ms 1000 queries.txt +cargo run -p asap-devtools --bin show_logical_dag -- --data-ingestion-interval-ms 1000 queries.txt ``` You can also provide the queries through stdin: ```sh -cargo run -p asap-devtools --bin show_pre_asap_ir -- --data-ingestion-interval-ms 1000 < queries.txt +cargo run -p asap-devtools --bin show_logical_dag -- --data-ingestion-interval-ms 1000 < queries.txt ``` To dump and compare every PromQL corpus, run: @@ -76,44 +75,6 @@ The corresponding SQL corpus analysis is: cargo run -p asap-devtools --bin analyze_corpora -- --sql-corpora --out-dir artifacts/sql_pre_asap ``` -### Inspect Post-ASAP IR candidates - -Run: - -```sh -cargo run -p asap-devtools --bin show_post_asap_ir -- --data-ingestion-interval-ms 1000 queries.txt -``` - -Or through stdin: - -```sh -cargo run -p asap-devtools --bin show_post_asap_ir -- --data-ingestion-interval-ms 1000 < queries.txt -``` - -`show_post_asap_ir` uses an approximation target of ε = 0.01 and prints every -available binding from the sketch strategy for each query, numbered in cost-model -order. If no candidate is available, it prints the pre-ASAP fallback as candidate -1. It does not show the complete ranked workload candidate set or choose a -deployment. Its SQL examples use a fixed demonstration catalog, not -your database schema. Use the -[library workflow](../develop_docs/library-api.md) to retain workload alternatives -and provide your own models. - -The default strategy generates DDSketch quantile-ratio candidates even when no -input-domain evidence is available. Such candidates have `guarantee: None`: -they do not claim a certified end-to-end accuracy bound. They also remain -visible in a target-aware `CandidateLogicalASAPDAGs` so the downstream backend can decide -whether to select them using its own evidence. Planner's automatic -`global_selection` skips them; their presence alone does not show that they -meet the requested target. - -Each input line is followed by its debug IR or an `ERR:` message. Pre-ASAP and -Post-ASAP output use the same node format: Post-ASAP output adds summary nodes -(state, readouts) and keeps the original exact operators wherever no summary -replaces them. An -approximate target permits approximation; it does not guarantee a legal or -certified sketch. The tool prints plans, not query results. - ## More inspection commands ### See how the planner plans a workload @@ -166,5 +127,5 @@ cargo run -p asap-devtools --example canonical_examples PromQL commands require `--data-ingestion-interval-ms` with the nonzero source sample cadence in milliseconds. The examples use a one-second cadence; supply -the interval for your data. The mixed-input `show_pre_asap_ir` and -`show_post_asap_ir` tools require this option even for SQL-only input files. +the interval for your data. The mixed-input `show_logical_dag` tool requires +this option even for SQL-only input files.