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
36 changes: 28 additions & 8 deletions control_plane/src/asap_tier_analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,26 +355,46 @@ fn collect_agg_intents(expr: &QueryExpr, out: &mut Vec<AggIntent>) {
fn intent_kind_label(intent: &AggIntent) -> &'static str {
match intent {
AggIntent::Count { .. } => "count",
AggIntent::Sum => "sum",
AggIntent::Min => "min",
AggIntent::Max => "max",
AggIntent::Avg => "avg",
AggIntent::Sum { .. } => "sum",
AggIntent::Min { .. } => "min",
AggIntent::Max { .. } => "max",
AggIntent::Avg { .. } => "avg",
AggIntent::StdDev { .. } => "stddev",
AggIntent::Variance { .. } => "variance",
AggIntent::Quantile { .. } => "quantile",
AggIntent::TopK { .. } => "topk",
AggIntent::Cardinality { .. } => "cardinality",
AggIntent::Frequency { .. } => "frequency",
AggIntent::Rate { .. } => "rate",
AggIntent::Increase { .. } => "increase",
AggIntent::Absent => "absent",
AggIntent::Present => "present",
AggIntent::AbsentOverTime => "absent_over_time",
AggIntent::PresentOverTime => "present_over_time",
AggIntent::Delta { .. } => "delta",
AggIntent::Deriv { .. } => "deriv",
AggIntent::PredictLinear { .. } => "predict_linear",
AggIntent::HoltWinters { .. } => "holt_winters",
AggIntent::Idelta { .. } => "idelta",
AggIntent::Irate { .. } => "irate",
AggIntent::DoubleExpSmoothing { .. } => "double_exponential_smoothing",
AggIntent::IDelta { .. } => "idelta",
AggIntent::Resets { .. } => "resets",
AggIntent::Changes { .. } => "changes",
AggIntent::HistogramCount => "histogram_count",
AggIntent::HistogramSum => "histogram_sum",
AggIntent::HistogramAvg => "histogram_avg",
AggIntent::HistogramStdDev => "histogram_stddev",
AggIntent::HistogramStdVar => "histogram_stdvar",
AggIntent::HistogramFraction { .. } => "histogram_fraction",
AggIntent::HistogramQuantile { .. } => "histogram_quantile",
AggIntent::Math(_) => "math",
AggIntent::TimeFn(_) => "time_fn",
AggIntent::Group => "group",
AggIntent::CountValues { .. } => "count_values",
AggIntent::LastOverTime => "last_over_time",
AggIntent::FirstOverTime => "first_over_time",
AggIntent::MadOverTime => "mad_over_time",
AggIntent::TsOfMinOverTime => "ts_of_min_over_time",
AggIntent::TsOfMaxOverTime => "ts_of_max_over_time",
AggIntent::TsOfFirstOverTime => "ts_of_first_over_time",
AggIntent::TsOfLastOverTime => "ts_of_last_over_time",
}
}

Expand Down
885 changes: 633 additions & 252 deletions control_plane/src/intent_algebra/agg_intent.rs

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions control_plane/src/intent_algebra/column_resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ pub fn resolve_named_keys(keys: &[String], schema: &Schema) -> Result<Vec<Column
/// let by: Vec<usize> = vec![];
/// let aggs = vec![
/// AggIntent::Count { accuracy: types_v2::AccuracyTarget::Exact },
/// AggIntent::Sum,
/// AggIntent::Sum { col: None },
/// ];
/// let output = output_schema_for_aggregate(&input, &by, &aggs);
/// assert_eq!(output.columns.len(), 2); // count + sum
Expand Down Expand Up @@ -410,7 +410,7 @@ mod tests {
});
// Group by host, region (positions 2 and 3).
let by = vec![2usize, 3usize];
let aggs = vec![AggIntent::Sum];
let aggs = vec![AggIntent::Sum { col: None }];
let out = output_schema_for_aggregate(&input, &by, &aggs);
// Output columns: host, region, sum.
assert_eq!(out.columns.len(), 3);
Expand All @@ -425,7 +425,7 @@ mod tests {
fn output_schema_for_aggregate_drops_out_of_range_by_ids() {
let input = infer_source_schema("m");
// schema only has columns 0..=1; ask for by=[5] which is out of range.
let aggs = vec![AggIntent::Sum];
let aggs = vec![AggIntent::Sum { col: None }];
let out = output_schema_for_aggregate(&input, &[5usize], &aggs);
// The out-of-range by id is silently dropped; output has only the agg.
assert_eq!(out.columns.len(), 1);
Expand Down
7 changes: 5 additions & 2 deletions control_plane/src/intent_algebra/cse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ mod tests {
let q = QueryExpr::Aggregate {
by: vec![1],
aggs: vec![AggIntent::Quantile {
col: None,
q: 0.99,
accuracy: AccuracyTarget::Epsilon(0.01),
}],
Expand All @@ -237,6 +238,7 @@ mod tests {
let q1 = QueryExpr::Aggregate {
by: vec![1],
aggs: vec![AggIntent::Quantile {
col: None,
q: 0.99,
accuracy: AccuracyTarget::Epsilon(0.01),
}],
Expand All @@ -246,6 +248,7 @@ mod tests {
let q2 = QueryExpr::Aggregate {
by: vec![1],
aggs: vec![AggIntent::Quantile {
col: None,
q: 0.95,
accuracy: AccuracyTarget::Epsilon(0.01),
}],
Expand Down Expand Up @@ -280,7 +283,7 @@ mod tests {
fn dedupe_subtrees_no_shared_subexpr() {
let q1 = QueryExpr::Aggregate {
by: vec![],
aggs: vec![AggIntent::Sum],
aggs: vec![AggIntent::Sum { col: None }],
having: None,
child: Box::new(windowed_scan()),
};
Expand All @@ -303,7 +306,7 @@ mod tests {
};
let q2 = QueryExpr::Aggregate {
by: vec![],
aggs: vec![AggIntent::Max],
aggs: vec![AggIntent::Max { col: None }],
having: None,
child: Box::new(QueryExpr::Window {
kind: WindowKind::Sliding,
Expand Down
13 changes: 8 additions & 5 deletions control_plane/src/intent_algebra/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -488,25 +488,28 @@ fn agg_func_to_intents(func: &AggFunc) -> Vec<AggIntent> {
AggFunc::Frequency => vec![default_frequency()],
AggFunc::Count => vec![default_frequency()],
AggFunc::Avg => vec![AggIntent::Quantile {
col: None,
q: 0.5,
accuracy: AccuracyTarget::Epsilon(0.01),
}],
AggFunc::Min => vec![AggIntent::Min],
AggFunc::Max => vec![AggIntent::Max],
AggFunc::Min => vec![AggIntent::Min { col: None }],
AggFunc::Max => vec![AggIntent::Max { col: None }],
// StdDev / Variance: legacy carried two quantiles in a single
// Quantile intent; Step α F1 fans them out into two siblings.
AggFunc::StdDev { .. } | AggFunc::Variance { .. } => vec![
AggIntent::Quantile {
col: None,
q: 0.25,
accuracy: AccuracyTarget::Epsilon(0.01),
},
AggIntent::Quantile {
col: None,
q: 0.75,
accuracy: AccuracyTarget::Epsilon(0.01),
},
],
AggFunc::Sum | AggFunc::Rate | AggFunc::Increase | AggFunc::Delta => {
vec![AggIntent::Sum]
vec![AggIntent::Sum { col: None }]
}
AggFunc::Custom(_) => vec![],
}
Expand Down Expand Up @@ -590,7 +593,7 @@ mod tests {
assert_eq!(size, Duration::from_secs(300));
match *child {
CQueryExpr::Aggregate { aggs, child, .. } => {
assert!(matches!(aggs.as_slice(), [AggIntent::Sum]));
assert!(matches!(aggs.as_slice(), [AggIntent::Sum { col: None }]));
assert!(matches!(*child, CQueryExpr::Scan { .. }));
}
other => panic!("expected Aggregate, got {other:?}"),
Expand Down Expand Up @@ -629,7 +632,7 @@ mod tests {
match convert_root(&legacy).unwrap() {
CQueryExpr::Aggregate { by, aggs, .. } => {
assert!(by.is_empty(), "no GROUP BY → empty `by`: {by:?}");
assert!(matches!(aggs.as_slice(), [AggIntent::Sum]));
assert!(matches!(aggs.as_slice(), [AggIntent::Sum { col: None }]));
}
other => panic!("expected Aggregate, got {other:?}"),
}
Expand Down
8 changes: 5 additions & 3 deletions control_plane/src/intent_algebra/query_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,7 @@ mod tests {
let expr = QueryExpr::Aggregate {
by: vec![1], // service
aggs: vec![AggIntent::Quantile {
col: None,
q: 0.99,
accuracy: AccuracyTarget::Epsilon(0.01),
}],
Expand Down Expand Up @@ -860,7 +861,7 @@ mod tests {
}),
child: Box::new(QueryExpr::Aggregate {
by: vec![1],
aggs: vec![AggIntent::Max],
aggs: vec![AggIntent::Max { col: None }],
having: None,
child: Box::new(QueryExpr::Ref {
name: BindingName::new("w"),
Expand Down Expand Up @@ -906,7 +907,7 @@ mod tests {
fn query_expr_aggregate_invalid_by_column() {
let expr = QueryExpr::Aggregate {
by: vec![99],
aggs: vec![AggIntent::Sum],
aggs: vec![AggIntent::Sum { col: None }],
having: None,
child: Box::new(ts_scan()),
};
Expand All @@ -919,6 +920,7 @@ mod tests {
let expr = QueryExpr::Aggregate {
by: vec![1],
aggs: vec![AggIntent::Quantile {
col: None,
q: 0.99,
accuracy: AccuracyTarget::Epsilon(0.01),
}],
Expand All @@ -942,7 +944,7 @@ mod tests {
// consumers can legally share this.
let producer_with_uk = QueryExpr::Aggregate {
by: vec![1],
aggs: vec![AggIntent::Sum],
aggs: vec![AggIntent::Sum { col: None }],
having: None,
child: Box::new(QueryExpr::Window {
kind: WindowKind::Sliding,
Expand Down
16 changes: 8 additions & 8 deletions control_plane/src/intent_algebra/relational.rs
Original file line number Diff line number Diff line change
Expand Up @@ -515,10 +515,10 @@ impl AggFunc {
AggFunc::Count => Some(AggIntent::Count {
accuracy: AccuracyTarget::Exact,
}),
AggFunc::Sum => Some(AggIntent::Sum),
AggFunc::Avg => Some(AggIntent::Avg),
AggFunc::Min => Some(AggIntent::Min),
AggFunc::Max => Some(AggIntent::Max),
AggFunc::Sum => Some(AggIntent::Sum { col: None }),
AggFunc::Avg => Some(AggIntent::Avg { col: None }),
AggFunc::Min => Some(AggIntent::Min { col: None }),
AggFunc::Max => Some(AggIntent::Max { col: None }),
_ => None,
}
}
Expand Down Expand Up @@ -914,7 +914,7 @@ mod tests {

#[test]
fn agg_intent_avg_not_mergeable() {
assert!(!agg_is_mergeable(&AggIntent::Avg));
assert!(!agg_is_mergeable(&AggIntent::Avg { col: None }));
}

#[test]
Expand All @@ -936,9 +936,9 @@ mod tests {

#[test]
fn agg_intent_is_exact() {
assert!(agg_is_exact(&AggIntent::Sum));
assert!(agg_is_exact(&AggIntent::Min));
assert!(agg_is_exact(&AggIntent::Max));
assert!(agg_is_exact(&AggIntent::Sum { col: None }));
assert!(agg_is_exact(&AggIntent::Min { col: None }));
assert!(agg_is_exact(&AggIntent::Max { col: None }));
assert!(!agg_is_exact(&default_cardinality()));
}
}
8 changes: 6 additions & 2 deletions control_plane/src/optimizer/cost/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -704,7 +704,10 @@ fn node_cost_aggregate(by: &[usize], aggs: &[AggIntent], _input: &Schema) -> f64
#[allow(dead_code)]
fn intent_cost(intent: &AggIntent) -> f64 {
match intent {
AggIntent::Sum | AggIntent::Min | AggIntent::Max | AggIntent::Avg => 5.0,
AggIntent::Sum { .. }
| AggIntent::Min { .. }
| AggIntent::Max { .. }
| AggIntent::Avg { .. } => 5.0,
AggIntent::Count { .. } => 5.0,
AggIntent::Quantile { .. } => 20.0,
AggIntent::Cardinality { .. } => 15.0,
Expand Down Expand Up @@ -777,6 +780,7 @@ mod workload_cost_tests {
QueryExpr::Aggregate {
by: vec![],
aggs: vec![AggIntent::Quantile {
col: None,
q,
accuracy: AccuracyTarget::Epsilon(0.01),
}],
Expand All @@ -789,7 +793,7 @@ mod workload_cost_tests {
fn max_root(child: QueryExpr) -> QueryExpr {
QueryExpr::Aggregate {
by: vec![],
aggs: vec![AggIntent::Max],
aggs: vec![AggIntent::Max { col: None }],
having: None,
child: Box::new(child),
}
Expand Down
2 changes: 2 additions & 0 deletions control_plane/src/optimizer/rules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,12 @@ pub fn bind_workload_typed(w: &QueryWorkload) -> Option<crate::sketch_algebra::P
// PromQL `topk(10, …)` lowering uses.
let intent = match statistic {
StatisticClass::Quantile => L3AggIntent::Quantile {
col: None,
q: w.quantiles.first().copied().unwrap_or(0.99),
accuracy: intent_accuracy,
},
StatisticClass::Cardinality => L3AggIntent::Cardinality {
col: None,
accuracy: intent_accuracy,
},
StatisticClass::Frequency => L3AggIntent::Frequency {
Expand Down
44 changes: 32 additions & 12 deletions control_plane/src/physical/allocator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@ impl SketchAllocator {
let intent = aggs[0].clone();

// Exact non-mergeable (Avg) → always Db.
if matches!(intent, AggIntent::Avg) {
if matches!(intent, AggIntent::Avg { .. }) {
return PlanNode {
expr: QueryExpr::Aggregate {
by,
Expand Down Expand Up @@ -673,26 +673,46 @@ fn estimated_sketch_memory(op: &AggIntent) -> f64 {
fn canonical_intent_kind_str(intent: &AggIntent) -> &'static str {
match intent {
AggIntent::Count { .. } => "count",
AggIntent::Sum => "sum",
AggIntent::Min => "min",
AggIntent::Max => "max",
AggIntent::Avg => "avg",
AggIntent::Sum { .. } => "sum",
AggIntent::Min { .. } => "min",
AggIntent::Max { .. } => "max",
AggIntent::Avg { .. } => "avg",
AggIntent::StdDev { .. } => "stddev",
AggIntent::Variance { .. } => "variance",
AggIntent::Quantile { .. } => "quantile",
AggIntent::TopK { .. } => "topk",
AggIntent::Cardinality { .. } => "cardinality",
AggIntent::Frequency { .. } => "frequency",
AggIntent::Rate { .. } => "rate",
AggIntent::Increase { .. } => "increase",
AggIntent::Absent => "absent",
AggIntent::Present => "present",
AggIntent::AbsentOverTime => "absent_over_time",
AggIntent::PresentOverTime => "present_over_time",
AggIntent::Delta { .. } => "delta",
AggIntent::Deriv { .. } => "deriv",
AggIntent::PredictLinear { .. } => "predict_linear",
AggIntent::HoltWinters { .. } => "holt_winters",
AggIntent::Idelta { .. } => "idelta",
AggIntent::Irate { .. } => "irate",
AggIntent::DoubleExpSmoothing { .. } => "double_exponential_smoothing",
AggIntent::IDelta { .. } => "idelta",
AggIntent::Resets { .. } => "resets",
AggIntent::Changes { .. } => "changes",
AggIntent::HistogramCount => "histogram_count",
AggIntent::HistogramSum => "histogram_sum",
AggIntent::HistogramAvg => "histogram_avg",
AggIntent::HistogramStdDev => "histogram_stddev",
AggIntent::HistogramStdVar => "histogram_stdvar",
AggIntent::HistogramFraction { .. } => "histogram_fraction",
AggIntent::HistogramQuantile { .. } => "histogram_quantile",
AggIntent::Math(_) => "math",
AggIntent::TimeFn(_) => "time_fn",
AggIntent::Group => "group",
AggIntent::CountValues { .. } => "count_values",
AggIntent::LastOverTime => "last_over_time",
AggIntent::FirstOverTime => "first_over_time",
AggIntent::MadOverTime => "mad_over_time",
AggIntent::TsOfMinOverTime => "ts_of_min_over_time",
AggIntent::TsOfMaxOverTime => "ts_of_max_over_time",
AggIntent::TsOfFirstOverTime => "ts_of_first_over_time",
AggIntent::TsOfLastOverTime => "ts_of_last_over_time",
}
}

Expand Down Expand Up @@ -808,7 +828,7 @@ mod tests {

#[test]
fn exact_avg_goes_to_db() {
let node = alloc(unlimited(), agg(AggIntent::Avg));
let node = alloc(unlimited(), agg(AggIntent::Avg { col: None }));
assert_eq!(node.stage, PipelineStage::Db);
assert_eq!(node.mode, ExecutionMode::Exact);
}
Expand All @@ -817,7 +837,7 @@ mod tests {

#[test]
fn exact_sum_goes_to_backend() {
let node = alloc(unlimited(), agg(AggIntent::Sum));
let node = alloc(unlimited(), agg(AggIntent::Sum { col: None }));
assert_eq!(node.stage, PipelineStage::Backend);
assert_eq!(node.mode, ExecutionMode::Exact);
}
Expand Down Expand Up @@ -886,7 +906,7 @@ mod tests {
fn multi_intent_aggregate_goes_to_db() {
let expr = QueryExpr::Aggregate {
by: vec![],
aggs: vec![AggIntent::Sum, AggIntent::Min],
aggs: vec![AggIntent::Sum { col: None }, AggIntent::Min { col: None }],
having: None,
child: Box::new(scan("m")),
};
Expand Down
Loading