Skip to content

Commit 11306d0

Browse files
committed
fix(stack): serialize precompute state identity as a wire DTO
1 parent e4f52b4 commit 11306d0

2 files changed

Lines changed: 80 additions & 21 deletions

File tree

‎control_plane/src/physical/compiler.rs‎

Lines changed: 77 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ use asap_aware_mapping::{
1414
SummaryMaintenanceLifecycleCapabilities, SummaryMaintenanceLifecycleCostInputs, WorkloadDemand,
1515
};
1616
use planner_types::post_asap::{
17-
CompositionOperator, EvaluationSchedule, OutputRepresentation, SketchQuery, SummaryExpr,
18-
SummaryFamilyType, SummaryMaintenanceLifecycle, SummaryMaintenanceLifecycleGuarantee,
19-
SummaryMaintenanceMode, SummaryNode, SummaryWindowFramework,
17+
CompositionOperator, EvaluationSchedule, OutputRepresentation, SketchAlgorithm, SketchParams,
18+
SketchQuery, SummaryExpr, SummaryFamilyType, SummaryMaintenanceLifecycle,
19+
SummaryMaintenanceLifecycleGuarantee, SummaryMaintenanceMode, SummaryNode,
20+
SummaryWindowFramework,
2021
};
2122
use planner_types::pre_asap::QueryExpr;
2223
use planner_types::workload::{
@@ -236,14 +237,62 @@ pub enum StateEncoding {
236237
ExactAccumulatorV1,
237238
}
238239

240+
/// Serializable physical state identity derived from Planner's canonical
241+
/// summary family. This is a wire DTO, not a second planning algebra.
242+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
243+
#[serde(tag = "family", rename_all = "snake_case", deny_unknown_fields)]
244+
pub enum StateFamilyContract {
245+
Exact {
246+
kind: ExactStateKind,
247+
},
248+
Sketch {
249+
algorithm: SketchAlgorithm,
250+
parameters: SketchParams,
251+
},
252+
}
253+
254+
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
255+
#[serde(rename_all = "snake_case")]
256+
pub enum ExactStateKind {
257+
Sum,
258+
Count,
259+
MinMax,
260+
Increase,
261+
Rate,
262+
}
263+
264+
impl TryFrom<&SummaryFamilyType> for StateFamilyContract {
265+
type Error = ();
266+
267+
fn try_from(family: &SummaryFamilyType) -> Result<Self, Self::Error> {
268+
use planner_types::post_asap::ExactKind;
269+
Ok(match family {
270+
SummaryFamilyType::ExactAggregate(kind, _) => Self::Exact {
271+
kind: match kind {
272+
ExactKind::Sum => ExactStateKind::Sum,
273+
ExactKind::Count => ExactStateKind::Count,
274+
ExactKind::MinMax => ExactStateKind::MinMax,
275+
ExactKind::Increase => ExactStateKind::Increase,
276+
ExactKind::Rate => ExactStateKind::Rate,
277+
},
278+
},
279+
SummaryFamilyType::Sketch(kind, _) => Self::Sketch {
280+
algorithm: kind.algorithm().clone(),
281+
parameters: kind.params().clone(),
282+
},
283+
_ => return Err(()),
284+
})
285+
}
286+
}
287+
239288
/// Decoder/schema contract for one content-addressed materialization.
240289
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
241290
#[serde(deny_unknown_fields)]
242291
pub struct StateSchemaContract {
243292
pub schema_id: String,
244293
pub schema_version: u32,
245294
pub materialization: asap_types::PolicyFingerprint,
246-
pub family: SummaryFamilyType,
295+
pub family: StateFamilyContract,
247296
pub source: Source,
248297
pub value_column: planner_types::pre_asap::ColumnRef,
249298
pub group_by: Vec<String>,
@@ -282,6 +331,8 @@ pub enum PrecomputePlanError {
282331
SchemaSetMismatch,
283332
#[error("schema {schema_id} has invalid version or no encoding")]
284333
InvalidSchema { schema_id: String },
334+
#[error("materialization {0} uses a summary family unsupported by the runtime schema")]
335+
UnsupportedFamily(u64),
285336
#[error("producer {producer_id} references an unknown materialization or schema")]
286337
InvalidProducer { producer_id: String },
287338
#[error("materialization {0} has no registered producer")]
@@ -309,22 +360,26 @@ impl PrecomputePlan {
309360
let schemas = backend_plan
310361
.materializations
311362
.iter()
312-
.map(|(fingerprint, materialization)| StateSchemaContract {
313-
schema_id: state_schema_id(*fingerprint),
314-
schema_version: 1,
315-
materialization: *fingerprint,
316-
family: materialization.family.clone(),
317-
source: materialization.source.clone(),
318-
value_column: materialization.col.clone(),
319-
group_by: materialization.group_by.clone(),
320-
window: StateWindowContract {
321-
kind: materialization.window.kind,
322-
size_ms: materialization.window.size_ms,
323-
slide_ms: materialization.window.slide_ms,
324-
},
325-
encodings: state_encodings(&materialization.family),
363+
.map(|(fingerprint, materialization)| {
364+
let family = StateFamilyContract::try_from(&materialization.family)
365+
.map_err(|_| PrecomputePlanError::UnsupportedFamily(fingerprint.0))?;
366+
Ok(StateSchemaContract {
367+
schema_id: state_schema_id(*fingerprint),
368+
schema_version: 1,
369+
materialization: *fingerprint,
370+
family,
371+
source: materialization.source.clone(),
372+
value_column: materialization.col.clone(),
373+
group_by: materialization.group_by.clone(),
374+
window: StateWindowContract {
375+
kind: materialization.window.kind,
376+
size_ms: materialization.window.size_ms,
377+
slide_ms: materialization.window.slide_ms,
378+
},
379+
encodings: state_encodings(&materialization.family),
380+
})
326381
})
327-
.collect::<Vec<_>>();
382+
.collect::<Result<Vec<_>, PrecomputePlanError>>()?;
328383
let producers = producer_ids
329384
.iter()
330385
.flat_map(|producer_id| {
@@ -443,7 +498,9 @@ impl PrecomputePlan {
443498
else {
444499
return Err(PrecomputePlanError::SchemaSetMismatch);
445500
};
446-
if schema.family != materialization.family
501+
let expected_family = StateFamilyContract::try_from(&materialization.family)
502+
.map_err(|_| PrecomputePlanError::UnsupportedFamily(schema.materialization.0))?;
503+
if schema.family != expected_family
447504
|| schema.schema_id != state_schema_id(schema.materialization)
448505
|| schema.source != materialization.source
449506
|| schema.value_column != materialization.col

‎data_plane/src/drivers/query/servers/http.rs‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5410,7 +5410,9 @@ async fn handle_post_physical_plan(
54105410
)
54115411
.into_response();
54125412
};
5413-
if materialization.kind != schema.family || materialization.params != schema.parameters {
5413+
if control_plane::physical::compiler::StateFamilyContract::try_from(&materialization.family)
5414+
!= Ok(schema.family.clone())
5415+
{
54145416
return (
54155417
StatusCode::UNPROCESSABLE_ENTITY,
54165418
axum::Json(serde_json::json!({

0 commit comments

Comments
 (0)