Skip to content
Merged
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
132 changes: 48 additions & 84 deletions asap-common/dependencies/rs/asap_types/src/capability_matching.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,21 +62,17 @@ fn is_key_agg_type(agg_type: AggregationType) -> bool {

/// Window compatibility: can `config` serve a query needing `data_range_ms`?
///
/// - `None` (spatial-only): always compatible.
/// - Tumbling: `data_range_ms` must be a positive integer multiple of `window_size_ms`.
/// - Sliding: `data_range_ms` must equal `window_size_ms` exactly (a sliding window
/// precomputes one fixed range per timestamp; overlapping windows cannot be merged).
pub fn window_compatible(config: &AggregationConfig, data_range_ms: Option<u64>) -> bool {
let Some(range) = data_range_ms else {
return true;
};
pub fn window_compatible(config: &AggregationConfig, data_range_ms: u64) -> bool {
let window_ms = config.window_size_ms;
if window_ms == 0 || range == 0 {
if window_ms == 0 || data_range_ms == 0 {
return false;
}
match config.window_type {
WindowType::Sliding => range == window_ms,
WindowType::Tumbling => range % window_ms == 0,
WindowType::Sliding => data_range_ms == window_ms,
WindowType::Tumbling => data_range_ms.is_multiple_of(window_ms),
}
}

Expand Down Expand Up @@ -339,7 +335,7 @@ mod tests {
fn req(
metric: &str,
stats: &[Statistic],
data_range_ms: Option<u64>,
data_range_ms: u64,
grouping: &[&str],
spatial_filter: &str,
) -> QueryRequirements {
Expand Down Expand Up @@ -373,10 +369,8 @@ mod tests {
&[],
"",
));
let result = find_compatible_aggregation(
&configs,
&req("cpu", &[Statistic::Sum], Some(300_000), &[], ""),
);
let result =
find_compatible_aggregation(&configs, &req("cpu", &[Statistic::Sum], 300_000, &[], ""));
assert!(result.is_some());
assert_eq!(result.unwrap().aggregation_id_for_value, 1);
}
Expand All @@ -396,11 +390,11 @@ mod tests {
// quantile value (0.5 or 0.9) is NOT part of QueryRequirements — both should find the same config
let r1 = find_compatible_aggregation(
&configs,
&req("lat", &[Statistic::Quantile], Some(300_000), &[], ""),
&req("lat", &[Statistic::Quantile], 300_000, &[], ""),
);
let r2 = find_compatible_aggregation(
&configs,
&req("lat", &[Statistic::Quantile], Some(300_000), &[], ""),
&req("lat", &[Statistic::Quantile], 300_000, &[], ""),
);
assert_eq!(r1.unwrap().aggregation_id_for_value, 2);
assert_eq!(r2.unwrap().aggregation_id_for_value, 2);
Expand All @@ -420,7 +414,7 @@ mod tests {
));
let result = find_compatible_aggregation(
&configs,
&req("lat", &[Statistic::Quantile], Some(300_000), &[], ""),
&req("lat", &[Statistic::Quantile], 300_000, &[], ""),
);
assert_eq!(result.unwrap().aggregation_id_for_value, 3);
}
Expand All @@ -437,10 +431,8 @@ mod tests {
&[],
"",
));
let result = find_compatible_aggregation(
&configs,
&req("mem", &[Statistic::Sum], Some(300_000), &[], ""),
);
let result =
find_compatible_aggregation(&configs, &req("mem", &[Statistic::Sum], 300_000, &[], ""));
assert!(result.is_none());
}

Expand All @@ -456,10 +448,8 @@ mod tests {
&[],
"",
));
let result = find_compatible_aggregation(
&configs,
&req("cpu", &[Statistic::Sum], Some(300_000), &[], ""),
);
let result =
find_compatible_aggregation(&configs, &req("cpu", &[Statistic::Sum], 300_000, &[], ""));
assert!(result.is_none());
}

Expand All @@ -477,10 +467,8 @@ mod tests {
&[],
"",
));
let result = find_compatible_aggregation(
&configs,
&req("cpu", &[Statistic::Sum], Some(300_000), &[], ""),
);
let result =
find_compatible_aggregation(&configs, &req("cpu", &[Statistic::Sum], 300_000, &[], ""));
assert!(result.is_some());
}

Expand All @@ -497,10 +485,8 @@ mod tests {
&[],
"",
));
let result = find_compatible_aggregation(
&configs,
&req("cpu", &[Statistic::Sum], Some(900_000), &[], ""),
);
let result =
find_compatible_aggregation(&configs, &req("cpu", &[Statistic::Sum], 900_000, &[], ""));
assert!(result.is_some());
}

Expand All @@ -517,10 +503,8 @@ mod tests {
&[],
"",
));
let result = find_compatible_aggregation(
&configs,
&req("cpu", &[Statistic::Sum], Some(600_000), &[], ""),
);
let result =
find_compatible_aggregation(&configs, &req("cpu", &[Statistic::Sum], 600_000, &[], ""));
assert!(result.is_none());
}

Expand All @@ -536,10 +520,8 @@ mod tests {
&[],
"",
));
let result = find_compatible_aggregation(
&configs,
&req("cpu", &[Statistic::Sum], Some(300_000), &[], ""),
);
let result =
find_compatible_aggregation(&configs, &req("cpu", &[Statistic::Sum], 300_000, &[], ""));
assert!(result.is_some());
}

Expand All @@ -556,10 +538,8 @@ mod tests {
&[],
"",
));
let result = find_compatible_aggregation(
&configs,
&req("cpu", &[Statistic::Sum], Some(600_000), &[], ""),
);
let result =
find_compatible_aggregation(&configs, &req("cpu", &[Statistic::Sum], 600_000, &[], ""));
assert!(result.is_none());
}

Expand All @@ -575,28 +555,26 @@ mod tests {
make_config(2, "cpu", "Sum", "", 900_000, "tumbling", &[], ""),
);
// 900_000 ms is divisible by both 300_000 ms and 900_000 ms — prefer 900_000 ms
let result = find_compatible_aggregation(
&configs,
&req("cpu", &[Statistic::Sum], Some(900_000), &[], ""),
);
let result =
find_compatible_aggregation(&configs, &req("cpu", &[Statistic::Sum], 900_000, &[], ""));
assert_eq!(result.unwrap().aggregation_id_for_value, 2);
}

#[test]
fn spatial_only_no_range() {
// data_range_ms = None → any window size is compatible
// spatial-only queries use data_range_ms = scrape interval; window must match
let configs = single_config(make_config(
1,
"cpu",
"Sum",
"",
900_000,
15_000,
"tumbling",
&[],
"",
));
let result =
find_compatible_aggregation(&configs, &req("cpu", &[Statistic::Sum], None, &[], ""));
find_compatible_aggregation(&configs, &req("cpu", &[Statistic::Sum], 15_000, &[], ""));
assert!(result.is_some());
}

Expand All @@ -616,7 +594,7 @@ mod tests {
));
let result = find_compatible_aggregation(
&configs,
&req("cpu", &[Statistic::Sum], Some(300_000), &["job"], ""),
&req("cpu", &[Statistic::Sum], 300_000, &["job"], ""),
);
assert!(result.is_some());
}
Expand All @@ -636,7 +614,7 @@ mod tests {
));
let result = find_compatible_aggregation(
&configs,
&req("cpu", &[Statistic::Sum], Some(300_000), &["job"], ""),
&req("cpu", &[Statistic::Sum], 300_000, &["job"], ""),
);
assert!(result.is_none());
}
Expand All @@ -655,7 +633,7 @@ mod tests {
));
let result = find_compatible_aggregation(
&configs,
&req("cpu", &[Statistic::Sum], Some(300_000), &["job"], ""),
&req("cpu", &[Statistic::Sum], 300_000, &["job"], ""),
);
assert!(result.is_none());
}
Expand All @@ -674,10 +652,8 @@ mod tests {
&[],
"",
));
let result = find_compatible_aggregation(
&configs,
&req("cpu", &[Statistic::Sum], Some(300_000), &[], ""),
);
let result =
find_compatible_aggregation(&configs, &req("cpu", &[Statistic::Sum], 300_000, &[], ""));
assert!(result.is_some());
}

Expand All @@ -694,10 +670,8 @@ mod tests {
&[],
"env=prod",
));
let result = find_compatible_aggregation(
&configs,
&req("cpu", &[Statistic::Sum], Some(300_000), &[], ""),
);
let result =
find_compatible_aggregation(&configs, &req("cpu", &[Statistic::Sum], 300_000, &[], ""));
assert!(result.is_none());
}

Expand All @@ -715,7 +689,7 @@ mod tests {
));
let result = find_compatible_aggregation(
&configs,
&req("cpu", &[Statistic::Sum], Some(300_000), &[], "env=prod"),
&req("cpu", &[Statistic::Sum], 300_000, &[], "env=prod"),
);
assert!(result.is_some());
}
Expand All @@ -734,7 +708,7 @@ mod tests {
));
let result = find_compatible_aggregation(
&configs,
&req("cpu", &[Statistic::Sum], Some(300_000), &[], "env=staging"),
&req("cpu", &[Statistic::Sum], 300_000, &[], "env=staging"),
);
assert!(result.is_none());
}
Expand All @@ -753,10 +727,8 @@ mod tests {
&[],
"",
));
let result = find_compatible_aggregation(
&configs,
&req("cpu", &[Statistic::Min], Some(300_000), &[], ""),
);
let result =
find_compatible_aggregation(&configs, &req("cpu", &[Statistic::Min], 300_000, &[], ""));
assert!(result.is_some());
}

Expand All @@ -773,10 +745,8 @@ mod tests {
&[],
"",
));
let result = find_compatible_aggregation(
&configs,
&req("cpu", &[Statistic::Max], Some(300_000), &[], ""),
);
let result =
find_compatible_aggregation(&configs, &req("cpu", &[Statistic::Max], 300_000, &[], ""));
assert!(result.is_none());
}

Expand Down Expand Up @@ -813,7 +783,7 @@ mod tests {
);
let result = find_compatible_aggregation(
&configs,
&req("req", &[Statistic::Topk], Some(300_000), &[], ""),
&req("req", &[Statistic::Topk], 300_000, &[], ""),
);
let info = result.unwrap();
assert_eq!(info.aggregation_id_for_value, 10);
Expand All @@ -835,7 +805,7 @@ mod tests {
));
let result = find_compatible_aggregation(
&configs,
&req("req", &[Statistic::Topk], Some(300_000), &[], ""),
&req("req", &[Statistic::Topk], 300_000, &[], ""),
);
assert!(result.is_none());
}
Expand Down Expand Up @@ -867,7 +837,7 @@ mod tests {
&req(
"cpu",
&[Statistic::Sum, Statistic::Count],
Some(300_000),
300_000,
&["job"],
"",
),
Expand Down Expand Up @@ -895,13 +865,7 @@ mod tests {
));
let result = find_compatible_aggregation(
&configs,
&req(
"peers",
&[Statistic::Cardinality],
Some(1_000),
&["srcip"],
"",
),
&req("peers", &[Statistic::Cardinality], 1_000, &["srcip"], ""),
);
let info = result.expect("HLL should serve Cardinality");
assert_eq!(info.aggregation_id_for_value, 42);
Expand Down Expand Up @@ -953,7 +917,7 @@ mod tests {
&req(
"cpu",
&[Statistic::Sum, Statistic::Count],
Some(300_000),
300_000,
&["job"],
"",
),
Expand Down Expand Up @@ -1003,7 +967,7 @@ mod tests {
}

fn topk_req(metric: &str, count_events: Option<bool>) -> QueryRequirements {
let mut r = req(metric, &[Statistic::Topk], Some(1_000), &[], "");
let mut r = req(metric, &[Statistic::Topk], 1_000, &[], "");
r.topk_count_events = count_events;
r
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ pub struct QueryRequirements {
/// window_size and grouping_labels.
pub statistics: Vec<Statistic>,
/// The span of historical data the query reads, in milliseconds.
/// None for spatial-only queries (no time range).
pub data_range_ms: Option<u64>,
/// For spatial-only queries (no time range), set to the scrape interval.
pub data_range_ms: u64,
/// GROUP BY labels expected in the query result.
pub grouping_labels: KeyByLabelNames,
/// Normalized label filter (produced by normalize_spatial_filter).
Expand All @@ -48,6 +48,7 @@ pub fn build_query_requirements_promql(
match_result: &PromQLMatchResult,
pattern_type: QueryPatternType,
metric_schema: &PromQLSchema,
data_ingestion_interval_ms: u64,
) -> Option<QueryRequirements> {
let (metric, spatial_filter) = get_metric_and_spatial_filter(match_result);

Expand All @@ -63,12 +64,12 @@ pub fn build_query_requirements_promql(
.ok()?;

let data_range_ms = match pattern_type {
QueryPatternType::OnlySpatial => None,
QueryPatternType::OnlySpatial => data_ingestion_interval_ms,
// promql-parser supports a literal `ms` duration suffix (e.g. `[500ms]`),
// so .num_seconds() would truncate sub-second ranges to 0.
_ => match_result
.get_range_duration()
.map(|d| d.num_milliseconds() as u64),
.map(|d| d.num_milliseconds() as u64)?,
};

let all_labels = metric_schema
Expand Down
Loading
Loading