@@ -984,6 +984,11 @@ impl BackendLocalPlanningInput {
984984 & strategy,
985985 & asap_aware_mapping:: TargetSubDAG :: new ( & typed) ,
986986 ) ;
987+ proposed. candidates . extend (
988+ strategy
989+ . fixed_window_rate_topk_candidates ( & typed)
990+ . candidates ,
991+ ) ;
987992 proposed. candidates . extend ( direct. candidates ) ;
988993 proposed. rejected . extend ( direct. rejected ) ;
989994 for candidate in proposed. candidates {
@@ -992,6 +997,16 @@ impl BackendLocalPlanningInput {
992997 } ;
993998 let compiled = asap_physical_operators:: physical_planner:: promql_rows:: compile_current_series_readout ( & root)
994999 . or_else ( |_| asap_physical_operators:: physical_planner:: promql_rows:: compile_rate_ranking ( & root) . map ( |( _, program) | program) ) ;
1000+ if let Ok ( physical) = asap_physical_operators:: physical_planner:: promql_rows:: compile_fixed_window_rate_ranking ( & root) {
1001+ planner_selection_trace. push ( serde_json:: json!( {
1002+ "stage" : "planner.physical_candidate" , "query_id" : query. query_id,
1003+ "logical_root_id" : crate :: planner_selection:: explained_root_id( & root, & query. accuracy_target) ,
1004+ "rationale" : candidate. rationale, "physical_candidate" : serde_json:: from_slice:: <Value >( & physical. encode( ) . map_err( |e| CompileError :: Snapshot ( e. to_string( ) ) ) ?) . map_err( |e| CompileError :: Snapshot ( e. to_string( ) ) ) ?,
1005+ "guarantee" : root. guarantee,
1006+ } ) ) ;
1007+ candidate_roots. push ( vec ! [ ( index, root) ] ) ;
1008+ continue ;
1009+ }
9951010 match compiled {
9961011 Ok ( program) => {
9971012 planner_selection_trace. push ( serde_json:: json!( {
@@ -1533,7 +1548,7 @@ impl DeploymentPlanCompiler {
15331548 for state in & selected {
15341549 if matches ! ( & state. node. expr, SummaryExpr :: SummaryAgg {
15351550 reduction: planner_types:: pre_asap:: Reduction :: Reduce ( keys) , ..
1536- } if keys. is_empty( ) )
1551+ } if keys. is_empty( ) ) || asap_physical_operators :: physical_planner :: promql_rows :: compile_fixed_window_rate_ranking ( & query . selected_plan_root ) . is_ok ( )
15371552 {
15381553 if let Some ( sources) = immutable_materialization_sources ( & state. node ) {
15391554 canonical_nodes. insert ( Rc :: as_ptr ( & state. node ) as usize ) ;
@@ -2115,7 +2130,31 @@ impl DeploymentPlanCompiler {
21152130 & query. selected_plan_root ,
21162131 )
21172132 . ok ( ) ;
2133+ let native_rate = native_rate. or_else ( || {
2134+ let physical = asap_physical_operators:: physical_planner:: promql_rows:: compile_fixed_window_rate_ranking ( & query. selected_plan_root ) . ok ( ) ?;
2135+ fn heap ( node : & Rc < SummaryNode > ) -> Option < Rc < SummaryNode > > {
2136+ match & node. expr {
2137+ SummaryExpr :: SummaryAgg { family : SummaryFamilyType :: Sketch ( ..) , .. } => Some ( node. clone ( ) ) ,
2138+ SummaryExpr :: ValueOperation { child, .. } => heap ( child) ,
2139+ SummaryExpr :: SummaryEstimate { summary_input, .. } => heap ( summary_input) ,
2140+ _ => None ,
2141+ }
2142+ }
2143+ Some ( ( heap ( & query. selected_plan_root ) ?, physical. query ) )
2144+ } ) ;
21182145 let mut entry = if let Some ( ( source, program) ) = native_rate {
2146+ let native_state_binding = if let SummaryExpr :: SummaryAgg {
2147+ family : SummaryFamilyType :: Sketch ( ..) ,
2148+ ..
2149+ } = & source. expr
2150+ {
2151+ let SummaryExpr :: SummaryAgg { family, .. } = & source. expr else {
2152+ unreachable ! ( )
2153+ } ;
2154+ Some ( binding ( & source, family) ?)
2155+ } else {
2156+ None
2157+ } ;
21192158 let mut entry = crate :: query_plan:: compile_bound_composable_mapped (
21202159 query. query_id . clone ( ) ,
21212160 canonical. clone ( ) ,
@@ -2132,6 +2171,12 @@ impl DeploymentPlanCompiler {
21322171 }
21332172 } ,
21342173 ) ?;
2174+ if let Some ( binding) = native_state_binding {
2175+ entry. nodes . insert (
2176+ entry. root ,
2177+ crate :: query_plan:: QueryPlanNode :: ReadMaterialization { binding } ,
2178+ ) ;
2179+ }
21352180 let root = crate :: query_plan:: QueryNodeId (
21362181 entry. nodes . keys ( ) . map ( |id| id. 0 ) . max ( ) . unwrap_or ( 0 ) + 1 ,
21372182 ) ;
@@ -2297,7 +2342,7 @@ impl DeploymentPlanCompiler {
22972342 . find ( |entry| entry. query_id == query_id)
22982343 . expect ( "compiled query entry exists" )
22992344 . root ;
2300- let installed = super :: executable_binding:: install_selected_dag (
2345+ let mut installed = super :: executable_binding:: install_selected_dag (
23012346 query_id. clone ( ) ,
23022347 & compiled. dag ,
23032348 query_plan_sink,
@@ -2313,11 +2358,35 @@ impl DeploymentPlanCompiler {
23132358 query_id : query_id. clone ( ) ,
23142359 reason,
23152360 } ) ?;
2361+ if let Ok ( physical) = asap_physical_operators:: physical_planner:: promql_rows:: compile_fixed_window_rate_ranking ( & request. queries [ query_index] . selected_plan_root ) {
2362+ let program = physical. precompute . ok_or_else ( || CompileError :: Snapshot ( "fixed-window candidate has no maintenance program" . into ( ) ) ) ?;
2363+ let sink = PostAsapNodeId ( u32:: try_from ( program. roots ( ) [ 0 ] ) . map_err ( |_| CompileError :: Snapshot ( "maintenance sink overflow" . into ( ) ) ) ?) ;
2364+ installed. native_programs . insert ( sink, serde_json:: from_slice ( & program. encode ( ) . map_err ( |e| CompileError :: Snapshot ( e. to_string ( ) ) ) ?) . map_err ( |e| CompileError :: Snapshot ( e. to_string ( ) ) ) ?) ;
2365+ }
23162366 query_plan
23172367 . selected_dags
23182368 . insert ( query_id. clone ( ) , installed. document . clone ( ) ) ;
23192369 installed_dags. insert ( query_id, installed) ;
23202370 }
2371+ let maintenance_lookbacks = materializations
2372+ . iter ( )
2373+ . filter_map ( |target| {
2374+ target
2375+ . derived_input
2376+ . as_ref ( )
2377+ . map ( |input| ( input, target. stored_window_ms ( ) ) )
2378+ } )
2379+ . flat_map ( |( input, window) | input. inputs . iter ( ) . map ( move |source| ( * source, window) ) )
2380+ . fold (
2381+ BTreeMap :: < asap_types:: sds:: StoredOutputId , u64 > :: new ( ) ,
2382+ |mut windows, ( source, window) | {
2383+ windows
2384+ . entry ( source)
2385+ . and_modify ( |old| * old = ( * old) . max ( window) )
2386+ . or_insert ( window) ;
2387+ windows
2388+ } ,
2389+ ) ;
23212390 for materialization in & mut materializations {
23222391 let fingerprint = materialization. policy_fingerprint ( ) ;
23232392 let max_lookback_ms = query_plan
@@ -2326,6 +2395,7 @@ impl DeploymentPlanCompiler {
23262395 . flat_map ( QueryPlanEntry :: materialization_bindings)
23272396 . filter ( |binding| binding. materialization . fingerprint ( ) == fingerprint)
23282397 . filter_map ( |binding| binding. readout_lookback_ms )
2398+ . chain ( maintenance_lookbacks. get ( & fingerprint. into ( ) ) . copied ( ) )
23292399 . max ( ) ;
23302400 if let Some ( lookback_ms) = max_lookback_ms {
23312401 materialization. num_aggregates_to_retain = Some ( retained_state_count (
@@ -3434,6 +3504,11 @@ pub(crate) fn retained_partition_count(
34343504 A :: Increase | A :: Rate | A :: Min | A :: Max
34353505 )
34363506 || !materialization. grouping_labels . names ( ) . is_empty ( )
3507+ || ( materialization. derived_input . is_some ( )
3508+ && matches ! (
3509+ materialization. aggregation_type,
3510+ A :: CountMinSketchWithHeap | A :: CountSketchWithHeap
3511+ ) )
34373512 {
34383513 u128:: from ( input_cardinality. unwrap_or ( 1 ) . max ( 1 ) )
34393514 } else {
@@ -3761,6 +3836,21 @@ fn immutable_materialization_sources(node: &SummaryNode) -> Option<Vec<Rc<Summar
37613836 // because Planner made the exact accumulator boundary explicit.
37623837 return None ;
37633838 } ;
3839+ if matches ! ( & node. expr, SummaryExpr :: SummaryAgg { family: SummaryFamilyType :: Sketch ( kind, _) , .. }
3840+ if matches!( kind. algorithm( ) , SketchAlgorithm :: CmsWithHeap | SketchAlgorithm :: CountSketchWithHeap ) )
3841+ {
3842+ if let SummaryExpr :: ValueOperation {
3843+ child : source,
3844+ operation : planner_types:: post_asap:: ValueOperation :: FinalizeExactAccumulator ,
3845+ timing : ExecutionTiming :: IngestionTime ,
3846+ } = & child. expr
3847+ {
3848+ if matches ! ( & source. expr, SummaryExpr :: SummaryAgg { family: SummaryFamilyType :: ExactAggregate ( ExactKind :: Rate , _) , reduction: planner_types:: pre_asap:: Reduction :: PerEntity , child, .. } if matches!( child. expr, SummaryExpr :: KeepPreAsap ( _) ) )
3849+ {
3850+ return Some ( vec ! [ source. clone( ) ] ) ;
3851+ }
3852+ }
3853+ }
37643854 if input. item . is_some ( )
37653855 || !matches ! (
37663856 input. weight,
@@ -3934,6 +4024,22 @@ fn scoped_materialization(
39344024 let SummaryExpr :: SummaryAgg { reduction, .. } = & node. expr else {
39354025 anyhow:: bail!( "materialization lacks SummaryAgg partition contract" ) ;
39364026 } ;
4027+ if immutable_materialization_sources ( node) . is_some_and ( |sources| {
4028+ sources. iter ( ) . any ( |source| {
4029+ matches ! (
4030+ & source. expr,
4031+ SummaryExpr :: SummaryAgg {
4032+ family: SummaryFamilyType :: ExactAggregate (
4033+ planner_types:: post_asap:: ExactKind :: Rate ,
4034+ _
4035+ ) ,
4036+ ..
4037+ }
4038+ )
4039+ } )
4040+ } ) {
4041+ config. grouping_labels = asap_types:: KeyByLabelNames { labels : Vec :: new ( ) } . into ( ) ;
4042+ }
39374043 config. partitioning = Some ( match reduction {
39384044 planner_types:: pre_asap:: Reduction :: PerEntity => {
39394045 asap_types:: sds:: PopulationPartitioning :: PerEntity
0 commit comments