Skip to content
Closed
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
44 changes: 44 additions & 0 deletions crates/asap-aware-mapping/src/cost_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ use asap_types::post_asap::{
use asap_types::pre_asap::agg_intent::AggIntent;
use asap_types::pre_asap::expr_ir::ColumnRef;
use asap_types::pre_asap::query_expr::QueryExpr;
use asap_types::types::AccuracyTarget;

use crate::replacement::{
realize_child, Implementation, Replacement, ReplacementSubDAG, TargetSubDAG,
Expand Down Expand Up @@ -223,6 +224,49 @@ pub trait CostModel {
crate::replacement::default_size_params(kind, intent, eps, delta)
}

/// The accuracy budget to size a sketch candidate at for an
/// `AggIntent::TopK { accuracy: Exact, .. }` request, or `None` to
/// decline entirely (issue #151).
///
/// There is no *exact* mergeable top-k accumulator (unlike `Count`), so
/// `asap-plan` itself has no way to decide between "no summary at all"
/// and "the closest available approximation" for an `Exact`-accuracy
/// top-k request — that's a deployment policy call, and if it opts in,
/// *what budget to size at* is also a deployment call: `AccuracyTarget
/// ::Exact` itself resolves (via `replacement::accuracy_budget`) to the
/// tightest parameters every sketch family's sizing formula clamps to
/// (e.g. `cms_width`'s `1 << 26`), which is not a meaningful target for
/// any real deployment — sizing an opted-in candidate at that clamp
/// would silently produce the largest possible sketch (hundreds of MB
/// to GB per instance, multiplied per subpopulation for a grouped
/// top-k) for every un-annotated top-k query, since both this crate's
/// SQL and PromQL frontends default to `Exact` when no accuracy is
/// specified. Returning `Some` here instead requires the deployment to
/// state the budget it actually wants (e.g.
/// `Some(AccuracyTarget::Epsilon(1.0 / k as f64))`, or a fixed
/// deployment-wide epsilon). Returning `Some(AccuracyTarget::Exact)` is
/// a contract violation and is rejected before sizing, preventing the
/// clamp-saturated allocation described above. [`rank_candidates`](Self::rank_candidates)
/// and [`size_params`](Self::size_params) are then consulted exactly as
/// they are for any other top-k sketch candidate, sized against the
/// returned approximate target rather than `Exact`'s own degenerate one.
///
/// The sketch candidate(s) this produces are offered *alongside*
/// `Implementation::PassThrough`, not instead of it — `PassThrough`
/// stays in the exhaustive candidate set `implementations_for_with`
/// returns; ranking (via `rank_candidates`, and ultimately whichever
/// candidate a caller commits to) decides which one wins, the same as
/// every other exhaustive-then-ranked candidate set this crate builds.
///
/// Default: `None` — preserves today's `PassThrough`-only behavior for
/// every deployment that doesn't override this, including one whose own
/// `rank_candidates` override doesn't special-case `TopK`'s `Exact`
/// case (it is never consulted here unless this method also returns
/// `Some`).
fn topk_exact_accuracy_target(&self, _intent: &AggIntent) -> Option<AccuracyTarget> {
None
}

/// Estimated number of distinct subpopulations produced by `target`'s
/// grouping keys. `None` means the deployment has no cardinality estimate;
/// grouping alternatives remain legal but keep their discovery order.
Expand Down
58 changes: 58 additions & 0 deletions crates/asap-aware-mapping/src/explanation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,13 @@ mod tests {
);
}

/// True for every `AggIntent` this crate has no opt-in sketch path for
/// under an `Exact` accuracy target, and for `TopK { accuracy: Exact,
/// .. }` specifically under the *default* `CostModel` (no
/// `topk_exact_accuracy_target` override) — see
/// `topk_exact_opted_in_cost_model_reports_a_sketch_applicability_finding`
/// below for the one case (an opted-in `CostModel`, issue #151) where an
/// `Exact` accuracy target *does* report sketch-applicability.
#[test]
fn exact_quantile_is_not_a_sketch_applicability_finding() {
let q = agg(
Expand All @@ -561,6 +568,57 @@ mod tests {
);
}

/// The one exception to the invariant above (issue #151): a `CostModel`
/// that opts a `TopK { accuracy: Exact, .. }` request into a sketch
/// candidate via `CostModel::topk_exact_accuracy_target` makes that
/// candidate a real, non-trivial alternative in the `TargetSubDAG`'s
/// candidate list (alongside `PassThrough`) — exactly the shape this
/// module's own framing ("does this candidate list contain anything
/// other than the trivial, no-op realization?") reports as a
/// `SketchApproximation` finding. No special-casing was needed in this
/// module for that: `sketch_finding_reason` already reads whatever
/// `crate::replacement` decided, and the finding's `reason` is that
/// candidate's own rationale, which names the sketch kind explicitly —
/// a reviewer reading the finding can already tell an approximation was
/// used, even though the query asked for `Exact`.
#[test]
fn topk_exact_opted_in_cost_model_reports_a_sketch_applicability_finding() {
struct OffersSketchForExactTopK;
impl crate::cost_model::CostModel for OffersSketchForExactTopK {
fn rank_candidates(
&self,
_intent: &AggIntent,
candidates: &[asap_types::post_asap::SketchAlgorithm],
) -> Vec<asap_types::post_asap::SketchAlgorithm> {
candidates.to_vec()
}
fn topk_exact_accuracy_target(&self, _intent: &AggIntent) -> Option<AccuracyTarget> {
Some(AccuracyTarget::Epsilon(0.1))
}
}

let q = agg(
vec![2],
AggIntent::TopK {
k: 10,
accuracy: AccuracyTarget::Exact,
},
metric_scan(&["job"]),
);
let cost_model = OffersSketchForExactTopK;
let strategies = replacement::default_strategies_with(&cost_model);
let findings = explain_replacements_with(vec![("exact_top10", q)], &strategies);
let sketch = findings
.iter()
.find(|f| f.kind == ExplanationKind::SketchApproximation)
.expect("an opted-in CostModel should report a sketch finding for TopK{Exact}");
assert!(
sketch.reason.to_lowercase().contains("cmswithheap"),
"reason should name the sketch kind actually offered, got {:?}",
sketch.reason
);
}

#[test]
fn nested_aggregate_still_finds_the_inner_sketchable_node() {
// avg(quantile(0.9, sum by (job) (m))) shaped test isn't representable
Expand Down
Loading
Loading