@@ -216,6 +216,8 @@ pub struct PhysicalCompilationRequest {
216216 pub query_workload : Option < QueryWorkload > ,
217217 /// Source evidence supplied independently from query demand.
218218 pub data_workload : Option < DataWorkload > ,
219+ /// Per-metric samples/second used to price raw selector folds.
220+ pub source_ingestion_rates : BTreeMap < String , Evidence < Rate > > ,
219221 /// Workload-lowered roots retained across physical candidate enumeration.
220222 pub canonical_roots : Vec < Rc < QueryExpr > > ,
221223 pub queries : Vec < QueryCompilationInput > ,
@@ -438,6 +440,10 @@ pub struct BackendLocalPhysicalInputs {
438440 #[ serde( default , skip_serializing_if = "std::ops::Not::not" ) ]
439441 pub require_backend_local_execution : bool ,
440442 pub lifecycle_costs : LifecycleUnitCosts ,
443+ /// Samples/second by metric, before label filtering. Missing or stale
444+ /// evidence falls back to the conservative workload-wide ingestion rate.
445+ #[ serde( default , skip_serializing_if = "BTreeMap::is_empty" ) ]
446+ pub source_ingestion_rates : BTreeMap < String , Evidence < Rate > > ,
441447 pub evidence_observed_at_unix_ms : u64 ,
442448 pub evidence_valid_for_ms : u64 ,
443449 pub horizon_seconds : f64 ,
@@ -912,6 +918,18 @@ impl BackendLocalPlanningInput {
912918 "compatibility workload requires backend_local_remote_write target" . into ( ) ,
913919 ) ) ;
914920 }
921+ for ( metric, evidence) in & self . physical_inputs . source_ingestion_rates {
922+ if metric. trim ( ) . is_empty ( )
923+ || evidence
924+ . value
925+ . is_some_and ( |rate| !rate. 0 . is_finite ( ) || rate. 0 < 0.0 )
926+ {
927+ return Err ( CompileError :: Snapshot (
928+ "source_ingestion_rates requires named metrics and finite nonnegative rates"
929+ . into ( ) ,
930+ ) ) ;
931+ }
932+ }
915933 let workload = self . query_workload ;
916934 let mut data_workload = self . data_workload . clone ( ) ;
917935 let scoped_snapshot_id = self
@@ -1219,6 +1237,7 @@ impl BackendLocalPlanningInput {
12191237 & data_workload,
12201238 index,
12211239 & self . environment ,
1240+ & self . physical_inputs ,
12221241 ) else {
12231242 if typed_search {
12241243 planner_selection_trace. push ( serde_json:: json!( {
@@ -1280,6 +1299,7 @@ impl BackendLocalPlanningInput {
12801299 require_backend_local_execution : self . physical_inputs . require_backend_local_execution ,
12811300 query_workload : Some ( workload) ,
12821301 data_workload : Some ( data_workload) ,
1302+ source_ingestion_rates : self . physical_inputs . source_ingestion_rates ,
12831303 canonical_roots,
12841304 queries,
12851305 topk_membership_evidence_by_query_id : topk_evidence_by_id,
@@ -6098,6 +6118,7 @@ pub(crate) mod tests {
60986118 require_backend_local_execution : false ,
60996119 query_workload : None ,
61006120 data_workload : None ,
6121+ source_ingestion_rates : BTreeMap :: new ( ) ,
61016122 queries : vec ! [ QueryCompilationInput {
61026123 query_id: query_id. into( ) ,
61036124 query_string: promql. into( ) ,
@@ -8981,6 +9002,7 @@ pub(crate) mod tests {
89819002 physical_inputs : BackendLocalPhysicalInputs {
89829003 require_backend_local_execution : false ,
89839004 lifecycle_costs : template. summary_lifecycle_inputs . costs ,
9005+ source_ingestion_rates : BTreeMap :: new ( ) ,
89849006 evidence_observed_at_unix_ms : 9_500 ,
89859007 evidence_valid_for_ms : 60_000 ,
89869008 horizon_seconds : 300.0 ,
0 commit comments