This recipe processes a bounded upload with structured concurrency, backpressure, cancellation, and deterministic cleanup.
The .one and Rust APIs below are illustrative end-product syntax.
1. Declare resource and execution bounds
semantic parcelhub.imports
domain one.components@1
operation ImportOrders@1
input OrderArchive
output ImportReport
effect write(orders)
resource temporary_storage max gib(2)
resource memory max mib(256)
execution children max 8
execution queue max 64
deadline 10m
cancellation cooperative
Acquisition requirements do not grant filesystem access or select a storage provider.
2. Borrow resources through the operation context
pub async fn import(ctx: ImportOrders, archive: OrderArchive) -> Result<ImportReport> {
let scratch = ctx.resources().temporary_storage().acquire().await?;
let mut scope = ctx.execution().scope();
for batch in archive.bounded_batches(500)? {
scope.spawn_bounded(async {
validate_and_write(batch).await
}).await?;
}
let results = scope.join_all().await?;
scratch.release().await?;
ImportReport::from_results(results)
}
Children cannot outlive the scope. Queue saturation applies backpressure instead of creating unbounded tasks.
3. Handle cancellation explicitly
match scope.join_all().await {
Ok(results) => commit(results).await,
Err(ExecutionFault::Cancelled) => {
scope.cancel_children().await;
rollback_uncommitted_batches().await
}
Err(fault) => reconcile_completed_batches(fault).await,
}
Dropping a handle is not proof that an external effect stopped. Completed writes retain receipts and uncertain writes are reconciled.
4. Test finite capacity
one test --root OrderImport --scenario queue-saturation
one test --root OrderImport --scenario deadline-during-write
one test --root OrderImport --scenario resource-release-after-cancelSee execution and resources and lifecycle.