Skip to content

Commit 1ef309b

Browse files
implemented plan
1 parent 0e2d616 commit 1ef309b

20 files changed

Lines changed: 764 additions & 118 deletions

File tree

Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

asap-planner-rs/src/config/input.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
use asap_types::enums::CleanupPolicy;
2+
use asap_types::inference_config::InferenceConfig;
3+
use asap_types::streaming_config::StreamingConfig;
24
use asap_types::PromQLSchema;
35
use promql_utilities::data_model::KeyByLabelNames;
46
use serde::Deserialize;
@@ -13,6 +15,18 @@ pub struct ControllerConfig {
1315
/// returns no series for a metric. Prometheus-inferred labels take priority.
1416
#[serde(default)]
1517
pub metrics: Option<Vec<MetricDefinition>>,
18+
/// Current streaming config, passed as context for repeated reconfiguration.
19+
/// NOTE: reserved for future use — the planner does not yet act on these fields.
20+
/// They are wired through now so that repeated-reconfig support can be added
21+
/// without a second round of type-signature changes.
22+
#[serde(default)]
23+
pub existing_streaming_config: Option<StreamingConfig>,
24+
/// Current inference config, passed as context for repeated reconfiguration.
25+
/// NOTE: see existing_streaming_config — same future-use caveat applies.
26+
/// Not serializable via serde (InferenceConfig does not impl Deserialize);
27+
/// set programmatically only.
28+
#[serde(skip)]
29+
pub existing_inference_config: Option<InferenceConfig>,
1630
}
1731

1832
impl ControllerConfig {

asap-planner-rs/src/query_log/converter.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,7 @@ pub fn to_controller_config(
4242
policy: Some(CleanupPolicy::ReadBased),
4343
}),
4444
metrics: None,
45+
existing_streaming_config: None,
46+
existing_inference_config: None,
4547
}
4648
}

asap-query-engine/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ lazy_static = "1.4"
5757
zstd = "0.13"
5858
reqwest = { version = "0.11", features = ["json"] }
5959
tracing-appender = "0.2"
60+
arc-swap = "1"
6061
elastic_dsl_utilities.workspace = true
6162
asap_sketchlib = { git = "https://github.com/ProjectASAP/asap_sketchlib" }
6263

asap-query-engine/src/engines/simple_engine/elastic.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ impl SimpleEngine {
5252
// TODO: Figure out how to handle query configuration for ElasticSearch queries.
5353
let query_config = self.find_query_config(&query)?;
5454
let agg_info = self
55-
.get_aggregation_id_info(query_config)
55+
.get_aggregation_id_info(&query_config)
5656
.map_err(|e| {
5757
warn!("{}", e);
5858
e
@@ -76,14 +76,13 @@ impl SimpleEngine {
7676
})
7777
.ok()?;
7878

79-
let grouping_labels = self
80-
.streaming_config
79+
let sc = self.streaming_config.read().unwrap().clone();
80+
let grouping_labels = sc
8181
.get_aggregation_config(agg_info.aggregation_id_for_value)
8282
.map(|config| config.grouping_labels.clone())
8383
.unwrap_or_else(|| query_metadata.query_output_labels.clone());
8484

85-
let aggregated_labels = self
86-
.streaming_config
85+
let aggregated_labels = sc
8786
.get_aggregation_config(agg_info.aggregation_id_for_key)
8887
.map(|config| config.aggregated_labels.clone())
8988
.unwrap_or_else(KeyByLabelNames::empty);

asap-query-engine/src/engines/simple_engine/mod.rs

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::engines::query_result::{InstantVectorElement, QueryResult};
1212
// };
1313
use crate::stores::{Store, TimestampedBucketsMap};
1414
use std::collections::HashMap;
15-
use std::sync::Arc;
15+
use std::sync::{Arc, RwLock};
1616
use std::time::Instant;
1717
use tracing::{debug, warn};
1818

@@ -125,8 +125,12 @@ pub struct RangeQueryExecutionContext {
125125
pub struct SimpleEngine {
126126
store: Arc<dyn Store>,
127127
// promsketch_store: Option<Arc<PromSketchStore>>,
128-
inference_config: InferenceConfig,
129-
streaming_config: Arc<StreamingConfig>,
128+
/// Updated at runtime via update_inference_config(). RwLock provides interior
129+
/// mutability since SimpleEngine is shared behind Arc<SimpleEngine>.
130+
inference_config: RwLock<InferenceConfig>,
131+
/// Updated at runtime via update_streaming_config(). Readers briefly lock to
132+
/// clone the Arc pointer, then use without holding the lock.
133+
streaming_config: RwLock<Arc<StreamingConfig>>,
130134
prometheus_scrape_interval: u64,
131135
controller_patterns: HashMap<QueryPatternType, Vec<PromQLPattern>>,
132136
query_language: QueryLanguage,
@@ -276,25 +280,45 @@ impl SimpleEngine {
276280
Self {
277281
store,
278282
// promsketch_store,
279-
inference_config,
280-
streaming_config,
283+
inference_config: RwLock::new(inference_config),
284+
streaming_config: RwLock::new(streaming_config),
281285
prometheus_scrape_interval,
282286
controller_patterns,
283287
query_language,
284288
}
285289
}
286290

291+
/// Replace the inference config at runtime. Called by the applier task after
292+
/// the planner fires.
293+
///
294+
/// NOTE: streaming_config and inference_config are applied to their respective
295+
/// components independently (not atomically). A brief window may exist where
296+
/// the precompute engine has a new streaming_config but this engine still uses
297+
/// the old inference_config, causing query misses that fall back to Prometheus.
298+
pub fn update_inference_config(&self, new_config: InferenceConfig) {
299+
*self.inference_config.write().unwrap() = new_config;
300+
}
301+
302+
/// Replace the streaming config at runtime. Called by the applier task after
303+
/// the planner fires.
304+
pub fn update_streaming_config(&self, new_config: Arc<StreamingConfig>) {
305+
*self.streaming_config.write().unwrap() = new_config;
306+
}
307+
287308
/// Convert query timestamp (seconds) to data timestamp (milliseconds)
288309
pub fn convert_query_time_to_data_time(query_time: f64) -> u64 {
289310
(query_time * 1000.0) as u64
290311
}
291312

292313
/// Finds the query configuration for a given query string
293-
fn find_query_config(&self, query: &str) -> Option<&QueryConfig> {
314+
fn find_query_config(&self, query: &str) -> Option<QueryConfig> {
294315
self.inference_config
316+
.read()
317+
.unwrap()
295318
.query_configs
296319
.iter()
297320
.find(|config| config.query == query)
321+
.cloned()
298322
}
299323

300324
/// Validates and potentially aligns end timestamp based on query pattern
@@ -344,6 +368,8 @@ impl SimpleEngine {
344368
// Latest window only
345369
let window_size = self
346370
.streaming_config
371+
.read()
372+
.unwrap()
347373
.get_aggregation_config(agg_info.aggregation_id_for_key)
348374
.map(|config| config.window_size * 1000)
349375
.ok_or_else(|| {
@@ -375,9 +401,9 @@ impl SimpleEngine {
375401
timestamps: &QueryTimestamps,
376402
agg_info: &AggregationIdInfo,
377403
) -> Result<StoreQueryPlan, String> {
404+
let sc = self.streaming_config.read().unwrap().clone();
378405
// Get aggregation config for value to determine window type
379-
let aggregation_config_for_value = self
380-
.streaming_config
406+
let aggregation_config_for_value = sc
381407
.get_aggregation_config(agg_info.aggregation_id_for_value)
382408
.ok_or_else(|| {
383409
format!(
@@ -896,10 +922,10 @@ impl SimpleEngine {
896922
let mut aggregation_type_for_key: Option<AggregationType> = None;
897923
let mut aggregation_type_for_value: Option<AggregationType> = None;
898924

925+
let sc = self.streaming_config.read().unwrap().clone();
899926
if query_config_aggregations.len() == 2 {
900927
for aggregation in query_config_aggregations {
901-
let aggregation_type = self
902-
.streaming_config
928+
let aggregation_type = sc
903929
.get_aggregation_config(aggregation.aggregation_id)
904930
.map(|config| config.aggregation_type)
905931
.ok_or_else(|| {
@@ -935,8 +961,7 @@ impl SimpleEngine {
935961
} else {
936962
// Single aggregation: key and value share the same aggregation
937963
let id = query_config_aggregations[0].aggregation_id;
938-
let agg_type = self
939-
.streaming_config
964+
let agg_type = sc
940965
.get_aggregation_config(id)
941966
.map(|config| config.aggregation_type)
942967
.ok_or_else(|| format!("No streaming config for aggregation_id {id}"))?;

asap-query-engine/src/engines/simple_engine/promql.rs

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -155,14 +155,20 @@ impl SimpleEngine {
155155
pub fn find_query_config_promql_structural(
156156
&self,
157157
arm_ast: &promql_parser::parser::Expr,
158-
) -> Option<&QueryConfig> {
158+
) -> Option<QueryConfig> {
159159
let arm_canonical = format!("{}", arm_ast);
160-
self.inference_config.query_configs.iter().find(|config| {
161-
let config_canonical = promql_parser::parser::parse(&config.query)
162-
.map(|ast| format!("{}", ast))
163-
.unwrap_or_default();
164-
config_canonical == arm_canonical
165-
})
160+
self.inference_config
161+
.read()
162+
.unwrap()
163+
.query_configs
164+
.iter()
165+
.find(|config| {
166+
let config_canonical = promql_parser::parser::parse(&config.query)
167+
.map(|ast| format!("{}", ast))
168+
.unwrap_or_default();
169+
config_canonical == arm_canonical
170+
})
171+
.cloned()
166172
}
167173

168174
/// Variant of `build_query_execution_context_promql` that accepts a pre-parsed
@@ -222,7 +228,8 @@ impl SimpleEngine {
222228
) -> Option<QueryExecutionContext> {
223229
let (metric, spatial_filter) = get_metric_and_spatial_filter(match_result);
224230

225-
let promql_schema = match &self.inference_config.schema {
231+
let ic = self.inference_config.read().unwrap();
232+
let promql_schema = match &ic.schema {
226233
SchemaConfig::PromQL(schema) => schema,
227234
_ => return None,
228235
};
@@ -299,14 +306,13 @@ impl SimpleEngine {
299306
let do_merge = query_pattern_type == QueryPatternType::OnlyTemporal
300307
|| query_pattern_type == QueryPatternType::OneTemporalOneSpatial;
301308

302-
let grouping_labels = self
303-
.streaming_config
309+
let sc = self.streaming_config.read().unwrap().clone();
310+
let grouping_labels = sc
304311
.get_aggregation_config(agg_info.aggregation_id_for_value)
305312
.map(|config| config.grouping_labels.clone())
306313
.unwrap_or_else(|| query_output_labels.clone());
307314

308-
let aggregated_labels = self
309-
.streaming_config
315+
let aggregated_labels = sc
310316
.get_aggregation_config(agg_info.aggregation_id_for_key)
311317
.map(|config| config.aggregated_labels.clone())
312318
.unwrap_or_else(KeyByLabelNames::empty);
@@ -355,7 +361,7 @@ impl SimpleEngine {
355361
other => {
356362
// Leaf pattern: structural config lookup + context + plan
357363
let config = self.find_query_config_promql_structural(other)?;
358-
let ctx = self.build_query_execution_context_from_ast(other, config, time)?;
364+
let ctx = self.build_query_execution_context_from_ast(other, &config, time)?;
359365
let label_names = ctx.metadata.query_output_labels.labels.clone();
360366
let plan = ctx.to_logical_plan().ok()?;
361367
Some((plan, label_names))
@@ -463,7 +469,7 @@ impl SimpleEngine {
463469
other => {
464470
let config = self.find_query_config_promql_structural(other)?;
465471
let base_context =
466-
self.build_query_execution_context_from_ast(other, config, end)?;
472+
self.build_query_execution_context_from_ast(other, &config, end)?;
467473
let label_names = base_context.metadata.query_output_labels.labels.clone();
468474

469475
let start_ms = Self::convert_query_time_to_data_time(start);
@@ -472,6 +478,8 @@ impl SimpleEngine {
472478

473479
let tumbling_window_ms = self
474480
.streaming_config
481+
.read()
482+
.unwrap()
475483
.get_aggregation_config(base_context.agg_info.aggregation_id_for_value)
476484
.map(|c| c.window_size * 1000)?;
477485

@@ -619,7 +627,7 @@ impl SimpleEngine {
619627
.map(|d| d.num_seconds() as u64 * 1000),
620628
};
621629

622-
let all_labels = match &self.inference_config.schema {
630+
let all_labels = match &self.inference_config.read().unwrap().schema {
623631
SchemaConfig::PromQL(schema) => schema
624632
.get_labels(&metric)
625633
.cloned()
@@ -1053,7 +1061,7 @@ impl SimpleEngine {
10531061

10541062
// Resolve aggregation: try pre-configured query_configs first, fall back to capability matching.
10551063
let agg_info: AggregationIdInfo = if let Some(config) = self.find_query_config(&query) {
1056-
self.get_aggregation_id_info(config)
1064+
self.get_aggregation_id_info(&config)
10571065
.map_err(|e| {
10581066
warn!("{}", e);
10591067
e
@@ -1067,6 +1075,9 @@ impl SimpleEngine {
10671075
let requirements =
10681076
self.build_query_requirements_promql(&match_result, query_pattern_type);
10691077
self.streaming_config
1078+
.read()
1079+
.unwrap()
1080+
.clone()
10701081
.find_compatible_aggregation(&requirements)?
10711082
};
10721083

@@ -1106,6 +1117,8 @@ impl SimpleEngine {
11061117
// Get window size
11071118
let tumbling_window_ms = self
11081119
.streaming_config
1120+
.read()
1121+
.unwrap()
11091122
.get_aggregation_config(base_context.agg_info.aggregation_id_for_value)
11101123
.map(|config| config.window_size * 1000)?;
11111124

0 commit comments

Comments
 (0)