Skip to content

Commit 085e439

Browse files
fix(runtime): bound relation future construction on native stacks
1 parent 7e364ea commit 085e439

2 files changed

Lines changed: 41 additions & 26 deletions

File tree

‎crates/plasm-agent-core/src/plasm_plan_run/materialize.rs‎

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55
66
use super::*;
77

8+
// Construct relation child futures outside the dispatcher's poll frame. Boxing
9+
// an already-constructed future still reserves its stack temporary in that frame.
10+
fn pin_relation_future<F: std::future::Future>(make: impl FnOnce() -> F) -> std::pin::Pin<Box<F>> {
11+
Box::pin(make())
12+
}
13+
814
fn materialization_failure(code: &'static str, diagnostic: impl Into<String>) -> ExecutionFailure {
915
ExecutionFailure::new(plasm_runtime::FailureCause::Program, code, diagnostic)
1016
}
@@ -282,23 +288,25 @@ pub(crate) async fn materialize_validated_relation_traversal(
282288
"relation traversal",
283289
)));
284290
}
285-
return super::compute_eval::finalize_empty_relation_materialized_node(
286-
st,
287-
es,
288-
session_id,
289-
node,
290-
relation,
291-
trace,
292-
read_cap,
293-
source_mat
294-
.result
295-
.collection
296-
.flat_map(&"empty_relation", &[])?,
291+
return Box::pin(
292+
super::compute_eval::finalize_empty_relation_materialized_node(
293+
st,
294+
es,
295+
session_id,
296+
node,
297+
relation,
298+
trace,
299+
read_cap,
300+
source_mat
301+
.result
302+
.collection
303+
.flat_map(&"empty_relation", &[])?,
304+
),
297305
)
298306
.await;
299307
}
300308
match &relation.relation.materialize {
301-
RelationMaterialization::FromParentGet { .. } => Box::pin(try_materialize_from_parent_get_relation(
309+
RelationMaterialization::FromParentGet { .. } => pin_relation_future(|| try_materialize_from_parent_get_relation(
302310
st,
303311
es,
304312
session_id,
@@ -318,7 +326,7 @@ pub(crate) async fn materialize_validated_relation_traversal(
318326
))
319327
}),
320328
RelationMaterialization::PreferFromParentGet { .. } => {
321-
Box::pin(materialize_prefer_from_parent_get_relation(
329+
pin_relation_future(|| materialize_prefer_from_parent_get_relation(
322330
st,
323331
es,
324332
session_id,
@@ -340,7 +348,7 @@ pub(crate) async fn materialize_validated_relation_traversal(
340348
relation.relation.source_cardinality,
341349
RelationSourceCardinality::Many
342350
) {
343-
Box::pin(materialize_relation_scoped_fanout(
351+
pin_relation_future(|| materialize_relation_scoped_fanout(
344352
st,
345353
es,
346354
session_id,
@@ -368,7 +376,7 @@ pub(crate) async fn materialize_validated_relation_traversal(
368376
"relation traversal",
369377
)));
370378
}
371-
Box::pin(materialize_relation_singleton_chain(
379+
pin_relation_future(|| materialize_relation_singleton_chain(
372380
st,
373381
es,
374382
session_id,
@@ -389,7 +397,7 @@ pub(crate) async fn materialize_validated_relation_traversal(
389397
relation.relation.source_cardinality,
390398
RelationSourceCardinality::Many
391399
) {
392-
return Box::pin(materialize_relation_scoped_fanout(
400+
return pin_relation_future(|| materialize_relation_scoped_fanout(
393401
st,
394402
es,
395403
session_id,
@@ -417,7 +425,7 @@ pub(crate) async fn materialize_validated_relation_traversal(
417425
"relation traversal",
418426
)));
419427
}
420-
Box::pin(materialize_relation_singleton_chain(
428+
pin_relation_future(|| materialize_relation_singleton_chain(
421429
st,
422430
es,
423431
session_id,
@@ -431,7 +439,7 @@ pub(crate) async fn materialize_validated_relation_traversal(
431439
.await
432440
}
433441
RelationMaterialization::ViewEmbed { .. } => {
434-
materialize_cached_embed_or_error(
442+
pin_relation_future(|| materialize_cached_embed_or_error(
435443
st,
436444
es,
437445
session_id,
@@ -449,7 +457,7 @@ pub(crate) async fn materialize_validated_relation_traversal(
449457
relation.relation.relation
450458
)
451459
},
452-
)
460+
))
453461
.await
454462
}
455463
RelationMaterialization::Unavailable => {
@@ -458,7 +466,7 @@ pub(crate) async fn materialize_validated_relation_traversal(
458466
RelationSourceCardinality::Many
459467
) {
460468
// Plural source → per-row fanout (covers one-from-many and many-from-many).
461-
Box::pin(materialize_relation_scoped_fanout(
469+
pin_relation_future(|| materialize_relation_scoped_fanout(
462470
st,
463471
es,
464472
session_id,
@@ -477,7 +485,7 @@ pub(crate) async fn materialize_validated_relation_traversal(
477485
relation.relation.cardinality,
478486
crate::plasm_plan::RelationCardinality::One
479487
) {
480-
Box::pin(materialize_relation_singleton_chain(
488+
pin_relation_future(|| materialize_relation_singleton_chain(
481489
st,
482490
es,
483491
session_id,

‎crates/plasm-node/src/hydration_boundary_tests.rs‎

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ async fn run_native_hydration() {
154154
let envelope: serde_json::Value =
155155
serde_json::from_str(result.rows_json.as_deref().unwrap()).unwrap();
156156
assert_eq!(
157-
envelope[0]["rows"][0]["content"],
157+
envelope[0]["rows"][0]["value"],
158158
json!((1..=18)
159159
.map(|i| format!("note-{i}:owner-{i}"))
160160
.collect::<Vec<_>>()
@@ -179,7 +179,7 @@ async fn run_native_hydration() {
179179
assert!(result.ok, "{}", result.message);
180180
let envelope: serde_json::Value =
181181
serde_json::from_str(result.rows_json.as_deref().unwrap()).unwrap();
182-
assert_eq!(envelope[0]["rows"][0]["content"], json!("18"));
182+
assert_eq!(envelope[0]["rows"][0]["value"], json!("18"));
183183
let derived_program = compute_program
184184
.replace(&format!("list[Value[{note}]]"), "list[Row]")
185185
.replace("return self.size(notes.select(\"note_id\"))", "ids = notes.select(\"note_id\")\n members = notes.where(lambda row: row.note_id in ids).select(\"note_id\")\n combined = members.union(ids).distinct(\"note_id\")\n return self.size(combined)");
@@ -191,7 +191,7 @@ async fn run_native_hydration() {
191191
assert!(result.ok, "{}", result.message);
192192
let envelope: serde_json::Value =
193193
serde_json::from_str(result.rows_json.as_deref().unwrap()).unwrap();
194-
assert_eq!(envelope[0]["rows"][0]["content"], json!("18"));
194+
assert_eq!(envelope[0]["rows"][0]["value"], json!("18"));
195195

196196
for (identity, edge, expected) in [
197197
("empty", "notes", Some("0")),
@@ -209,7 +209,7 @@ async fn run_native_hydration() {
209209
assert!(result.ok, "{}", result.message);
210210
let envelope: serde_json::Value =
211211
serde_json::from_str(result.rows_json.as_deref().unwrap()).unwrap();
212-
assert_eq!(envelope[0]["rows"][0]["content"], json!(expected));
212+
assert_eq!(envelope[0]["rows"][0]["value"], json!(expected));
213213
} else {
214214
let result = result.unwrap();
215215
assert!(
@@ -245,6 +245,13 @@ async fn run_native_hydration() {
245245
let envelope: serde_json::Value =
246246
serde_json::from_str(result.rows_json.as_deref().unwrap()).unwrap();
247247
// The explicit return is followed by the provider's operation receipt.
248+
assert!(envelope[1]["operations"]
249+
.as_array()
250+
.unwrap()
251+
.iter()
252+
.any(|operation| {
253+
operation["capability"] == "login" && operation["completed"] == 1
254+
}));
248255
let ids: Vec<_> = envelope[0]["rows"]
249256
.as_array()
250257
.expect("owners rows")

0 commit comments

Comments
 (0)