@@ -12,7 +12,7 @@ use crate::engines::query_result::{InstantVectorElement, QueryResult};
1212// };
1313use crate :: stores:: { Store , TimestampedBucketsMap } ;
1414use std:: collections:: HashMap ;
15- use std:: sync:: Arc ;
15+ use std:: sync:: { Arc , RwLock } ;
1616use std:: time:: Instant ;
1717use tracing:: { debug, warn} ;
1818
@@ -125,8 +125,12 @@ pub struct RangeQueryExecutionContext {
125125pub 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}" ) ) ?;
0 commit comments