Skip to content

Commit 18c6c10

Browse files
zzylolclaude
andcommitted
feat(data_plane): bind query-time raw series inputs to Prometheus
A Planner raw-series input contract of an installed physical program is bound to a RawSource that reads the range selector from the configured Prometheus-compatible endpoint inside the run: lazily, under the request budget and cancellation, with bounded, deterministically ordered rows. Endpoint failures are execution errors. Candidate selection is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent e4aced1 commit 18c6c10

5 files changed

Lines changed: 766 additions & 19 deletions

File tree

‎data_plane/src/query_engines/asap_query_engine/engine.rs‎

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -519,19 +519,29 @@ impl ASAPQueryEngine {
519519
inputs.iter().all(|id| {
520520
matches!(
521521
entry.nodes.get(id),
522-
Some(asap_types::query_plan::QueryPlanNode::ReadMaterialization { .. })
522+
Some(
523+
asap_types::query_plan::QueryPlanNode::ReadMaterialization { .. }
524+
| asap_types::query_plan::QueryPlanNode::Logical {
525+
operator: asap_types::query_plan::query_time::QueryTimeOperator::Scan { .. },
526+
..
527+
}
528+
)
523529
)
524530
})
525531
});
526532
let result = if native_stored {
527-
let store = index.ok_or_else(|| {
528-
EngineError::capability_miss("native_stored", "summary store unavailable")
529-
})?;
533+
// Query-time raw reads are external queries under the forwarding policy.
534+
let raw_endpoint = self
535+
.exact_subquery_endpoint
536+
.as_deref()
537+
.filter(|_| self.query_forwarding_policy.allows_external_queries())
538+
.map(|endpoint| (&self.exact_subquery_client, endpoint));
530539
super::logical_dag::native_values::execute_stored(
531540
entry,
532541
physical.query_plan.plan_id,
533542
physical.query_plan.plan_version,
534-
store,
543+
index,
544+
raw_endpoint,
535545
at,
536546
)
537547
} else {

‎data_plane/src/query_engines/asap_query_engine/exact_subqueries.rs‎

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,25 @@ fn leaves(
8787
},
8888
// A join is a typed composition node rather than a Logical
8989
// wrapper, but its value input can still be a Prometheus leaf.
90-
QueryPlanNode::PhysicalFragment { inputs, .. }
91-
| QueryPlanNode::Physical { inputs, .. } => {
90+
QueryPlanNode::PhysicalFragment { inputs, .. } => {
9291
pending.extend(inputs.iter().map(|input| (*input, at)));
9392
}
93+
// Raw Scan inputs of a physical program are bound as lazy physical
94+
// sources at execution, not prepared as exact leaves.
95+
QueryPlanNode::Physical { inputs, .. } => pending.extend(
96+
inputs
97+
.iter()
98+
.filter(|input| {
99+
!matches!(
100+
entry.nodes.get(input),
101+
Some(QueryPlanNode::Logical {
102+
operator: QueryTimeOperator::Scan { .. },
103+
..
104+
})
105+
)
106+
})
107+
.map(|input| (*input, at)),
108+
),
94109

95110
QueryPlanNode::ExternalExact { request, inputs } => {
96111
pending.extend(inputs.iter().map(|input| (*input, at)));

‎data_plane/src/query_engines/asap_query_engine/logical_dag/native_values.rs‎

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -587,11 +587,11 @@ mod tests {
587587
#[test]
588588
fn selected_candidate_input_budget_is_a_terminal_error() {
589589
let plan = CompiledPhysicalDag::decode(&sorted()).unwrap();
590-
let error = execute_batches(&plan, 1, 1, 42, |_, schema| {
591-
Ok(Batch::try_new(
592-
schema.clone(),
590+
let error = execute_batches(&plan, 1, 1, 42, |_, contract| {
591+
Ok(BoundInput::Rows(Batch::try_new(
592+
contract.schema.clone(),
593593
vec![vec![Value::Float64(1.)]],
594-
)?)
594+
)?))
595595
})
596596
.expect_err("input must exceed the byte budget");
597597
assert!(
@@ -767,7 +767,8 @@ where
767767
max_bytes,
768768
bindings.len(),
769769
at,
770-
|input_id, schema| {
770+
|input_id, contract| {
771+
let schema = &contract.schema;
771772
let values = super::vector(super::from_result(callback(bindings[&input_id], at)?)?)?;
772773
let rows = values
773774
.into_iter()
@@ -781,7 +782,9 @@ where
781782
.map_err(|e| miss(e.to_string()))
782783
})
783784
.collect::<Result<Vec<_>, _>>()?;
784-
Batch::try_new(schema.clone(), rows).map_err(EngineError::from)
785+
Batch::try_new(schema.clone(), rows)
786+
.map(BoundInput::Rows)
787+
.map_err(EngineError::from)
785788
},
786789
)
787790
}
@@ -790,7 +793,8 @@ pub(in crate::query_engines::asap_query_engine) fn execute_stored(
790793
entry: &asap_types::query_plan::QueryPlanEntry,
791794
plan_id: u64,
792795
plan_version: u64,
793-
store: &crate::storage_engines::sketch_db::index::SketchStore,
796+
store: Option<&crate::storage_engines::sketch_db::index::SketchStore>,
797+
raw_endpoint: Option<(&reqwest::Client, &str)>,
794798
at: u64,
795799
) -> Result<
796800
(
@@ -805,16 +809,28 @@ pub(in crate::query_engines::asap_query_engine) fn execute_stored(
805809
let program = entry
806810
.recover_vector_physical_dag()
807811
.map_err(|e| miss(e.to_string()))?;
808-
execute_batches(&program, max_bytes, inputs.len(), at, |id, schema| {
812+
execute_batches(&program, max_bytes, inputs.len(), at, |id, contract| {
813+
let schema = &contract.schema;
809814
let index = sources
810815
.iter()
811816
.position(|source| *source == id)
812817
.ok_or_else(|| miss("native source is unbound"))?;
813-
let Some(asap_types::query_plan::QueryPlanNode::ReadMaterialization { binding }) =
814-
entry.nodes.get(&inputs[index])
818+
let node = entry.nodes.get(&inputs[index]);
819+
if let Some(asap_types::query_plan::QueryPlanNode::Logical { operator, .. }) = node {
820+
let (client, endpoint) = raw_endpoint
821+
.ok_or_else(|| miss("query-time raw input has no Prometheus endpoint"))?;
822+
let at = i64::try_from(at).map_err(|_| miss("native timestamp overflow"))?;
823+
return Ok(BoundInput::Lazy(super::super::raw_source::bind(
824+
contract, operator, at, client, endpoint,
825+
)?));
826+
}
827+
let Some(asap_types::query_plan::QueryPlanNode::ReadMaterialization { binding }) = node
815828
else {
816829
return Err(miss("native stored source has no deployed summary binding"));
817830
};
831+
let store = store.ok_or_else(|| {
832+
EngineError::capability_miss("native_stored", "summary store unavailable")
833+
})?;
818834
let end = i64::try_from(at).map_err(|_| miss("native timestamp overflow"))?;
819835
let start = at
820836
.checked_sub(binding.window_ms)
@@ -836,6 +852,7 @@ pub(in crate::query_engines::asap_query_engine) fn execute_stored(
836852
schema.clone(),
837853
max_bytes as usize,
838854
)
855+
.map(BoundInput::Rows)
839856
.map_err(|error| match error {
840857
crate::storage_engines::sketch_db::index::NativeReadError::Unavailable(message) => {
841858
miss(message)
@@ -847,12 +864,22 @@ pub(in crate::query_engines::asap_query_engine) fn execute_stored(
847864
})
848865
}
849866

867+
/// Stored and protocol inputs are read before execution; query-time raw
868+
/// sources open lazily inside the run and charge its budget as they stream.
869+
enum BoundInput {
870+
Rows(Batch),
871+
Lazy(asap_physical_operators::physical_planner::Source<'static>),
872+
}
873+
850874
fn execute_batches(
851875
program: &asap_physical_operators::physical_planner::CompiledPhysicalDag,
852876
max_bytes: u64,
853877
input_count: usize,
854878
at: u64,
855-
mut input_batch: impl FnMut(u64, &Schema) -> Result<Batch, EngineError>,
879+
mut input_batch: impl FnMut(
880+
u64,
881+
&asap_physical_operators::physical_planner::InputContract,
882+
) -> Result<BoundInput, EngineError>,
856883
) -> Result<
857884
(
858885
crate::query_engines::query_result::QueryResult,
@@ -887,7 +914,13 @@ fn execute_batches(
887914
let mut input_bytes = 0usize;
888915
for (input_id, input) in program.input_contracts() {
889916
crate::query_engines::request::check()?;
890-
let batch = input_batch(input_id, &input.schema)?;
917+
let batch = match input_batch(input_id, input)? {
918+
BoundInput::Rows(batch) => batch,
919+
BoundInput::Lazy(source) => {
920+
sources.insert(input_id, source);
921+
continue;
922+
}
923+
};
891924
input_bytes = input_bytes
892925
.checked_add(batch.bytes())
893926
.ok_or(asap_physical_operators::Error::MemoryLimit)?;
@@ -996,6 +1029,7 @@ mod request_contract_tests {
9961029
)?;
9971030
execute_batches(&program, 64 * 1024, 1, 0, |_, _| {
9981031
Batch::try_new(schema.clone(), vec![vec![Value::Float64(1.0)]])
1032+
.map(BoundInput::Rows)
9991033
.map_err(EngineError::from)
10001034
})
10011035
},

‎data_plane/src/query_engines/asap_query_engine/mod.rs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ mod exact_subqueries;
77
pub mod live_serve;
88
pub mod logical_dag;
99
pub mod post_asap_readout;
10+
mod raw_source;
1011
pub mod summary_executor;
1112

1213
pub use crate::storage_engines::sketch_db::query as asap_tier;

0 commit comments

Comments
 (0)