diff --git a/asap-planner-rs/src/bin/candidate_gen_dump.rs b/asap-planner-rs/src/bin/candidate_gen_dump.rs index c8b07bef..80eb59c4 100644 --- a/asap-planner-rs/src/bin/candidate_gen_dump.rs +++ b/asap-planner-rs/src/bin/candidate_gen_dump.rs @@ -61,7 +61,7 @@ fn main() -> anyhow::Result<()> { query_string: q.clone(), t_repeat_ms: qg.repetition_delay_ms, accuracy_sla: qg.controller_options.accuracy_sla, - latency_sla: qg.controller_options.latency_sla, + latency_sla_ms: qg.controller_options.latency_sla_ms, }) }) .collect(); @@ -71,13 +71,13 @@ fn main() -> anyhow::Result<()> { for (i, aqe) in aqes.iter().enumerate() { println!( - "\n--- Item #{i}: metric={} stat={:?} range={}ms T={}ms accuracy_sla={} latency_sla={} freq={:.4}Hz ---", + "\n--- Item #{i}: metric={} stat={:?} range={}ms T={}ms accuracy_sla={} latency_sla_ms={:?} freq={:.4}Hz ---", aqe.requirements.metric, aqe.requirements.statistics, aqe.requirements.data_range_ms, aqe.t_repeat_ms, aqe.accuracy_sla, - aqe.latency_sla, + aqe.latency_sla_ms, aqe.query_frequency_hz, ); println!(" queries: {:?}", aqe.query_strings); diff --git a/asap-planner-rs/src/config/input.rs b/asap-planner-rs/src/config/input.rs index 8e260323..c76b55cf 100644 --- a/asap-planner-rs/src/config/input.rs +++ b/asap-planner-rs/src/config/input.rs @@ -32,16 +32,16 @@ pub struct ControllerConfig { } impl ControllerConfig { - /// Warn if any query group has both SLAs at 0.0 (the serde Default), - /// which indicates `controller_options` was omitted from the config. + /// Warn if any query group still has the serde-default SLAs, which + /// indicates `controller_options` was omitted from the config. pub fn warn_default_slas(&self) { for qg in &self.query_groups { let opts = &qg.controller_options; - if opts.accuracy_sla == 0.0 && opts.latency_sla == 0.0 { + if opts.accuracy_sla == 0.0 && opts.latency_sla_ms.is_none() { warn!( query_group_id = ?qg.id, "controller_options not set in query group; \ - accuracy_sla=0.0 and latency_sla=0.0 will be used — \ + accuracy_sla=0.0 and no latency_sla_ms limit will be used — \ add controller_options to your config" ); } @@ -63,6 +63,7 @@ impl ControllerConfig { } #[derive(Debug, Clone, Deserialize)] +#[serde(deny_unknown_fields)] pub struct QueryGroup { pub id: Option, pub queries: Vec, @@ -79,11 +80,13 @@ pub struct QueryGroup { } #[derive(Debug, Clone, Deserialize, Default)] +#[serde(deny_unknown_fields)] pub struct ControllerOptions { #[serde(deserialize_with = "deserialize_finite_f64")] pub accuracy_sla: f64, - #[serde(deserialize_with = "deserialize_finite_f64")] - pub latency_sla: f64, + /// Maximum modeled query latency in milliseconds; `None` means no limit. + #[serde(default, deserialize_with = "deserialize_optional_positive_f64")] + pub latency_sla_ms: Option, } fn deserialize_finite_f64<'de, D>(deserializer: D) -> Result @@ -98,6 +101,18 @@ where } } +fn deserialize_optional_positive_f64<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + match Option::::deserialize(deserializer)? { + Some(value) if !(value.is_finite() && value > 0.0) => Err(serde::de::Error::custom( + "must be a finite number greater than zero", + )), + value => Ok(value), + } +} + fn deserialize_positive_u64<'de, D>(deserializer: D) -> Result where D: Deserializer<'de>, @@ -211,6 +226,7 @@ pub struct HllParams { } #[derive(Debug, Clone, Deserialize)] +#[serde(deny_unknown_fields)] pub struct SQLControllerConfig { pub query_groups: Vec, pub tables: Vec, @@ -220,6 +236,7 @@ pub struct SQLControllerConfig { } #[derive(Debug, Clone, Deserialize)] +#[serde(deny_unknown_fields)] pub struct SQLQueryGroup { pub id: Option, pub queries: Vec, @@ -237,6 +254,7 @@ pub struct TableDefinition { } #[derive(Debug, Clone, Deserialize)] +#[serde(deny_unknown_fields)] pub struct ElasticDSLControllerConfig { pub query_groups: Vec, pub sketch_parameters: Option, @@ -244,6 +262,7 @@ pub struct ElasticDSLControllerConfig { } #[derive(Debug, Clone, Deserialize)] +#[serde(deny_unknown_fields)] pub struct ElasticDSLQueryGroup { pub id: Option, pub queries: Vec, @@ -265,7 +284,6 @@ query_groups: repetition_delay_ms: 60000 controller_options: accuracy_sla: .nan - latency_sla: .inf "#; let error = serde_yaml::from_str::(yaml) @@ -283,13 +301,126 @@ query_groups: repetition_delay_ms: 60000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 250.0 "#; let config: ControllerConfig = serde_yaml::from_str(yaml).unwrap(); let options = &config.query_groups[0].controller_options; assert_eq!(options.accuracy_sla, 0.99); - assert_eq!(options.latency_sla, 1.0); + assert_eq!(options.latency_sla_ms, Some(250.0)); + } + + #[test] + fn omitted_latency_sla_ms_means_no_limit() { + let yaml = r#" +query_groups: + - queries: [sum(metric)] + repetition_delay_ms: 60000 + controller_options: + accuracy_sla: 0.99 +"#; + + let config: ControllerConfig = serde_yaml::from_str(yaml).unwrap(); + assert_eq!( + config.query_groups[0].controller_options.latency_sla_ms, + None + ); + } + + // Reject the unitless `latency_sla` key so it can't be silently ignored. + #[test] + fn rejects_legacy_latency_sla_key() { + let yaml = r#" +query_groups: + - queries: [sum(metric)] + repetition_delay_ms: 60000 + controller_options: + accuracy_sla: 0.99 + latency_sla: 1.0 +"#; + + let error = serde_yaml::from_str::(yaml) + .expect_err("legacy latency_sla key must be rejected") + .to_string(); + assert!(error.contains("unknown field `latency_sla`"), "{error}"); + } + + #[test] + fn rejects_misspelled_controller_options() { + // A typo would otherwise drop the whole SLA block to its defaults. + let yaml = r#" +query_groups: + - queries: [sum(metric)] + repetition_delay_ms: 60000 + controler_options: + accuracy_sla: 0.99 +"#; + + let error = serde_yaml::from_str::(yaml) + .expect_err("unknown query group key must be rejected") + .to_string(); + assert!( + error.contains("unknown field `controler_options`"), + "{error}" + ); + } + + #[test] + fn sql_and_elastic_configs_reject_unknown_group_keys() { + let sql = r#" +tables: [] +query_groups: + - queries: ["SELECT 1"] + repetition_delay_ms: 60000 + latency_sla: 1.0 + controller_options: + accuracy_sla: 0.99 +"#; + let error = serde_yaml::from_str::(sql) + .expect_err("unknown SQL group key must be rejected") + .to_string(); + assert!(error.contains("unknown field `latency_sla`"), "{error}"); + + let elastic = r#" +query_groups: + - queries: ["{}"] + repetition_delay_ms: 60000 + index: i + time_field: t + controler_options: + accuracy_sla: 0.99 +"#; + let error = serde_yaml::from_str::(elastic) + .expect_err("unknown Elastic group key must be rejected") + .to_string(); + assert!( + error.contains("unknown field `controler_options`"), + "{error}" + ); + } + + #[test] + fn rejects_non_positive_or_non_finite_latency_sla_ms() { + for bad in ["0.0", "-5.0", ".inf", ".nan"] { + let yaml = format!( + r#" +query_groups: + - queries: [sum(metric)] + repetition_delay_ms: 60000 + controller_options: + accuracy_sla: 0.99 + latency_sla_ms: {bad} +"# + ); + + let error = serde_yaml::from_str::(&yaml) + .expect_err("invalid latency_sla_ms must be rejected") + .to_string(); + assert!( + error.contains("must be a finite number greater than zero"), + "{bad}: {error}" + ); + } } #[test] diff --git a/asap-planner-rs/src/optimizer/aqe_extractor.rs b/asap-planner-rs/src/optimizer/aqe_extractor.rs index 15ed2064..ac825a38 100644 --- a/asap-planner-rs/src/optimizer/aqe_extractor.rs +++ b/asap-planner-rs/src/optimizer/aqe_extractor.rs @@ -20,7 +20,7 @@ pub struct RQE { pub query_string: String, pub t_repeat_ms: u64, pub accuracy_sla: f64, - pub latency_sla: f64, + pub latency_sla_ms: Option, } /// Stable key for merging identical optimizer demand. @@ -36,7 +36,7 @@ struct OptimizerItemKey { topk_by_labels: Option, t_repeat_ms: u64, accuracy_sla_bits: u64, - latency_sla_bits: u64, + latency_sla_ms_bits: Option, } impl OptimizerItemKey { @@ -51,7 +51,7 @@ impl OptimizerItemKey { topk_by_labels: req.topk_by_labels.clone(), t_repeat_ms: rqe.t_repeat_ms, accuracy_sla_bits: normalized_f64_bits(rqe.accuracy_sla), - latency_sla_bits: normalized_f64_bits(rqe.latency_sla), + latency_sla_ms_bits: rqe.latency_sla_ms.map(normalized_f64_bits), } } } @@ -110,7 +110,7 @@ pub fn extract_aqes( query_frequency_hz, t_repeat_ms: key.t_repeat_ms, accuracy_sla: f64::from_bits(key.accuracy_sla_bits), - latency_sla: f64::from_bits(key.latency_sla_bits), + latency_sla_ms: key.latency_sla_ms_bits.map(f64::from_bits), }, ) .collect()) @@ -236,7 +236,7 @@ mod tests { query_string: query.to_string(), t_repeat_ms: t_ms, accuracy_sla: 0.0, - latency_sla: 0.0, + latency_sla_ms: None, } } @@ -309,10 +309,24 @@ mod tests { assert_eq!(items.len(), 2); } + #[test] + fn different_latency_slas_become_distinct_items() { + let unlimited = rqe("sum_over_time(metric[5m])", 60_000); + let mut fast = rqe("sum_over_time(metric[5m])", 60_000); + fast.latency_sla_ms = Some(100.0); + let mut slow = rqe("sum_over_time(metric[5m])", 60_000); + slow.latency_sla_ms = Some(1_000.0); + + let items = extract_aqes(&[unlimited, fast, slow], &empty_schema(), 15_000).unwrap(); + let mut slas: Vec<_> = items.iter().map(|item| item.latency_sla_ms).collect(); + slas.sort_by(|a, b| a.partial_cmp(b).unwrap()); + assert_eq!(slas, vec![None, Some(100.0), Some(1_000.0)]); + } + #[test] fn signed_zero_slas_merge_into_one_item() { let mut negative_zero = rqe("sum_over_time(metric[5m])", 60_000); - negative_zero.latency_sla = -0.0; + negative_zero.accuracy_sla = -0.0; let items = extract_aqes( &[negative_zero, rqe("sum_over_time(metric[5m])", 60_000)], &empty_schema(), diff --git a/asap-planner-rs/src/optimizer/candidate_gen.rs b/asap-planner-rs/src/optimizer/candidate_gen.rs index 53e46223..b3aa0f88 100644 --- a/asap-planner-rs/src/optimizer/candidate_gen.rs +++ b/asap-planner-rs/src/optimizer/candidate_gen.rs @@ -408,7 +408,7 @@ mod tests { query_frequency_hz: 1.0 / 60.0, t_repeat_ms: min_t, accuracy_sla: 0.0, - latency_sla: 0.0, + latency_sla_ms: None, } } diff --git a/asap-planner-rs/src/optimizer/cost_model.rs b/asap-planner-rs/src/optimizer/cost_model.rs index f663bb70..fe9b0f15 100644 --- a/asap-planner-rs/src/optimizer/cost_model.rs +++ b/asap-planner-rs/src/optimizer/cost_model.rs @@ -216,7 +216,7 @@ mod tests { query_frequency_hz: 1.0 / 60.0, t_repeat_ms: min_t, accuracy_sla: 0.0, - latency_sla: 0.0, + latency_sla_ms: None, } } diff --git a/asap-planner-rs/src/optimizer/error.rs b/asap-planner-rs/src/optimizer/error.rs index 9269c5c7..da7933b5 100644 --- a/asap-planner-rs/src/optimizer/error.rs +++ b/asap-planner-rs/src/optimizer/error.rs @@ -24,6 +24,6 @@ pub struct UnservableItem { pub data_range_ms: u64, pub t_repeat_ms: u64, pub accuracy_sla: f64, - pub latency_sla: f64, + pub latency_sla_ms: Option, pub reason: String, } diff --git a/asap-planner-rs/src/optimizer/greedy.rs b/asap-planner-rs/src/optimizer/greedy.rs index dff2b44c..51ab0f8e 100644 --- a/asap-planner-rs/src/optimizer/greedy.rs +++ b/asap-planner-rs/src/optimizer/greedy.rs @@ -66,7 +66,7 @@ pub fn greedy_assign( data_range_ms: aqe.requirements.data_range_ms, t_repeat_ms: aqe.t_repeat_ms, accuracy_sla: aqe.accuracy_sla, - latency_sla: aqe.latency_sla, + latency_sla_ms: aqe.latency_sla_ms, reason: "no candidate remained after structural and atomic-cost filters".into(), }); continue; @@ -139,7 +139,7 @@ mod tests { query_frequency_hz: freq_hz, t_repeat_ms: min_t, accuracy_sla: 0.0, - latency_sla: 0.0, + latency_sla_ms: None, } } @@ -196,7 +196,7 @@ mod tests { query_frequency_hz: 1.0 / 60.0, t_repeat_ms: 60_000, accuracy_sla: 0.0, - latency_sla: 0.0, + latency_sla_ms: None, }; let error = greedy_assign( vec![aqe.clone()], diff --git a/asap-planner-rs/src/optimizer/label_set_facts.rs b/asap-planner-rs/src/optimizer/label_set_facts.rs index d268b535..418899fd 100644 --- a/asap-planner-rs/src/optimizer/label_set_facts.rs +++ b/asap-planner-rs/src/optimizer/label_set_facts.rs @@ -308,7 +308,7 @@ mod tests { query_frequency_hz: 1.0 / 60.0, t_repeat_ms: 60_000, accuracy_sla: 0.0, - latency_sla: 0.0, + latency_sla_ms: None, } } diff --git a/asap-planner-rs/src/optimizer/pipeline.rs b/asap-planner-rs/src/optimizer/pipeline.rs index cc40dfc6..8a817d68 100644 --- a/asap-planner-rs/src/optimizer/pipeline.rs +++ b/asap-planner-rs/src/optimizer/pipeline.rs @@ -104,7 +104,7 @@ fn config_to_rqes(config: &ControllerConfig) -> Vec { query_string: q.clone(), t_repeat_ms: qg.repetition_delay_ms, accuracy_sla: qg.controller_options.accuracy_sla, - latency_sla: qg.controller_options.latency_sla, + latency_sla_ms: qg.controller_options.latency_sla_ms, }) }) .collect() diff --git a/asap-planner-rs/src/optimizer/solution.rs b/asap-planner-rs/src/optimizer/solution.rs index ba866faf..2dfa9930 100644 --- a/asap-planner-rs/src/optimizer/solution.rs +++ b/asap-planner-rs/src/optimizer/solution.rs @@ -26,8 +26,9 @@ pub struct OptimizerItem { /// Required accuracy for every RQE contributing to this item. pub accuracy_sla: f64, - /// Required query latency for every RQE contributing to this item. - pub latency_sla: f64, + /// Maximum query latency (ms) for every RQE contributing to this item; + /// `None` means no limit. + pub latency_sla_ms: Option, } /// How an optimizer item is answered from its assigned streaming config. diff --git a/asap-planner-rs/tests/comparison/test_data/configs/binary_arithmetic.yaml b/asap-planner-rs/tests/comparison/test_data/configs/binary_arithmetic.yaml index 30d55df0..6c9209ed 100644 --- a/asap-planner-rs/tests/comparison/test_data/configs/binary_arithmetic.yaml +++ b/asap-planner-rs/tests/comparison/test_data/configs/binary_arithmetic.yaml @@ -5,7 +5,7 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 metrics: - metric: "errors_total" labels: ["instance", "job"] diff --git a/asap-planner-rs/tests/comparison/test_data/configs/binary_arithmetic_dedup.yaml b/asap-planner-rs/tests/comparison/test_data/configs/binary_arithmetic_dedup.yaml index 18b8fd4d..a2469b0b 100644 --- a/asap-planner-rs/tests/comparison/test_data/configs/binary_arithmetic_dedup.yaml +++ b/asap-planner-rs/tests/comparison/test_data/configs/binary_arithmetic_dedup.yaml @@ -6,7 +6,7 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 metrics: - metric: "errors_total" labels: ["instance", "job"] diff --git a/asap-planner-rs/tests/comparison/test_data/configs/binary_arithmetic_nested.yaml b/asap-planner-rs/tests/comparison/test_data/configs/binary_arithmetic_nested.yaml index 879d918a..848798f9 100644 --- a/asap-planner-rs/tests/comparison/test_data/configs/binary_arithmetic_nested.yaml +++ b/asap-planner-rs/tests/comparison/test_data/configs/binary_arithmetic_nested.yaml @@ -5,7 +5,7 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 metrics: - metric: "a_total" labels: ["instance"] diff --git a/asap-planner-rs/tests/comparison/test_data/configs/binary_arithmetic_non_acceleratable.yaml b/asap-planner-rs/tests/comparison/test_data/configs/binary_arithmetic_non_acceleratable.yaml index 2542bb7f..71641492 100644 --- a/asap-planner-rs/tests/comparison/test_data/configs/binary_arithmetic_non_acceleratable.yaml +++ b/asap-planner-rs/tests/comparison/test_data/configs/binary_arithmetic_non_acceleratable.yaml @@ -5,7 +5,7 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 metrics: - metric: "errors_total" labels: ["instance"] diff --git a/asap-planner-rs/tests/comparison/test_data/configs/binary_arithmetic_scalar.yaml b/asap-planner-rs/tests/comparison/test_data/configs/binary_arithmetic_scalar.yaml index 63d2e22b..ba31242c 100644 --- a/asap-planner-rs/tests/comparison/test_data/configs/binary_arithmetic_scalar.yaml +++ b/asap-planner-rs/tests/comparison/test_data/configs/binary_arithmetic_scalar.yaml @@ -5,7 +5,7 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 metrics: - metric: "errors_total" labels: ["instance", "job"] diff --git a/asap-planner-rs/tests/comparison/test_data/configs/cleanup_circular.yaml b/asap-planner-rs/tests/comparison/test_data/configs/cleanup_circular.yaml index 6e53ddac..623d0765 100644 --- a/asap-planner-rs/tests/comparison/test_data/configs/cleanup_circular.yaml +++ b/asap-planner-rs/tests/comparison/test_data/configs/cleanup_circular.yaml @@ -5,6 +5,6 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 aggregate_cleanup: policy: "circular_buffer" diff --git a/asap-planner-rs/tests/comparison/test_data/configs/cleanup_read_based.yaml b/asap-planner-rs/tests/comparison/test_data/configs/cleanup_read_based.yaml index 96637d51..9fc4f613 100644 --- a/asap-planner-rs/tests/comparison/test_data/configs/cleanup_read_based.yaml +++ b/asap-planner-rs/tests/comparison/test_data/configs/cleanup_read_based.yaml @@ -5,6 +5,6 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 aggregate_cleanup: policy: "read_based" diff --git a/asap-planner-rs/tests/comparison/test_data/configs/deduplicated.yaml b/asap-planner-rs/tests/comparison/test_data/configs/deduplicated.yaml index 0e8017b0..5f8f9a76 100644 --- a/asap-planner-rs/tests/comparison/test_data/configs/deduplicated.yaml +++ b/asap-planner-rs/tests/comparison/test_data/configs/deduplicated.yaml @@ -7,13 +7,13 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 - id: 2 queries: - "increase(http_requests_total[5m])" repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 aggregate_cleanup: policy: "read_based" diff --git a/asap-planner-rs/tests/comparison/test_data/configs/increase.yaml b/asap-planner-rs/tests/comparison/test_data/configs/increase.yaml index 38d23e94..c63df302 100644 --- a/asap-planner-rs/tests/comparison/test_data/configs/increase.yaml +++ b/asap-planner-rs/tests/comparison/test_data/configs/increase.yaml @@ -5,6 +5,6 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 aggregate_cleanup: policy: "read_based" diff --git a/asap-planner-rs/tests/comparison/test_data/configs/mixed_workload.yaml b/asap-planner-rs/tests/comparison/test_data/configs/mixed_workload.yaml index 79d11262..e31dbfaa 100644 --- a/asap-planner-rs/tests/comparison/test_data/configs/mixed_workload.yaml +++ b/asap-planner-rs/tests/comparison/test_data/configs/mixed_workload.yaml @@ -6,6 +6,6 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 aggregate_cleanup: policy: "read_based" diff --git a/asap-planner-rs/tests/comparison/test_data/configs/quantile_over_time.yaml b/asap-planner-rs/tests/comparison/test_data/configs/quantile_over_time.yaml index a5f4c1b9..2e2231da 100644 --- a/asap-planner-rs/tests/comparison/test_data/configs/quantile_over_time.yaml +++ b/asap-planner-rs/tests/comparison/test_data/configs/quantile_over_time.yaml @@ -5,6 +5,6 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 aggregate_cleanup: policy: "read_based" diff --git a/asap-planner-rs/tests/comparison/test_data/configs/range_query.yaml b/asap-planner-rs/tests/comparison/test_data/configs/range_query.yaml index 96637d51..9fc4f613 100644 --- a/asap-planner-rs/tests/comparison/test_data/configs/range_query.yaml +++ b/asap-planner-rs/tests/comparison/test_data/configs/range_query.yaml @@ -5,6 +5,6 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 aggregate_cleanup: policy: "read_based" diff --git a/asap-planner-rs/tests/comparison/test_data/configs/rate_increase.yaml b/asap-planner-rs/tests/comparison/test_data/configs/rate_increase.yaml index 96637d51..9fc4f613 100644 --- a/asap-planner-rs/tests/comparison/test_data/configs/rate_increase.yaml +++ b/asap-planner-rs/tests/comparison/test_data/configs/rate_increase.yaml @@ -5,6 +5,6 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 aggregate_cleanup: policy: "read_based" diff --git a/asap-planner-rs/tests/comparison/test_data/configs/spatial_quantile.yaml b/asap-planner-rs/tests/comparison/test_data/configs/spatial_quantile.yaml index 4e2ba500..817adbb0 100644 --- a/asap-planner-rs/tests/comparison/test_data/configs/spatial_quantile.yaml +++ b/asap-planner-rs/tests/comparison/test_data/configs/spatial_quantile.yaml @@ -5,6 +5,6 @@ query_groups: repetition_delay_ms: 60000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 aggregate_cleanup: policy: "read_based" diff --git a/asap-planner-rs/tests/comparison/test_data/configs/sum_by.yaml b/asap-planner-rs/tests/comparison/test_data/configs/sum_by.yaml index ad8aaf50..a98e5b3c 100644 --- a/asap-planner-rs/tests/comparison/test_data/configs/sum_by.yaml +++ b/asap-planner-rs/tests/comparison/test_data/configs/sum_by.yaml @@ -5,6 +5,6 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 aggregate_cleanup: policy: "read_based" diff --git a/asap-planner-rs/tests/comparison/test_data/configs/sum_by_overlapping.yaml b/asap-planner-rs/tests/comparison/test_data/configs/sum_by_overlapping.yaml index 9a9b57bd..893b7845 100644 --- a/asap-planner-rs/tests/comparison/test_data/configs/sum_by_overlapping.yaml +++ b/asap-planner-rs/tests/comparison/test_data/configs/sum_by_overlapping.yaml @@ -5,6 +5,6 @@ query_groups: repetition_delay_ms: 60000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 aggregate_cleanup: policy: "read_based" diff --git a/asap-planner-rs/tests/comparison/test_data/configs/sum_over_time.yaml b/asap-planner-rs/tests/comparison/test_data/configs/sum_over_time.yaml index 8e0eee95..29484139 100644 --- a/asap-planner-rs/tests/comparison/test_data/configs/sum_over_time.yaml +++ b/asap-planner-rs/tests/comparison/test_data/configs/sum_over_time.yaml @@ -5,6 +5,6 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 aggregate_cleanup: policy: "read_based" diff --git a/asap-planner-rs/tests/comparison/test_data/configs/temporal_overlapping.yaml b/asap-planner-rs/tests/comparison/test_data/configs/temporal_overlapping.yaml index 23017f48..f3da78c5 100644 --- a/asap-planner-rs/tests/comparison/test_data/configs/temporal_overlapping.yaml +++ b/asap-planner-rs/tests/comparison/test_data/configs/temporal_overlapping.yaml @@ -8,6 +8,6 @@ query_groups: repetition_delay_ms: 60000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 aggregate_cleanup: policy: "read_based" diff --git a/asap-planner-rs/tests/comparison/test_data/configs/topk.yaml b/asap-planner-rs/tests/comparison/test_data/configs/topk.yaml index 7ab4b3cb..ec069ae2 100644 --- a/asap-planner-rs/tests/comparison/test_data/configs/topk.yaml +++ b/asap-planner-rs/tests/comparison/test_data/configs/topk.yaml @@ -5,6 +5,6 @@ query_groups: repetition_delay_ms: 60000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 aggregate_cleanup: policy: "read_based" diff --git a/asap-planner-rs/tests/elastic_dsl_integration.rs b/asap-planner-rs/tests/elastic_dsl_integration.rs index 4d143893..334e217b 100644 --- a/asap-planner-rs/tests/elastic_dsl_integration.rs +++ b/asap-planner-rs/tests/elastic_dsl_integration.rs @@ -28,7 +28,7 @@ query_groups: repetition_delay_ms: {t_repeat_ms} controller_options: accuracy_sla: 0.95 - latency_sla: 1.0 + latency_sla_ms: 1000 aggregate_cleanup: policy: read_based "#, @@ -390,7 +390,7 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.95 - latency_sla: 1.0 + latency_sla_ms: 1000 - id: 2 index: other_metrics time_field: "timestamp" @@ -429,7 +429,7 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.95 - latency_sla: 1.0 + latency_sla_ms: 1000 aggregate_cleanup: policy: read_based "#; diff --git a/asap-planner-rs/tests/elastic_example.yaml b/asap-planner-rs/tests/elastic_example.yaml index 66014c45..15bc9993 100644 --- a/asap-planner-rs/tests/elastic_example.yaml +++ b/asap-planner-rs/tests/elastic_example.yaml @@ -30,6 +30,6 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.95 - latency_sla: 1.0 + latency_sla_ms: 1000 aggregate_cleanup: policy: read_based diff --git a/asap-planner-rs/tests/integration.rs b/asap-planner-rs/tests/integration.rs index 1c8e3aea..f193a1db 100644 --- a/asap-planner-rs/tests/integration.rs +++ b/asap-planner-rs/tests/integration.rs @@ -125,14 +125,14 @@ query_groups: repetition_delay_ms: 65000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 - id: 2 queries: - "rate(http_requests_total[55s])" repetition_delay_ms: 55000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 "#, http_requests_schema(), default_opts(), @@ -165,7 +165,7 @@ query_groups: repetition_delay_ms: 60000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 "#, http_requests_schema(), default_opts(), @@ -241,7 +241,7 @@ query_groups: repetition_delay_ms: 60000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 "#, http_requests_schema(), default_opts(), @@ -278,7 +278,7 @@ query_groups: step_ms: 80000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 "#, http_requests_schema(), default_opts(), @@ -306,7 +306,7 @@ query_groups: repetition_delay_ms: 60000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 "#, http_requests_schema(), default_opts(), @@ -680,7 +680,7 @@ query_groups: repetition_delay_ms: 60000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 "#, http_requests_schema(), default_opts(), @@ -731,7 +731,7 @@ query_groups: repetition_delay_ms: 60000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 "#, http_requests_schema(), default_opts(), @@ -905,7 +905,7 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 aggregate_cleanup: policy: "not_a_real_policy" "#; @@ -928,7 +928,7 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 "#; let c = Controller::from_yaml_with_schema(yaml, http_requests_schema(), default_opts()).unwrap(); @@ -948,14 +948,14 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 - id: 2 queries: - "rate(http_requests_total[5m])" repetition_delay_ms: 60000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 "#; let c = Controller::from_yaml_with_schema(yaml, http_requests_schema(), default_opts()).unwrap(); @@ -976,7 +976,7 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 "#; // Schema only knows about http_requests_total, not unknown_metric. let c = @@ -1284,14 +1284,14 @@ query_groups: repetition_delay_ms: 65000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 - id: 2 queries: - "rate(errors_total[55s]) / rate(requests_total[55s])" repetition_delay_ms: 55000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 "#, binary_arithmetic_schema(), default_opts(), @@ -1326,7 +1326,7 @@ query_groups: repetition_delay_ms: 60000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 "#, binary_arithmetic_schema(), default_opts(), diff --git a/asap-planner-rs/tests/sql_integration.rs b/asap-planner-rs/tests/sql_integration.rs index 0e9aa364..ee77222b 100644 --- a/asap-planner-rs/tests/sql_integration.rs +++ b/asap-planner-rs/tests/sql_integration.rs @@ -85,7 +85,7 @@ query_groups: repetition_delay_ms: 60000 controller_options: accuracy_sla: 0.95 - latency_sla: 100.0 + latency_sla_ms: 1000 queries: - "{query}" aggregate_cleanup: @@ -123,7 +123,7 @@ query_groups: repetition_delay_ms: 60000 controller_options: accuracy_sla: 0.95 - latency_sla: 100.0 + latency_sla_ms: 1000 queries: - "{query}" aggregate_cleanup: @@ -192,7 +192,7 @@ query_groups: repetition_delay_ms: {t_repeat_ms} controller_options: accuracy_sla: 0.95 - latency_sla: 100.0 + latency_sla_ms: 1000 queries: - >- {query} @@ -642,7 +642,7 @@ query_groups: repetition_delay_ms: {t_repeat_ms} controller_options: accuracy_sla: 0.95 - latency_sla: 100.0 + latency_sla_ms: 1000 queries: - >- {query} @@ -771,7 +771,7 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.95 - latency_sla: 100.0 + latency_sla_ms: 1000 queries: - >- SELECT MIN(cpu_usage) FROM metrics_table WHERE time BETWEEN DATEADD(s, -300, NOW()) AND NOW() GROUP BY datacenter @@ -779,7 +779,7 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.95 - latency_sla: 100.0 + latency_sla_ms: 1000 queries: - >- SELECT MAX(cpu_usage) FROM metrics_table WHERE time BETWEEN DATEADD(s, -300, NOW()) AND NOW() GROUP BY datacenter @@ -812,7 +812,7 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.95 - latency_sla: 100.0 + latency_sla_ms: 1000 queries: - >- SELECT SUM(cpu_usage) FROM metrics_table WHERE time BETWEEN DATEADD(s, -300, NOW()) AND NOW() GROUP BY datacenter @@ -846,7 +846,7 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.95 - latency_sla: 100.0 + latency_sla_ms: 1000 queries: - >- {q} @@ -854,7 +854,7 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.95 - latency_sla: 100.0 + latency_sla_ms: 1000 queries: - >- {q} @@ -883,7 +883,7 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.95 - latency_sla: 100.0 + latency_sla_ms: 1000 queries: - >- SELECT SUM(cpu_usage) FROM metrics_table WHERE time BETWEEN DATEADD(s, -300, NOW()) AND NOW() GROUP BY datacenter @@ -891,7 +891,7 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.95 - latency_sla: 100.0 + latency_sla_ms: 1000 queries: - >- SELECT SUM(cpu_usage) FROM metrics_table WHERE time BETWEEN DATEADD(s, -600, NOW()) AND NOW() GROUP BY datacenter @@ -975,7 +975,7 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.95 - latency_sla: 100.0 + latency_sla_ms: 1000 queries: - >- {q} @@ -1006,7 +1006,7 @@ query_groups: repetition_delay_ms: 300000 controller_options: accuracy_sla: 0.95 - latency_sla: 100.0 + latency_sla_ms: 1000 queries: - >- {q} diff --git a/asap-planner-rs/tests/test_data/windowing/promql_sliding.yaml b/asap-planner-rs/tests/test_data/windowing/promql_sliding.yaml index ad1093cf..51e8e705 100644 --- a/asap-planner-rs/tests/test_data/windowing/promql_sliding.yaml +++ b/asap-planner-rs/tests/test_data/windowing/promql_sliding.yaml @@ -9,13 +9,13 @@ query_groups: repetition_delay_ms: 60000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 - id: 2 queries: - "rate(http_requests_total[2m])" repetition_delay_ms: 120000 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 aggregate_cleanup: policy: "read_based" diff --git a/asap-planner-rs/tests/test_data/windowing/sql_sliding.yaml b/asap-planner-rs/tests/test_data/windowing/sql_sliding.yaml index acf8a142..1187b428 100644 --- a/asap-planner-rs/tests/test_data/windowing/sql_sliding.yaml +++ b/asap-planner-rs/tests/test_data/windowing/sql_sliding.yaml @@ -12,7 +12,7 @@ query_groups: repetition_delay_ms: 60000 controller_options: accuracy_sla: 0.95 - latency_sla: 100.0 + latency_sla_ms: 1000 queries: - >- SELECT MIN(cpu_usage) FROM metrics_table @@ -22,7 +22,7 @@ query_groups: repetition_delay_ms: 120000 controller_options: accuracy_sla: 0.95 - latency_sla: 100.0 + latency_sla_ms: 1000 queries: - >- SELECT MIN(cpu_usage) FROM metrics_table diff --git a/asap-planner-rs/tests/test_data/windowing/sql_sliding_invalid.yaml b/asap-planner-rs/tests/test_data/windowing/sql_sliding_invalid.yaml index 91af6af8..42ada292 100644 --- a/asap-planner-rs/tests/test_data/windowing/sql_sliding_invalid.yaml +++ b/asap-planner-rs/tests/test_data/windowing/sql_sliding_invalid.yaml @@ -12,7 +12,7 @@ query_groups: repetition_delay_ms: 75000 controller_options: accuracy_sla: 0.95 - latency_sla: 100.0 + latency_sla_ms: 1000 queries: - >- SELECT MIN(cpu_usage) FROM metrics_table @@ -22,7 +22,7 @@ query_groups: repetition_delay_ms: 90000 controller_options: accuracy_sla: 0.95 - latency_sla: 100.0 + latency_sla_ms: 1000 queries: - >- SELECT MIN(cpu_usage) FROM metrics_table diff --git a/asap-query-engine/tests/e2e_precompute_equivalence.rs b/asap-query-engine/tests/e2e_precompute_equivalence.rs index 5d6592a5..47127774 100644 --- a/asap-query-engine/tests/e2e_precompute_equivalence.rs +++ b/asap-query-engine/tests/e2e_precompute_equivalence.rs @@ -175,7 +175,7 @@ query_groups: repetition_delay_ms: {interval_ms} controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 "# ); let planner = Controller::from_yaml_with_schema( diff --git a/asap-quickstart/config/controller-config.yaml b/asap-quickstart/config/controller-config.yaml index cacd044f..49235fcd 100644 --- a/asap-quickstart/config/controller-config.yaml +++ b/asap-quickstart/config/controller-config.yaml @@ -12,7 +12,7 @@ query_groups: repetition_delay: 10 controller_options: accuracy_sla: 0.99 - latency_sla: 1 + latency_sla_ms: 1000 # Metric metadata - includes the 'pattern' label metrics: diff --git a/asap-tools/docs/architecture.md b/asap-tools/docs/architecture.md index bada0fe1..e79ecc06 100644 --- a/asap-tools/docs/architecture.md +++ b/asap-tools/docs/architecture.md @@ -763,7 +763,7 @@ query_groups: query_time_offset: 0 controller_options: accuracy_sla: 0.99 - latency_sla: 1.0 + latency_sla_ms: 1000 servers: - name: prometheus diff --git a/asap-tools/docs/configuration.md b/asap-tools/docs/configuration.md index f9f07fb3..ef1163d9 100644 --- a/asap-tools/docs/configuration.md +++ b/asap-tools/docs/configuration.md @@ -757,7 +757,7 @@ The framework validates configurations at runtime using `experiment_utils/config - `client_options.repetitions`: Number of repetitions - `client_options.starting_delay`: Warmup period - `controller_options.accuracy_sla`: Accuracy threshold - - `controller_options.latency_sla`: Latency threshold + - `controller_options.latency_sla_ms` (optional): Maximum modeled query latency in milliseconds; omit for no limit. Not yet enforced by the planner 2. **exporters** - Must have: - `exporter_list`: Dictionary of exporter configs diff --git a/asap-tools/experiments/CONFIG_PARAMETERS_REFERENCE.md b/asap-tools/experiments/CONFIG_PARAMETERS_REFERENCE.md index 46fa9a93..35a8afed 100644 --- a/asap-tools/experiments/CONFIG_PARAMETERS_REFERENCE.md +++ b/asap-tools/experiments/CONFIG_PARAMETERS_REFERENCE.md @@ -513,12 +513,12 @@ These parameters come from the `experiment_type` config group and are prefixed w - **Example**: `0.95` - **Usage**: Quality assurance for approximate query results -#### `experiment_params.query_groups[].controller_options.latency_sla` (float, optional) -- **Description**: Latency SLA in seconds -- **Default**: `1` -- **Range**: 0.1-60.0 seconds -- **Example**: `2` -- **Usage**: Performance requirement for query response time +#### `experiment_params.query_groups[].controller_options.latency_sla_ms` (float, optional) +- **Description**: Maximum modeled query latency in milliseconds +- **Default**: omitted (no latency limit) +- **Range**: finite and > 0 +- **Example**: `250` +- **Usage**: Intended as a hard ceiling on query latency for the planner. Not yet enforced: plans are not rejected for exceeding it. ### Metrics Configuration diff --git a/asap-tools/experiments/config/experiment_type/cluster_data_alibaba_msresource_2021.yaml b/asap-tools/experiments/config/experiment_type/cluster_data_alibaba_msresource_2021.yaml index ea38e1dd..2e09414c 100644 --- a/asap-tools/experiments/config/experiment_type/cluster_data_alibaba_msresource_2021.yaml +++ b/asap-tools/experiments/config/experiment_type/cluster_data_alibaba_msresource_2021.yaml @@ -49,7 +49,7 @@ query_groups: starting_delay: 70 controller_options: accuracy_sla: 0.99 - latency_sla: 1 + latency_sla_ms: 1000 metrics: - metric: "alibaba_microservice_cpu_usage" diff --git a/asap-tools/experiments/config/experiment_type/cluster_data_alibaba_msresource_2022.yaml b/asap-tools/experiments/config/experiment_type/cluster_data_alibaba_msresource_2022.yaml index 2a1be88a..46a88a3e 100644 --- a/asap-tools/experiments/config/experiment_type/cluster_data_alibaba_msresource_2022.yaml +++ b/asap-tools/experiments/config/experiment_type/cluster_data_alibaba_msresource_2022.yaml @@ -49,7 +49,7 @@ query_groups: starting_delay: 70 controller_options: accuracy_sla: 0.99 - latency_sla: 1 + latency_sla_ms: 1000 metrics: - metric: "alibaba_microservice_cpu_usage" diff --git a/asap-tools/experiments/config/experiment_type/cluster_data_alibaba_node_2021.yaml b/asap-tools/experiments/config/experiment_type/cluster_data_alibaba_node_2021.yaml index e4311124..4d21eabb 100644 --- a/asap-tools/experiments/config/experiment_type/cluster_data_alibaba_node_2021.yaml +++ b/asap-tools/experiments/config/experiment_type/cluster_data_alibaba_node_2021.yaml @@ -60,7 +60,7 @@ query_groups: starting_delay: 70 controller_options: accuracy_sla: 0.99 - latency_sla: 1 + latency_sla_ms: 1000 metrics: - metric: "alibaba_node_cpu_usage" diff --git a/asap-tools/experiments/config/experiment_type/cluster_data_alibaba_node_2022.yaml b/asap-tools/experiments/config/experiment_type/cluster_data_alibaba_node_2022.yaml index c6f644bf..ea1dc644 100644 --- a/asap-tools/experiments/config/experiment_type/cluster_data_alibaba_node_2022.yaml +++ b/asap-tools/experiments/config/experiment_type/cluster_data_alibaba_node_2022.yaml @@ -52,7 +52,7 @@ query_groups: starting_delay: 70 controller_options: accuracy_sla: 0.99 - latency_sla: 1 + latency_sla_ms: 1000 metrics: - metric: "alibaba_node_cpu_usage" diff --git a/asap-tools/experiments/config/experiment_type/cluster_data_google.yaml b/asap-tools/experiments/config/experiment_type/cluster_data_google.yaml index 70436d2d..6c3e209f 100644 --- a/asap-tools/experiments/config/experiment_type/cluster_data_google.yaml +++ b/asap-tools/experiments/config/experiment_type/cluster_data_google.yaml @@ -66,7 +66,7 @@ query_groups: starting_delay: 70 controller_options: accuracy_sla: 0.99 - latency_sla: 1 + latency_sla_ms: 1000 metrics: # Google metrics with aggregation_type=0 diff --git a/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/alibaba_v2022_ms_cpu_by_msname_5m_default.yaml b/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/alibaba_v2022_ms_cpu_by_msname_5m_default.yaml index bd721fc5..3adde0c4 100644 --- a/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/alibaba_v2022_ms_cpu_by_msname_5m_default.yaml +++ b/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/alibaba_v2022_ms_cpu_by_msname_5m_default.yaml @@ -35,7 +35,7 @@ experiment_params: starting_delay: 390 controller_options: accuracy_sla: 0.99 - latency_sla: 1 + latency_sla_ms: 1000 metrics: - metric: alibaba_microservice_cpu_usage labels: diff --git a/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/alibaba_v2022_ms_cpu_by_msname_5m_recommended.yaml b/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/alibaba_v2022_ms_cpu_by_msname_5m_recommended.yaml index 593b834c..d372d21e 100644 --- a/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/alibaba_v2022_ms_cpu_by_msname_5m_recommended.yaml +++ b/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/alibaba_v2022_ms_cpu_by_msname_5m_recommended.yaml @@ -35,7 +35,7 @@ experiment_params: starting_delay: 390 controller_options: accuracy_sla: 0.99 - latency_sla: 1 + latency_sla_ms: 1000 metrics: - metric: alibaba_microservice_cpu_usage labels: diff --git a/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/alibaba_v2022_ms_cpu_by_msname_instant_default.yaml b/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/alibaba_v2022_ms_cpu_by_msname_instant_default.yaml index 6f54463e..3fbb04ed 100644 --- a/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/alibaba_v2022_ms_cpu_by_msname_instant_default.yaml +++ b/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/alibaba_v2022_ms_cpu_by_msname_instant_default.yaml @@ -35,7 +35,7 @@ experiment_params: starting_delay: 90 controller_options: accuracy_sla: 0.99 - latency_sla: 1 + latency_sla_ms: 1000 metrics: - metric: alibaba_microservice_cpu_usage labels: diff --git a/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/alibaba_v2022_ms_cpu_by_msname_instant_recommended.yaml b/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/alibaba_v2022_ms_cpu_by_msname_instant_recommended.yaml index dac39e20..72d6d54e 100644 --- a/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/alibaba_v2022_ms_cpu_by_msname_instant_recommended.yaml +++ b/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/alibaba_v2022_ms_cpu_by_msname_instant_recommended.yaml @@ -35,7 +35,7 @@ experiment_params: starting_delay: 90 controller_options: accuracy_sla: 0.99 - latency_sla: 1 + latency_sla_ms: 1000 metrics: - metric: alibaba_microservice_cpu_usage labels: diff --git a/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/google_2011_cpu_by_job_id_5m_default.yaml b/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/google_2011_cpu_by_job_id_5m_default.yaml index 9a163036..78ed3c79 100644 --- a/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/google_2011_cpu_by_job_id_5m_default.yaml +++ b/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/google_2011_cpu_by_job_id_5m_default.yaml @@ -34,7 +34,7 @@ experiment_params: starting_delay: 390 controller_options: accuracy_sla: 0.99 - latency_sla: 1 + latency_sla_ms: 1000 metrics: - metric: google_mean_cpu_usage_rate_0 labels: diff --git a/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/google_2011_cpu_by_job_id_5m_recommended.yaml b/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/google_2011_cpu_by_job_id_5m_recommended.yaml index 0f815477..ea34015f 100644 --- a/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/google_2011_cpu_by_job_id_5m_recommended.yaml +++ b/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/google_2011_cpu_by_job_id_5m_recommended.yaml @@ -34,7 +34,7 @@ experiment_params: starting_delay: 390 controller_options: accuracy_sla: 0.99 - latency_sla: 1 + latency_sla_ms: 1000 metrics: - metric: google_mean_cpu_usage_rate_0 labels: diff --git a/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/google_2011_cpu_by_job_id_instant_default.yaml b/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/google_2011_cpu_by_job_id_instant_default.yaml index d68ab47c..1195e9f5 100644 --- a/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/google_2011_cpu_by_job_id_instant_default.yaml +++ b/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/google_2011_cpu_by_job_id_instant_default.yaml @@ -34,7 +34,7 @@ experiment_params: starting_delay: 90 controller_options: accuracy_sla: 0.99 - latency_sla: 1 + latency_sla_ms: 1000 metrics: - metric: google_mean_cpu_usage_rate_0 labels: diff --git a/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/google_2011_cpu_by_job_id_instant_recommended.yaml b/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/google_2011_cpu_by_job_id_instant_recommended.yaml index c6fd091c..6fc64b5e 100644 --- a/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/google_2011_cpu_by_job_id_instant_recommended.yaml +++ b/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/google_2011_cpu_by_job_id_instant_recommended.yaml @@ -34,7 +34,7 @@ experiment_params: starting_delay: 90 controller_options: accuracy_sla: 0.99 - latency_sla: 1 + latency_sla_ms: 1000 metrics: - metric: google_mean_cpu_usage_rate_0 labels: diff --git a/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/google_2011_cpu_p99_instant_default.yaml b/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/google_2011_cpu_p99_instant_default.yaml index 3be876db..b9711d75 100644 --- a/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/google_2011_cpu_p99_instant_default.yaml +++ b/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/google_2011_cpu_p99_instant_default.yaml @@ -34,7 +34,7 @@ experiment_params: starting_delay: 90 controller_options: accuracy_sla: 0.99 - latency_sla: 1 + latency_sla_ms: 1000 metrics: - metric: google_mean_cpu_usage_rate_0 labels: diff --git a/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/google_2011_cpu_p99_instant_recommended.yaml b/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/google_2011_cpu_p99_instant_recommended.yaml index e26d8cd7..c4aef968 100644 --- a/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/google_2011_cpu_p99_instant_recommended.yaml +++ b/asap-tools/experiments/config/experiment_type/recommended_sketch_configs/google_2011_cpu_p99_instant_recommended.yaml @@ -34,7 +34,7 @@ experiment_params: starting_delay: 90 controller_options: accuracy_sla: 0.99 - latency_sla: 1 + latency_sla_ms: 1000 metrics: - metric: google_mean_cpu_usage_rate_0 labels: diff --git a/asap-tools/experiments/experiment_utils/config.py b/asap-tools/experiments/experiment_utils/config.py index 6828bd6c..a731c005 100644 --- a/asap-tools/experiments/experiment_utils/config.py +++ b/asap-tools/experiments/experiment_utils/config.py @@ -3,6 +3,7 @@ Contains functions for validating configs, generating controller configs, etc. """ +import math import os import copy import yaml @@ -13,6 +14,9 @@ import constants from experiment_utils.providers.factory import create_provider +# Keys the planner's `ControllerOptions` accepts. +CONTROLLER_OPTION_KEYS = {"accuracy_sla", "latency_sla_ms"} + def validate_basic_config( cfg: DictConfig, @@ -412,6 +416,13 @@ def generate_controller_client_configs( controller_only_config = { k: v for k, v in full_config.items() if k in CONTROLLER_ALLOWED_KEYS } + # Only the query client reads `client_options`; any other unknown group + # key reaches the planner and is rejected there. + if "query_groups" in controller_only_config: + controller_only_config["query_groups"] = [ + {k: v for k, v in group.items() if k != "client_options"} + for group in controller_only_config["query_groups"] + ] with open( os.path.join( output_dir, "{}_controller_input.yaml".format(experiment_mode["mode"]) @@ -829,6 +840,22 @@ def generate_clickhouse_client_configs( return modes +def _positive_latency_sla_ms(value: Any, idx: int) -> float: + """Fail at generation time rather than on the remote planner.""" + # bool is an int subclass; `true` would otherwise become 1.0. + if isinstance(value, bool) or not isinstance(value, (int, float)): + raise ValueError( + f"query_groups[{idx}].controller_options.latency_sla_ms must be a " + f"number, got {value!r}" + ) + if not (math.isfinite(value) and value > 0): + raise ValueError( + f"query_groups[{idx}].controller_options.latency_sla_ms must be " + f"finite and > 0, got {value!r}" + ) + return float(value) + + def generate_sql_planner_input( query_groups: Any, dataset_cfg: Any, @@ -853,7 +880,8 @@ def generate_sql_planner_input( Args: query_groups: ListConfig of query group dicts. Each entry must have ``sql_file``, ``repetition_delay_ms``, and - ``controller_options`` (``accuracy_sla``, ``latency_sla``). + ``controller_options`` (``accuracy_sla``, optional + ``latency_sla_ms``; omitted or null means no latency limit). dataset_cfg: DictConfig with ``table``/``name``, and ``precompute`` sub-config (``timestamp_col``, ``value_col``, ``label_cols``). sketch_parameters: Optional DictConfig/dict mirroring ``config.yaml``'s @@ -886,6 +914,15 @@ def generate_sql_planner_input( planner_query_groups = [] for idx, group in enumerate(groups_list): + ctrl_opts = dict(group.get("controller_options") or {}) + # Dropping an unknown key here would hide it from the planner's strict parse. + unknown = set(ctrl_opts) - CONTROLLER_OPTION_KEYS + if unknown: + raise ValueError( + f"query_groups[{idx}].controller_options has unknown keys " + f"{sorted(unknown)}; allowed: {sorted(CONTROLLER_OPTION_KEYS)}" + ) + sql_file = group.get("sql_file") if not sql_file: raise ValueError(f"query_groups[{idx}] missing 'sql_file'") @@ -900,16 +937,21 @@ def generate_sql_planner_input( if not queries: raise ValueError(f"No SQL statements found in {sql_file!r}") - ctrl_opts = dict(group.get("controller_options") or {}) + planner_ctrl_opts = { + "accuracy_sla": float(ctrl_opts.get("accuracy_sla", 0.95)), + } + # Omitted or null means no latency limit, as in the planner. + latency_sla_ms = ctrl_opts.get("latency_sla_ms") + if latency_sla_ms is not None: + planner_ctrl_opts["latency_sla_ms"] = _positive_latency_sla_ms( + latency_sla_ms, idx + ) planner_query_groups.append( { "id": idx + 1, "repetition_delay_ms": int(group.get("repetition_delay_ms", 0)), "queries": queries, - "controller_options": { - "accuracy_sla": float(ctrl_opts.get("accuracy_sla", 0.95)), - "latency_sla": float(ctrl_opts.get("latency_sla", 100.0)), - }, + "controller_options": planner_ctrl_opts, } ) diff --git a/asap-tools/experiments/generate_workload.py b/asap-tools/experiments/generate_workload.py index ac79d02e..26744064 100644 --- a/asap-tools/experiments/generate_workload.py +++ b/asap-tools/experiments/generate_workload.py @@ -433,7 +433,7 @@ def get_base_config() -> Dict: "query_time_offset": 10, "starting_delay": 60, }, - "controller_options": {"accuracy_sla": 0.99, "latency_sla": 1}, + "controller_options": {"accuracy_sla": 0.99, "latency_sla_ms": 1000}, } ], "metrics": [ diff --git a/asap-tools/experiments/recommended_sketch_configs/recommended_sketch_configs.py b/asap-tools/experiments/recommended_sketch_configs/recommended_sketch_configs.py index a0b104e6..937a997e 100644 --- a/asap-tools/experiments/recommended_sketch_configs/recommended_sketch_configs.py +++ b/asap-tools/experiments/recommended_sketch_configs/recommended_sketch_configs.py @@ -238,7 +238,7 @@ def build_experiment_config( "query_time_offset": 10, "starting_delay": starting_delay, }, - "controller_options": {"accuracy_sla": 0.99, "latency_sla": 1}, + "controller_options": {"accuracy_sla": 0.99, "latency_sla_ms": 1000}, } ], "metrics": [ diff --git a/asap-tools/experiments/tests/test_controller_client_configs.py b/asap-tools/experiments/tests/test_controller_client_configs.py new file mode 100644 index 00000000..96a92fc1 --- /dev/null +++ b/asap-tools/experiments/tests/test_controller_client_configs.py @@ -0,0 +1,49 @@ +"""Tests for the planner input written from experiment parameters.""" + +import os +import tempfile +import unittest + +import yaml +from omegaconf import OmegaConf + +from experiment_utils.config import generate_controller_client_configs + + +def _controller_input(experiment_params): + with tempfile.TemporaryDirectory() as tmp: + generate_controller_client_configs(OmegaConf.create(experiment_params), tmp) + path = os.path.join( + tmp, "controller_client_configs", "sketchdb_controller_input.yaml" + ) + with open(path) as f: + return yaml.safe_load(f) + + +BASE = { + "servers": [{"name": "sketchdb", "url": "http://localhost:8088"}], + "experiment": [{"mode": "sketchdb", "server": "sketchdb"}], + "metrics": [{"metric": "m", "labels": ["job"], "exporter": "fake"}], +} + + +class ControllerInputTest(unittest.TestCase): + def test_client_options_are_stripped_from_query_groups(self): + group = { + "id": 1, + "queries": ["sum(m)"], + "repetition_delay_ms": 60000, + "client_options": {"repetitions": 1}, + "controller_options": {"accuracy_sla": 0.99}, + } + planner_input = _controller_input({**BASE, "query_groups": [group]}) + self.assertNotIn("client_options", planner_input["query_groups"][0]) + self.assertIn("controller_options", planner_input["query_groups"][0]) + + def test_missing_query_groups_stays_missing(self): + # An empty list would parse and plan nothing; a missing key fails loudly. + self.assertNotIn("query_groups", _controller_input(BASE)) + + +if __name__ == "__main__": + unittest.main() diff --git a/asap-tools/experiments/tests/test_sql_planner_input.py b/asap-tools/experiments/tests/test_sql_planner_input.py new file mode 100644 index 00000000..007c67b9 --- /dev/null +++ b/asap-tools/experiments/tests/test_sql_planner_input.py @@ -0,0 +1,92 @@ +"""Tests for generating the SQL planner input.""" + +import os +import tempfile +import unittest + +import yaml +from omegaconf import OmegaConf + +from experiment_utils.config import generate_sql_planner_input + + +class ControllerOptionKeysTest(unittest.TestCase): + def test_unknown_controller_option_is_rejected(self): + # Unknown keys must fail here: dropping them would hide them from the + # planner's strict parse. + groups = [ + { + "sql_file": "unused.sql", + "repetition_delay_ms": 60000, + "controller_options": {"accuracy_sla": 0.99, "latency_sla": 1}, + } + ] + dataset_cfg = OmegaConf.create( + { + "name": "t", + "precompute": { + "timestamp_col": "ts", + "value_col": "v", + "label_cols": ["job"], + }, + } + ) + with self.assertRaisesRegex(ValueError, "latency_sla"): + generate_sql_planner_input(groups, dataset_cfg) + + +class LatencySlaMsTest(unittest.TestCase): + def _planner_latency(self, controller_options): + with tempfile.TemporaryDirectory() as tmp: + sql_path = os.path.join(tmp, "q.sql") + with open(sql_path, "w") as f: + f.write("SELECT 1;") + groups = [ + { + "sql_file": {"baseline": sql_path}, + "repetition_delay_ms": 60000, + "controller_options": controller_options, + } + ] + dataset_cfg = OmegaConf.create( + { + "name": "t", + "precompute": { + "timestamp_col": "ts", + "value_col": "v", + "label_cols": ["job"], + }, + } + ) + planner_input = yaml.safe_load( + generate_sql_planner_input(groups, dataset_cfg) + ) + return planner_input["query_groups"][0]["controller_options"].get( + "latency_sla_ms" + ) + + def test_omitted_latency_sla_ms_means_no_limit(self): + # Same meaning as the PromQL path and the planner. + self.assertIsNone(self._planner_latency({"accuracy_sla": 0.99})) + + def test_invalid_latency_sla_ms_is_rejected(self): + for bad in [0, -5, float("inf"), float("nan"), True, "1000"]: + with self.subTest(bad=bad), self.assertRaisesRegex( + ValueError, "latency_sla_ms" + ): + self._planner_latency({"accuracy_sla": 0.99, "latency_sla_ms": bad}) + + def test_explicit_latency_sla_ms_is_kept(self): + self.assertEqual( + self._planner_latency({"accuracy_sla": 0.99, "latency_sla_ms": 250}), + 250.0, + ) + + def test_null_latency_sla_ms_means_no_limit(self): + self.assertIsNone( + self._planner_latency({"accuracy_sla": 0.99, "latency_sla_ms": None}) + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/benchmarks/config/controller-config.yaml b/benchmarks/config/controller-config.yaml index 444be132..b90204d6 100644 --- a/benchmarks/config/controller-config.yaml +++ b/benchmarks/config/controller-config.yaml @@ -12,7 +12,7 @@ query_groups: repetition_delay_ms: 10000 controller_options: accuracy_sla: 0.99 - latency_sla: 1 + latency_sla_ms: 1000 # Metric metadata - includes the 'pattern' label metrics: diff --git a/docs/03-how-to-guides/operations/try-asap-planner-promql.md b/docs/03-how-to-guides/operations/try-asap-planner-promql.md index fec12012..a09861fd 100644 --- a/docs/03-how-to-guides/operations/try-asap-planner-promql.md +++ b/docs/03-how-to-guides/operations/try-asap-planner-promql.md @@ -23,7 +23,7 @@ query_groups: repetition_delay_ms: 300000 # ms between repeats of this query controller_options: accuracy_sla: 0.95 - latency_sla: 100.0 + latency_sla_ms: 1000 queries: - "sum by (job) (rate(http_requests_total[5m]))" aggregate_cleanup: @@ -43,8 +43,11 @@ auto-infer label sets per metric. ### `controller_options` -`accuracy_sla` and `latency_sla` are required by the config schema but not currently used by -the planner's decision logic — any numeric values are fine (e.g. the placeholders above). +`accuracy_sla` is required by the config schema but not currently used by the planner's +decision logic — any numeric value is fine (e.g. the placeholder above). + +`latency_sla_ms` is optional: the maximum modeled query latency in milliseconds (must be +finite and > 0). Omit it for no latency limit. The planner does not enforce it yet. ### Choosing `repetition_delay_ms` and `--data-ingestion-interval-ms` diff --git a/docs/03-how-to-guides/operations/try-asap-planner-sql.md b/docs/03-how-to-guides/operations/try-asap-planner-sql.md index cdb39838..3052f033 100644 --- a/docs/03-how-to-guides/operations/try-asap-planner-sql.md +++ b/docs/03-how-to-guides/operations/try-asap-planner-sql.md @@ -25,7 +25,7 @@ query_groups: repetition_delay: 300 # seconds between repeats of this query controller_options: accuracy_sla: 0.95 - latency_sla: 100.0 + latency_sla_ms: 1000 queries: - >- SELECT avg(cpu_usage) FROM metrics_table @@ -46,9 +46,11 @@ For each table: ### `controller_options` -`accuracy_sla` and `latency_sla` are required by the config schema but not currently used by -the planner's decision logic — any numeric values are fine (e.g. the placeholders in the -example above). +`accuracy_sla` is required by the config schema but not currently used by the planner's +decision logic — any numeric value is fine (e.g. the placeholder in the example above). + +`latency_sla_ms` is optional: the maximum modeled query latency in milliseconds (must be +finite and > 0). Omit it for no latency limit. The planner does not enforce it yet. ### Choosing `repetition_delay` and `--data-ingestion-interval` diff --git a/promql-compliance/runner/run.go b/promql-compliance/runner/run.go index d4aebfbc..c229a4eb 100644 --- a/promql-compliance/runner/run.go +++ b/promql-compliance/runner/run.go @@ -332,8 +332,8 @@ type plannerQueryGroup struct { } type plannerController struct { - AccuracySLA float64 `yaml:"accuracy_sla"` - LatencySLA float64 `yaml:"latency_sla"` + AccuracySLA float64 `yaml:"accuracy_sla"` + LatencySLAMs float64 `yaml:"latency_sla_ms"` } type plannerMetric struct { @@ -377,7 +377,7 @@ func writeGeneratedConfigs(directory string, fixture seeder.Fixture, suite Suite } group := plannerQueryGroup{ ID: index + 1, Queries: []string{query.Expr}, RepetitionDelayMS: repetitionDelayMS, - ControllerOptions: plannerController{AccuracySLA: 0.99, LatencySLA: 1}, + ControllerOptions: plannerController{AccuracySLA: 0.99, LatencySLAMs: 1000}, } if query.Range != nil { stepMS := int(query.Range.StepSeconds * float64(time.Second/time.Millisecond)) diff --git a/tools/run_metrics_observability_benchmark.py b/tools/run_metrics_observability_benchmark.py index 4cbcb2be..8dbb0cef 100644 --- a/tools/run_metrics_observability_benchmark.py +++ b/tools/run_metrics_observability_benchmark.py @@ -165,7 +165,7 @@ def make_workload(queries, path, scrape_ms, repeat_ms): { "id": 1, "repetition_delay_ms": repeat_ms, - "controller_options": {"accuracy_sla": 0.95, "latency_sla": 100.0}, + "controller_options": {"accuracy_sla": 0.95, "latency_sla_ms": 1000}, "queries": [ entry["query"] for entry in queries