1use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
2use std::marker::PhantomData;
3use std::mem::ManuallyDrop;
4use std::ops::{Deref, DerefMut};
5use std::sync::Arc;
6use std::time::Instant;
7
8use cudarc::driver::{sys::CUevent_flags, CudaEvent};
9use xlog_core::{resolve_bool, symbol, RelId, Result, ScalarType, Schema, XlogError};
10use xlog_cuda::cuda_graph::{
11 reserve_uncaptured_stream, retire_after_stream_captures, CapturedCudaGraph,
12 ConditionalCudaGraphSequenceBuilder, CudaConditionalGraphUnavailable, CudaGraphNodeKind,
13};
14use xlog_cuda::device_runtime::{BlockId, ResidentCompletionEvent, XlogDeviceRuntime};
15use xlog_cuda::launch::LaunchRecorder;
16use xlog_cuda::memory::GpuMemoryReservation;
17use xlog_cuda::provider::resident_filter_project::{
18 resident_filter_scratch_device_bytes, ResidentFilterScratch,
19};
20use xlog_cuda::provider::resident_relational::{
21 resident_control_device_bytes, resident_device_trace_bytes,
22 resident_join_workspace_device_bytes, resident_packed_receipt_with_schema_winners_device_bytes,
23 resident_relation_device_bytes, resident_schema_winners_device_bytes,
24 resident_set_workspace_device_bytes, ResidentConvergenceControl, ResidentDeviceTrace,
25 ResidentJoinKind, ResidentJoinWorkspace, ResidentPackedReceipt, ResidentPinnedReceipt,
26 ResidentRelation, ResidentResourceCode, ResidentSchemaWinners, ResidentSetWorkspace,
27 ResidentTerminalCode, ResidentTerminalStatus,
28};
29use xlog_cuda::provider::resident_schedule::{
30 resident_schedule_metadata_device_bytes, ResidentExecutionDomain,
31 ResidentFilterComparisonDescriptor, ResidentOpDescriptor, ResidentProjectExpressionDescriptor,
32 ResidentRegionDescriptor, ResidentScheduleDeviceProgram, ResidentScheduleExternalBindings,
33 ResidentScheduleOpKind, ResidentScheduleSlotBinding, ResidentWaveDescriptor,
34 RESIDENT_SCHEDULE_OP_MARK_NOVELTY, RESIDENT_SCHEDULE_OP_MARK_SCHEMA_WINNER,
35 RESIDENT_SCHEDULE_REGION_FINALIZE, RESIDENT_SCHEDULE_REGION_INITIALIZE,
36 RESIDENT_SCHEDULE_REGION_RECURSIVE, RESIDENT_SCHEDULE_REGION_SCC_BEGIN,
37};
38use xlog_cuda::{
39 CompareOp as CudaCompareOp, CudaBuffer, CudaColumn, CudaKernelProvider, CudaStream,
40};
41use xlog_ir::{
42 CompareOp as RirCompareOp, CompiledRule, ConstValue, ExecutionPlan, Expr, JoinType,
43 ProjectExpr, RirNode,
44};
45
46use super::Executor;
47use crate::resident_graph::{
48 ResidentGraphCertifiedPlan, ResidentGraphDeclineReason, ResidentGraphDeviceStatus,
49 ResidentGraphExecutionError, ResidentGraphPrepareDiagnosticSnapshot,
50 ResidentGraphPrepareOptions, ResidentGraphRouteCertificate,
51};
52
53const MAX_RESIDENT_CAPACITY: u32 = 65_536;
54const RESIDENT_DYNAMIC_SCHEMA_ID: u32 = u32::MAX;
55
56fn resident_schemas_type_compatible(left: &Schema, right: &Schema) -> bool {
57 left.arity() == right.arity()
58 && (0..left.arity()).all(|column| left.column_type(column) == right.column_type(column))
59}
60
61fn resident_intern_schema(candidates: &mut Vec<Schema>, schema: Schema) -> Result<u32> {
62 let index = candidates
63 .iter()
64 .position(|candidate| candidate == &schema)
65 .unwrap_or_else(|| {
66 candidates.push(schema);
67 candidates.len() - 1
68 });
69 u32::try_from(index)
70 .map_err(|_| XlogError::Execution("resident schema candidate count exceeds u32".into()))
71}
72#[derive(Debug, Clone)]
73pub(super) struct ResidentWorkspaceAdmission {
74 pub(super) relation_capacity: u32,
75 pub(super) head_schemas: BTreeMap<String, Schema>,
76 head_schema_choices: BTreeMap<String, Vec<Schema>>,
77 rule_schema_ids: Vec<Vec<Option<u32>>>,
78 head_schema_selections: BTreeMap<String, ResidentHeadSchemaSelection>,
79}
80
81#[derive(Debug, Clone)]
82struct ResidentHeadSchemaSelection {
83 source_head: String,
84 output_schemas_by_source_winner: Vec<Schema>,
85}
86
87#[derive(Debug, Clone)]
88enum ResidentSchemaVariants {
89 Fixed(Schema),
90 Dynamic {
91 source_head: String,
92 schemas: Vec<Schema>,
93 },
94}
95
96fn resident_schema_variants_from_source(
97 source_head: String,
98 schemas: Vec<Schema>,
99) -> std::result::Result<ResidentSchemaVariants, ResidentGraphDeclineReason> {
100 let Some(first) = schemas.first() else {
101 return Err(resident_workspace_decline(format!(
102 "resident schema source {source_head} has no admitted candidate"
103 )));
104 };
105 if schemas.iter().all(|schema| schema == first) {
106 Ok(ResidentSchemaVariants::Fixed(first.clone()))
107 } else {
108 Ok(ResidentSchemaVariants::Dynamic {
109 source_head,
110 schemas,
111 })
112 }
113}
114
115fn resident_register_schema_selection(
116 target_head: &str,
117 selection: ResidentHeadSchemaSelection,
118 head_schema_choices: &BTreeMap<String, Vec<Schema>>,
119 head_schema_selections: &mut BTreeMap<String, ResidentHeadSchemaSelection>,
120) -> std::result::Result<(), ResidentGraphDeclineReason> {
121 if selection.source_head == target_head {
122 return Err(resident_workspace_decline(format!(
123 "resident schema lineage for {target_head} contains a cycle"
124 )));
125 }
126 let source_candidates = head_schema_choices
127 .get(&selection.source_head)
128 .filter(|candidates| !candidates.is_empty())
129 .ok_or_else(|| {
130 resident_workspace_decline(format!(
131 "resident schema source {} for {target_head} has no admitted candidate",
132 selection.source_head
133 ))
134 })?;
135 if source_candidates.len() != selection.output_schemas_by_source_winner.len() {
136 return Err(resident_workspace_decline(format!(
137 "resident schema source {} for {target_head} has {} candidates but {} mappings",
138 selection.source_head,
139 source_candidates.len(),
140 selection.output_schemas_by_source_winner.len()
141 )));
142 }
143
144 let mut source = selection.source_head.as_str();
145 let mut seen = HashSet::new();
146 while let Some(parent) = head_schema_selections.get(source) {
147 if source == target_head || !seen.insert(source) {
148 return Err(resident_workspace_decline(format!(
149 "resident schema lineage for {target_head} contains a cycle"
150 )));
151 }
152 source = parent.source_head.as_str();
153 }
154 if source == target_head {
155 return Err(resident_workspace_decline(format!(
156 "resident schema lineage for {target_head} contains a cycle"
157 )));
158 }
159
160 if let Some(existing) = head_schema_selections.get(target_head) {
161 if existing.source_head != selection.source_head
162 || existing.output_schemas_by_source_winner != selection.output_schemas_by_source_winner
163 {
164 return Err(resident_workspace_decline(format!(
165 "resident head {target_head} has multiple schema sources or mappings"
166 )));
167 }
168 return Ok(());
169 }
170 head_schema_selections.insert(target_head.to_string(), selection);
171 Ok(())
172}
173
174#[derive(Debug, Clone)]
175enum ResidentOutputSchemaSelection {
176 OwnWinner,
177 SourceWinner {
178 source_output: usize,
179 schemas: Vec<Schema>,
180 },
181}
182
183#[derive(Debug, Clone)]
184struct ResidentOutputSchemaPlan {
185 candidates: Vec<Schema>,
186 selection: ResidentOutputSchemaSelection,
187}
188
189fn resident_resolve_output_schemas(
190 plans: &[ResidentOutputSchemaPlan],
191 winner_ids: &[u32],
192) -> Result<Vec<Schema>> {
193 if plans.len() != winner_ids.len() {
194 return Err(XlogError::Execution(format!(
195 "resident schema plan count {} does not match winner count {}",
196 plans.len(),
197 winner_ids.len()
198 )));
199 }
200 (0..plans.len())
201 .map(|output| {
202 resident_resolve_output_schema(plans, winner_ids, output, &mut HashSet::new())
203 })
204 .collect()
205}
206
207fn resident_resolve_output_schema(
208 plans: &[ResidentOutputSchemaPlan],
209 winner_ids: &[u32],
210 output: usize,
211 resolving: &mut HashSet<usize>,
212) -> Result<Schema> {
213 if !resolving.insert(output) {
214 return Err(XlogError::Execution(
215 "resident schema source lineage contains a cycle".into(),
216 ));
217 }
218 let result = (|| {
219 let plan = plans.get(output).ok_or_else(|| {
220 XlogError::Execution(format!("resident schema output {output} is missing"))
221 })?;
222 let winner = *winner_ids.get(output).ok_or_else(|| {
223 XlogError::Execution(format!("resident schema winner {output} is missing"))
224 })?;
225 if winner != RESIDENT_DYNAMIC_SCHEMA_ID {
226 return plan
227 .candidates
228 .get(winner as usize)
229 .cloned()
230 .ok_or_else(|| {
231 XlogError::Execution(format!(
232 "resident schema winner {winner} exceeds {} candidates",
233 plan.candidates.len()
234 ))
235 });
236 }
237 let ResidentOutputSchemaSelection::SourceWinner {
238 source_output,
239 schemas,
240 } = &plan.selection
241 else {
242 return Err(XlogError::Execution(
243 "resident dynamic schema winner has no source lineage".into(),
244 ));
245 };
246 let source_schema =
247 resident_resolve_output_schema(plans, winner_ids, *source_output, resolving)?;
248 let source_plan = plans
249 .get(*source_output)
250 .ok_or_else(|| XlogError::Execution("resident schema source plan is missing".into()))?;
251 let source_winner = source_plan
252 .candidates
253 .iter()
254 .position(|candidate| candidate == &source_schema)
255 .ok_or_else(|| {
256 XlogError::Execution(
257 "resident resolved source schema has no candidate index".into(),
258 )
259 })?;
260 schemas.get(source_winner).cloned().ok_or_else(|| {
261 XlogError::Execution(format!(
262 "resident schema source winner {source_winner} exceeds {} mappings",
263 schemas.len()
264 ))
265 })
266 })();
267 resolving.remove(&output);
268 result
269}
270
271struct ResidentRunOwners {
272 provider: Arc<CudaKernelProvider>,
273 runtime: Arc<XlogDeviceRuntime>,
274 stream: Arc<CudaStream>,
275 graph: CapturedCudaGraph,
278 execution_domain: ResidentExecutionDomain,
279 _schedule_program: ResidentScheduleDeviceProgram,
280 recorder: LaunchRecorder,
281 relations: Vec<Option<ResidentRelation>>,
282 source_binding_names: Vec<String>,
283 output_indices: Vec<(String, usize)>,
284 _filter_scratch: Option<ResidentFilterScratch>,
285 _set_workspace: ResidentSetWorkspace,
286 _join_workspace: ResidentJoinWorkspace,
287 _control: ResidentConvergenceControl,
288 _device_trace: ResidentDeviceTrace,
289 _schema_winners: ResidentSchemaWinners,
290 receipt: ResidentPackedReceipt,
291 pinned_receipt: ResidentPinnedReceipt,
292 source_epoch: u64,
293 relation_registration: Vec<(RelId, String)>,
294 transaction_identity: Arc<()>,
295 output_schema_plans: Vec<ResidentOutputSchemaPlan>,
296}
297
298struct PendingResidentOwners<T: Send + 'static> {
302 value: Option<T>,
303 wait: fn(&mut T) -> bool,
304}
305
306impl<T: Send + 'static> PendingResidentOwners<T> {
307 fn new(owners: T, wait: fn(&mut T) -> bool) -> Self {
308 Self {
309 value: Some(owners),
310 wait,
311 }
312 }
313
314 fn into_inner(mut self) -> T {
316 self.value
317 .take()
318 .expect("resident execution owners missing")
319 }
320}
321
322impl<T: Send + 'static> PendingResidentOwners<T> {
323 fn with_admission<A, R, E>(
324 mut self,
325 admit: impl FnOnce(&T) -> std::result::Result<A, E>,
326 operation: impl FnOnce(&mut T) -> std::result::Result<R, E>,
327 ) -> std::result::Result<(Self, R), E> {
328 let admission = admit(&self)?;
329 let result = operation(&mut self)?;
332 drop(admission);
333 Ok((self, result))
334 }
335}
336
337impl<T: Send + 'static> Deref for PendingResidentOwners<T> {
338 type Target = T;
339 fn deref(&self) -> &T {
340 self.value
341 .as_ref()
342 .expect("resident execution owners missing")
343 }
344}
345
346impl<T: Send + 'static> DerefMut for PendingResidentOwners<T> {
347 fn deref_mut(&mut self) -> &mut T {
348 self.value
349 .as_mut()
350 .expect("resident execution owners missing")
351 }
352}
353
354impl<T: Send + 'static> Drop for PendingResidentOwners<T> {
355 fn drop(&mut self) {
356 let Some(owners) = self.value.take() else {
357 return;
358 };
359 let wait = self.wait;
360 retire_after_stream_captures(move || {
361 let mut owners = ManuallyDrop::new(owners);
364 if !wait(&mut owners) {
365 eprintln!("CUDA resident completion remains unknown; retaining execution owners");
366 return;
367 }
368 unsafe { ManuallyDrop::drop(&mut owners) };
371 });
372 }
373}
374
375#[derive(Clone, Debug, PartialEq, Eq)]
376struct ResidentSourceSetSnapshot {
377 name: String,
378 version: u64,
379 schema: Schema,
380 row_capacity: u64,
381 column_blocks: Vec<Option<BlockId>>,
382 row_count_block: BlockId,
383}
384
385#[derive(Clone)]
386enum ResidentBufferRef {
387 Source(String),
388 Private(usize),
389}
390
391enum ResidentRecordedOp {
392 Unit {
393 output: usize,
394 op_id: u32,
395 },
396 Scan {
397 relation: ResidentBufferRef,
398 op_id: u32,
399 },
400 TraceDelta {
401 scan_delta: u32,
402 filter_delta: u32,
403 semantic_guard: Option<ResidentBufferRef>,
404 },
405 #[cfg(test)]
406 Clear {
407 output: usize,
408 },
409 Filter {
410 input: ResidentBufferRef,
411 output: usize,
412 workspace: usize,
413 op_id: u32,
414 },
415 Project {
416 input: ResidentBufferRef,
417 output: usize,
418 workspace: usize,
419 op_id: u32,
420 },
421 Union {
422 left: ResidentBufferRef,
423 right: ResidentBufferRef,
424 output: usize,
425 op_id: u32,
426 },
427 Diff {
428 left: ResidentBufferRef,
429 right: ResidentBufferRef,
430 output: usize,
431 op_id: u32,
432 },
433 Join {
434 kind: ResidentJoinKind,
435 left: ResidentBufferRef,
436 left_key: usize,
437 right: ResidentBufferRef,
438 right_key: usize,
439 output: usize,
440 op_id: u32,
441 },
442 ChangedReset,
443 ChangedMark {
444 relation: usize,
445 },
446 TestStatus(ResidentTerminalStatus),
447 SchemaWinnerMark {
448 contribution: ResidentBufferRef,
449 head_index: u32,
450 schema_id: u32,
451 },
452}
453
454#[cfg(test)]
455fn resident_record_unit_leaf(
456 output: usize,
457 op_id: u32,
458 ops: &mut Vec<ResidentRecordedOp>,
459 push_physical: impl FnOnce(&mut Vec<ResidentRecordedOp>, ResidentRecordedOp, u32),
460) -> ResidentBufferRef {
461 push_physical(ops, ResidentRecordedOp::Unit { output, op_id }, op_id);
462 ResidentBufferRef::Private(output)
463}
464
465fn resident_new_phase_unit(
466 relations: &mut Vec<ResidentLogicalRelation>,
467 op_id: u32,
468) -> Result<(ResidentBufferRef, ResidentRecordedOp)> {
469 let output = relations.len();
470 relations.push(ResidentLogicalRelation {
471 schema: Schema::new(Vec::new()),
472 initial_count: 0,
473 permanent: false,
474 });
475 Ok((
476 ResidentBufferRef::Private(output),
477 ResidentRecordedOp::Unit { output, op_id },
478 ))
479}
480
481fn resident_record_scan_leaf(
482 relation: ResidentBufferRef,
483 op_id: u32,
484 semantic_guard: Option<ResidentBufferRef>,
485 ops: &mut Vec<ResidentRecordedOp>,
486 push_physical: impl FnOnce(&mut Vec<ResidentRecordedOp>, ResidentRecordedOp, u32),
487) -> ResidentBufferRef {
488 push_physical(
489 ops,
490 ResidentRecordedOp::Scan {
491 relation: relation.clone(),
492 op_id,
493 },
494 op_id,
495 );
496 ops.push(ResidentRecordedOp::TraceDelta {
497 scan_delta: 1,
498 filter_delta: 0,
499 semantic_guard,
500 });
501 relation
502}
503
504fn resident_semantic_trace_guard(
505 override_scan: Option<(RelId, usize, usize)>,
506) -> Option<ResidentBufferRef> {
507 override_scan.map(|(_, _, delta)| ResidentBufferRef::Private(delta))
508}
509
510fn resident_record_schema_winner_mark(
511 ops: &mut Vec<ResidentRecordedOp>,
512 contribution: ResidentBufferRef,
513 head_index: u32,
514 schema_id: u32,
515) {
516 ops.push(ResidentRecordedOp::SchemaWinnerMark {
517 contribution,
518 head_index,
519 schema_id,
520 });
521}
522
523#[derive(Debug, Clone, Copy, PartialEq, Eq)]
524enum ResidentUnionFoldMode {
525 SelfUnion,
526 LeftAssociated,
527}
528
529fn resident_union_fold_mode(input_count: usize) -> Result<ResidentUnionFoldMode> {
530 match input_count {
531 0 => Err(XlogError::Execution("resident union has no inputs".into())),
532 1 => Ok(ResidentUnionFoldMode::SelfUnion),
533 _ => Ok(ResidentUnionFoldMode::LeftAssociated),
534 }
535}
536
537enum ResidentPhaseMergeStep<T> {
538 Deduplicate(T),
539 Union(T, T),
540}
541
542fn resident_phase_merge<T, E>(
543 current: Option<T>,
544 contribution: T,
545 mut execute: impl FnMut(ResidentPhaseMergeStep<T>) -> std::result::Result<T, E>,
546) -> std::result::Result<T, E> {
547 let contribution = execute(ResidentPhaseMergeStep::Deduplicate(contribution))?;
548 match current {
549 Some(current) => execute(ResidentPhaseMergeStep::Union(current, contribution)),
550 None => Ok(contribution),
551 }
552}
553
554enum ResidentCapturePhase {
555 Segment {
556 ops: Vec<ResidentRecordedOp>,
557 scc_begin: Option<(u32, u32)>,
558 },
559 ConditionalWhile {
560 ops: Vec<ResidentRecordedOp>,
561 iteration_limit: u32,
562 convergence_op_id: u32,
563 },
564}
565
566struct ResidentCompactLogicalRegion {
567 ops: Vec<ResidentRecordedOp>,
568 iteration_limit: u32,
569 op_id: u32,
570 flags: u32,
571}
572
573struct ResidentCompactSchedulePlan {
574 source_slots: BTreeMap<String, u32>,
575 ops: Vec<ResidentOpDescriptor>,
576 waves: Vec<ResidentWaveDescriptor>,
577 regions: Vec<ResidentRegionDescriptor>,
578 generation_bases: Vec<u32>,
579 filter_comparisons: Vec<ResidentFilterComparisonDescriptor>,
580 project_expressions: Vec<ResidentProjectExpressionDescriptor>,
581}
582
583#[derive(Debug, Clone, Copy, PartialEq, Eq)]
584enum ResidentCaptureParentKind {
585 Kernel,
586 Conditional,
587}
588
589struct ResidentCompactTopology {
590 parent_kinds: Vec<ResidentCaptureParentKind>,
591 conditional_body_kernel_counts: Vec<usize>,
592 hierarchical_node_count: usize,
593}
594
595trait ResidentRegionFlags {
596 fn region_flags(&self) -> u32;
597}
598
599impl ResidentRegionFlags for ResidentCompactLogicalRegion {
600 fn region_flags(&self) -> u32 {
601 self.flags
602 }
603}
604
605impl ResidentRegionFlags for ResidentRegionDescriptor {
606 fn region_flags(&self) -> u32 {
607 self.flags
608 }
609}
610
611fn resident_compact_topology<R: ResidentRegionFlags>(
612 regions: &[R],
613) -> Result<ResidentCompactTopology> {
614 let parent_kinds = regions
615 .iter()
616 .map(|region| {
617 if region.region_flags() == RESIDENT_SCHEDULE_REGION_RECURSIVE {
618 ResidentCaptureParentKind::Conditional
619 } else {
620 ResidentCaptureParentKind::Kernel
621 }
622 })
623 .collect::<Vec<_>>();
624 let conditional_count = parent_kinds
625 .iter()
626 .filter(|kind| **kind == ResidentCaptureParentKind::Conditional)
627 .count();
628 let hierarchical_node_count = parent_kinds
629 .len()
630 .checked_add(conditional_count)
631 .ok_or_else(|| XlogError::Execution("resident topology node count overflow".into()))?;
632 Ok(ResidentCompactTopology {
633 parent_kinds,
634 conditional_body_kernel_counts: vec![1; conditional_count],
635 hierarchical_node_count,
636 })
637}
638
639fn resident_validate_parent_graph_kinds(
640 actual: &[CudaGraphNodeKind],
641 expected: &[ResidentCaptureParentKind],
642) -> Result<()> {
643 if actual.len() != expected.len()
644 || actual.iter().zip(expected).any(|(actual, expected)| {
645 !matches!(
646 (actual, expected),
647 (CudaGraphNodeKind::Kernel, ResidentCaptureParentKind::Kernel)
648 | (
649 CudaGraphNodeKind::Conditional,
650 ResidentCaptureParentKind::Conditional
651 )
652 )
653 })
654 {
655 return Err(XlogError::Execution(
656 "resident parent graph topology differs from the compact schedule".into(),
657 ));
658 }
659 Ok(())
660}
661
662fn resident_validate_conditional_body_node_kinds(
663 actual: &[Vec<CudaGraphNodeKind>],
664 expected_conditional_count: usize,
665) -> Result<Vec<usize>> {
666 if actual.len() != expected_conditional_count {
667 return Err(XlogError::Execution(format!(
668 "resident conditional body inventory has {} bodies, expected {expected_conditional_count}",
669 actual.len()
670 )));
671 }
672 let mut kernel_counts = Vec::with_capacity(actual.len());
673 for (index, kinds) in actual.iter().enumerate() {
674 if kinds.as_slice() != [CudaGraphNodeKind::Kernel] {
675 return Err(XlogError::Execution(format!(
676 "resident conditional body {index} must contain exactly one kernel node, found {kinds:?}"
677 )));
678 }
679 kernel_counts.push(1);
680 }
681 Ok(kernel_counts)
682}
683
684#[derive(Default)]
685struct ResidentCompactDescriptorTables {
686 filter_comparisons: Vec<ResidentFilterComparisonDescriptor>,
687 filter_ranges: Vec<(u32, u32)>,
688 project_expressions: Vec<ResidentProjectExpressionDescriptor>,
689 project_ranges: Vec<(u32, u32)>,
690}
691
692#[expect(
693 clippy::too_many_arguments,
694 reason = "schedule sizing accounts for each independently bounded descriptor table before any device allocation"
695)]
696fn resident_compact_schedule_metadata_bytes(
697 slot_count: usize,
698 op_count: usize,
699 wave_count: usize,
700 region_count: usize,
701 generation_base_count: usize,
702 head_count: usize,
703 filter_comparison_count: usize,
704 project_expression_count: usize,
705) -> Result<u64> {
706 let generation_metadata_count =
707 generation_base_count
708 .checked_add(head_count)
709 .ok_or_else(|| {
710 XlogError::Execution("resident generation metadata count overflow".into())
711 })?;
712 resident_schedule_metadata_device_bytes(
713 slot_count,
714 op_count,
715 wave_count,
716 region_count,
717 generation_metadata_count,
718 filter_comparison_count,
719 project_expression_count,
720 )
721}
722
723fn resident_compact_schema_defaults(
724 ops: &[ResidentOpDescriptor],
725 head_count: usize,
726) -> Result<Vec<u32>> {
727 let mut defaults = vec![None; head_count];
728 for op in ops {
729 if op.flags & RESIDENT_SCHEDULE_OP_MARK_SCHEMA_WINNER == 0 {
730 continue;
731 }
732 let head = usize::try_from(op.schema_winner_head).map_err(|_| {
733 XlogError::Execution("resident schema winner head index overflow".into())
734 })?;
735 let default = defaults.get_mut(head).ok_or_else(|| {
736 XlogError::Execution("resident schema winner head is out of range".into())
737 })?;
738 if default.is_none() {
739 *default = Some(op.schema_winner_id);
740 }
741 }
742 defaults
743 .into_iter()
744 .enumerate()
745 .map(|(head, default)| {
746 default.ok_or_else(|| {
747 XlogError::Execution(format!(
748 "resident head {head} has no schema winner candidate"
749 ))
750 })
751 })
752 .collect()
753}
754
755fn resident_compact_allocation_bytes(
756 relation_bytes: u64,
757 filter_scratch_bytes: u64,
758 fixed_workspace_bytes: u64,
759 private_slot_count: usize,
760 head_count: usize,
761 plan: &ResidentCompactSchedulePlan,
762) -> Result<(u64, u64)> {
763 let slot_count = private_slot_count
764 .checked_add(plan.source_slots.len())
765 .ok_or_else(|| XlogError::Execution("resident compact slot count overflow".into()))?;
766 let metadata_bytes = resident_compact_schedule_metadata_bytes(
767 slot_count,
768 plan.ops.len(),
769 plan.waves.len(),
770 plan.regions.len(),
771 plan.generation_bases.len(),
772 head_count,
773 plan.filter_comparisons.len(),
774 plan.project_expressions.len(),
775 )?;
776 let required_bytes = relation_bytes
777 .checked_add(filter_scratch_bytes)
778 .and_then(|bytes| bytes.checked_add(fixed_workspace_bytes))
779 .and_then(|bytes| bytes.checked_add(metadata_bytes))
780 .ok_or_else(|| XlogError::Execution("resident compact manifest overflow".into()))?;
781 Ok((required_bytes, metadata_bytes))
782}
783
784fn resident_compact_preflight_device_bytes(
785 manifest: &ResidentAllocationManifest,
786 plan: &ResidentCompactSchedulePlan,
787) -> Result<(u64, u64, u64)> {
788 let filter_count = u64::try_from(plan.filter_comparisons.len().max(1)).map_err(|_| {
789 XlogError::Execution("resident compact filter descriptor count exceeds u64".into())
790 })?;
791 let project_count = u64::try_from(plan.project_expressions.len().max(1)).map_err(|_| {
792 XlogError::Execution("resident compact project descriptor count exceeds u64".into())
793 })?;
794 let filter_bytes = u64::try_from(std::mem::size_of::<ResidentFilterComparisonDescriptor>())
795 .ok()
796 .and_then(|size| size.checked_mul(filter_count))
797 .ok_or_else(|| {
798 XlogError::Execution("resident compact filter descriptor bytes overflow".into())
799 })?;
800 let project_bytes = u64::try_from(std::mem::size_of::<ResidentProjectExpressionDescriptor>())
801 .ok()
802 .and_then(|size| size.checked_mul(project_count))
803 .ok_or_else(|| {
804 XlogError::Execution("resident compact project descriptor bytes overflow".into())
805 })?;
806 let descriptor_bytes = filter_bytes
807 .checked_add(project_bytes)
808 .ok_or_else(|| XlogError::Execution("resident compact descriptor bytes overflow".into()))?;
809 let remaining_metadata_bytes = manifest
810 .schedule_metadata_bytes
811 .checked_sub(descriptor_bytes)
812 .ok_or_else(|| {
813 XlogError::Execution(
814 "resident compact descriptor bytes exceed schedule metadata".into(),
815 )
816 })?;
817 let fixed_bytes = manifest
818 .fixed_workspace_bytes
819 .checked_add(remaining_metadata_bytes)
820 .ok_or_else(|| XlogError::Execution("resident compact fixed bytes overflow".into()))?;
821 let reported_total = manifest
822 .relation_bytes
823 .checked_add(manifest.filter_scratch_bytes)
824 .and_then(|bytes| bytes.checked_add(filter_bytes))
825 .and_then(|bytes| bytes.checked_add(project_bytes))
826 .and_then(|bytes| bytes.checked_add(fixed_bytes))
827 .ok_or_else(|| XlogError::Execution("resident compact preflight bytes overflow".into()))?;
828 if reported_total != manifest.required_bytes {
829 return Err(XlogError::Execution(format!(
830 "resident compact preflight component mismatch: reported {reported_total}, required {}",
831 manifest.required_bytes
832 )));
833 }
834 Ok((filter_bytes, project_bytes, fixed_bytes))
835}
836
837fn resident_compact_tables(
838 filter_workspaces: &[ResidentFilterPlan],
839 project_workspaces: &[ResidentProjectPlan],
840) -> Result<ResidentCompactDescriptorTables> {
841 let mut tables = ResidentCompactDescriptorTables::default();
842 for workspace in filter_workspaces {
843 let offset = u32::try_from(tables.filter_comparisons.len()).map_err(|_| {
844 XlogError::Execution("resident filter comparison offset exceeds u32".into())
845 })?;
846 let count = u32::try_from(workspace.compact_comparisons.len()).map_err(|_| {
847 XlogError::Execution("resident filter comparison count exceeds u32".into())
848 })?;
849 tables
850 .filter_comparisons
851 .extend_from_slice(&workspace.compact_comparisons);
852 tables.filter_ranges.push((offset, count));
853 }
854 for workspace in project_workspaces {
855 let offset = u32::try_from(tables.project_expressions.len()).map_err(|_| {
856 XlogError::Execution("resident project expression offset exceeds u32".into())
857 })?;
858 let count = u32::try_from(workspace.compact_expressions.len()).map_err(|_| {
859 XlogError::Execution("resident project expression count exceeds u32".into())
860 })?;
861 tables
862 .project_expressions
863 .extend_from_slice(&workspace.compact_expressions);
864 tables.project_ranges.push((offset, count));
865 }
866 Ok(tables)
867}
868
869#[derive(Debug, Clone, Copy, PartialEq, Eq)]
870struct ResidentCompactSlotRef {
871 slot: u32,
872 generation: u32,
873}
874
875fn resident_compact_slot_ref(
876 reference: &ResidentBufferRef,
877 assignments: &[ResidentSlotAssignment],
878 source_slots: &BTreeMap<String, u32>,
879) -> Result<ResidentCompactSlotRef> {
880 match reference {
881 ResidentBufferRef::Private(logical) => {
882 let assignment = assignments.get(*logical).ok_or_else(|| {
883 XlogError::Execution(format!(
884 "resident logical relation {logical} has no compact slot assignment"
885 ))
886 })?;
887 Ok(ResidentCompactSlotRef {
888 slot: u32::try_from(assignment.slot).map_err(|_| {
889 XlogError::Execution("resident compact slot index exceeds u32".into())
890 })?,
891 generation: assignment.generation,
892 })
893 }
894 ResidentBufferRef::Source(name) => Ok(ResidentCompactSlotRef {
895 slot: *source_slots.get(name).ok_or_else(|| {
896 XlogError::Execution(format!(
897 "resident source {name} has no compact slot assignment"
898 ))
899 })?,
900 generation: 0,
901 }),
902 }
903}
904
905fn resident_lower_compact_regions<'a>(
906 logical_regions: Vec<ResidentCompactLogicalRegion>,
907 physical_slots: &[ResidentPhysicalSlotPlan],
908 assignments: &[ResidentSlotAssignment],
909 source_names: impl IntoIterator<Item = &'a str>,
910 tables: ResidentCompactDescriptorTables,
911) -> Result<ResidentCompactSchedulePlan> {
912 let source_slots = resident_source_slot_map(physical_slots.len(), source_names)?;
913 let slot_count = physical_slots
914 .len()
915 .checked_add(source_slots.len())
916 .ok_or_else(|| XlogError::Execution("resident compact slot count overflow".into()))?;
917 let slot_count_u32 = u32::try_from(slot_count)
918 .map_err(|_| XlogError::Execution("resident compact slot count exceeds u32".into()))?;
919 let mut ops = Vec::new();
920 let mut waves = Vec::new();
921 let mut regions = Vec::with_capacity(logical_regions.len());
922 let filter_total = u32::try_from(tables.filter_comparisons.len())
923 .map_err(|_| XlogError::Execution("resident filter comparison count exceeds u32".into()))?;
924 let project_total = u32::try_from(tables.project_expressions.len()).map_err(|_| {
925 XlogError::Execution("resident project expression count exceeds u32".into())
926 })?;
927 let mut filter_cursor = 0_u32;
928 let mut project_cursor = 0_u32;
929 let mut generation_bases = Vec::with_capacity(
930 logical_regions
931 .len()
932 .checked_mul(slot_count)
933 .ok_or_else(|| {
934 XlogError::Execution("resident generation baseline count overflow".into())
935 })?,
936 );
937
938 for logical_region in logical_regions {
939 let region_flags = logical_region.flags;
940 let first_wave = u32::try_from(waves.len())
941 .map_err(|_| XlogError::Execution("resident wave count exceeds u32".into()))?;
942 let first_op = u32::try_from(ops.len())
943 .map_err(|_| XlogError::Execution("resident compact op count exceeds u32".into()))?;
944 let generation_offset = u32::try_from(generation_bases.len()).map_err(|_| {
945 XlogError::Execution("resident generation baseline offset exceeds u32".into())
946 })?;
947 let mut first_generations = vec![None; slot_count];
948 let mut last_relation_descriptor = None::<(usize, ResidentCompactSlotRef)>;
949
950 for (logical_op_index, logical_op) in logical_region.ops.into_iter().enumerate() {
951 let mut emit = |descriptor: ResidentOpDescriptor,
952 output: ResidentCompactSlotRef,
953 references: Vec<ResidentCompactSlotRef>|
954 -> Result<()> {
955 for reference in &references {
956 let generation = first_generations
957 .get_mut(reference.slot as usize)
958 .ok_or_else(|| {
959 XlogError::Execution(
960 "resident compact operation slot is out of range".into(),
961 )
962 })?;
963 if generation.is_none() {
964 *generation = Some(reference.generation);
965 }
966 }
967 let op_index = ops.len();
968 ops.push(descriptor);
969 last_relation_descriptor = Some((op_index, output));
970 Ok(())
971 };
972
973 match logical_op {
974 ResidentRecordedOp::Unit { output, op_id } => {
975 let output = resident_compact_slot_ref(
976 &ResidentBufferRef::Private(output),
977 assignments,
978 &source_slots,
979 )?;
980 emit(
981 ResidentOpDescriptor::unit(op_id, output.slot, output.generation),
982 output,
983 vec![output],
984 )?;
985 }
986 ResidentRecordedOp::Scan { relation, op_id } => {
987 let source = resident_compact_slot_ref(&relation, assignments, &source_slots)?;
988 emit(
989 ResidentOpDescriptor::scan(op_id, source.slot, source.generation),
990 source,
991 vec![source],
992 )?;
993 }
994 op @ (ResidentRecordedOp::Filter { .. } | ResidentRecordedOp::Project { .. }) => {
995 let (is_filter, input, output, workspace, op_id) = match op {
996 ResidentRecordedOp::Filter {
997 input,
998 output,
999 workspace,
1000 op_id,
1001 } => (true, input, output, workspace, op_id),
1002 ResidentRecordedOp::Project {
1003 input,
1004 output,
1005 workspace,
1006 op_id,
1007 } => (false, input, output, workspace, op_id),
1008 _ => unreachable!("compact filter/project arm"),
1009 };
1010 let input = resident_compact_slot_ref(&input, assignments, &source_slots)?;
1011 let output = resident_compact_slot_ref(
1012 &ResidentBufferRef::Private(output),
1013 assignments,
1014 &source_slots,
1015 )?;
1016 let (aux_offset, aux_count) = if is_filter {
1017 *tables.filter_ranges.get(workspace).ok_or_else(|| {
1018 XlogError::Execution(
1019 "resident compact filter workspace is out of range".into(),
1020 )
1021 })?
1022 } else {
1023 *tables.project_ranges.get(workspace).ok_or_else(|| {
1024 XlogError::Execution(
1025 "resident compact project workspace is out of range".into(),
1026 )
1027 })?
1028 };
1029 let (cursor, total, table_name) = if is_filter {
1030 (&mut filter_cursor, filter_total, "filter")
1031 } else {
1032 (&mut project_cursor, project_total, "project")
1033 };
1034 if aux_offset != *cursor || aux_offset > total || aux_count > total - aux_offset
1035 {
1036 return Err(XlogError::Execution(format!(
1037 "resident compact {table_name} descriptor range is not contiguous"
1038 )));
1039 }
1040 *cursor = aux_offset + aux_count;
1041 emit(
1042 ResidentOpDescriptor {
1043 kind: if is_filter {
1044 ResidentScheduleOpKind::Filter
1045 } else {
1046 ResidentScheduleOpKind::Project
1047 },
1048 op_id,
1049 out: output.slot,
1050 in0: input.slot,
1051 in0_generation: input.generation,
1052 out_generation: output.generation,
1053 aux_offset,
1054 aux_count,
1055 ..Default::default()
1056 },
1057 output,
1058 vec![input, output],
1059 )?;
1060 }
1061 op @ (ResidentRecordedOp::Union { .. } | ResidentRecordedOp::Diff { .. }) => {
1062 let (is_union, left, right, output, op_id) = match op {
1063 ResidentRecordedOp::Union {
1064 left,
1065 right,
1066 output,
1067 op_id,
1068 } => (true, left, right, output, op_id),
1069 ResidentRecordedOp::Diff {
1070 left,
1071 right,
1072 output,
1073 op_id,
1074 } => (false, left, right, output, op_id),
1075 _ => unreachable!("compact set arm"),
1076 };
1077 let left = resident_compact_slot_ref(&left, assignments, &source_slots)?;
1078 let right = resident_compact_slot_ref(&right, assignments, &source_slots)?;
1079 let output = resident_compact_slot_ref(
1080 &ResidentBufferRef::Private(output),
1081 assignments,
1082 &source_slots,
1083 )?;
1084 emit(
1085 ResidentOpDescriptor {
1086 kind: if is_union {
1087 ResidentScheduleOpKind::Union
1088 } else {
1089 ResidentScheduleOpKind::Diff
1090 },
1091 op_id,
1092 out: output.slot,
1093 in0: left.slot,
1094 in1: right.slot,
1095 in0_generation: left.generation,
1096 in1_generation: right.generation,
1097 out_generation: output.generation,
1098 ..Default::default()
1099 },
1100 output,
1101 vec![left, right, output],
1102 )?;
1103 }
1104 ResidentRecordedOp::Join {
1105 kind,
1106 left,
1107 left_key,
1108 right,
1109 right_key,
1110 output,
1111 op_id,
1112 } => {
1113 let left = resident_compact_slot_ref(&left, assignments, &source_slots)?;
1114 let right = resident_compact_slot_ref(&right, assignments, &source_slots)?;
1115 let output = resident_compact_slot_ref(
1116 &ResidentBufferRef::Private(output),
1117 assignments,
1118 &source_slots,
1119 )?;
1120 emit(
1121 ResidentOpDescriptor {
1122 kind: match kind {
1123 ResidentJoinKind::Inner => ResidentScheduleOpKind::JoinInner,
1124 ResidentJoinKind::Semi => ResidentScheduleOpKind::JoinSemi,
1125 },
1126 op_id,
1127 out: output.slot,
1128 in0: left.slot,
1129 in1: right.slot,
1130 in0_generation: left.generation,
1131 in1_generation: right.generation,
1132 out_generation: output.generation,
1133 left_key: u32::try_from(left_key).map_err(|_| {
1134 XlogError::Execution(
1135 "resident compact left join key exceeds u32".into(),
1136 )
1137 })?,
1138 right_key: u32::try_from(right_key).map_err(|_| {
1139 XlogError::Execution(
1140 "resident compact right join key exceeds u32".into(),
1141 )
1142 })?,
1143 ..Default::default()
1144 },
1145 output,
1146 vec![left, right, output],
1147 )?;
1148 }
1149 ResidentRecordedOp::TraceDelta {
1150 scan_delta,
1151 filter_delta,
1152 semantic_guard,
1153 } => {
1154 let semantic_guard = semantic_guard
1155 .as_ref()
1156 .map(|reference| {
1157 resident_compact_slot_ref(reference, assignments, &source_slots)
1158 })
1159 .transpose()?;
1160 if let Some(reference) = semantic_guard {
1161 let generation = first_generations
1162 .get_mut(reference.slot as usize)
1163 .ok_or_else(|| {
1164 XlogError::Execution(
1165 "resident compact trace guard slot is out of range".into(),
1166 )
1167 })?;
1168 if generation.is_none() {
1169 *generation = Some(reference.generation);
1170 }
1171 }
1172 ops.push(ResidentOpDescriptor::trace_delta(
1173 scan_delta,
1174 filter_delta,
1175 semantic_guard.map(|reference| (reference.slot, reference.generation)),
1176 ));
1177 }
1178 ResidentRecordedOp::TestStatus(status) => {
1179 ops.push(ResidentOpDescriptor::test_status(status)?);
1180 }
1181 ResidentRecordedOp::SchemaWinnerMark {
1182 contribution,
1183 head_index,
1184 schema_id,
1185 } => {
1186 let contribution =
1187 resident_compact_slot_ref(&contribution, assignments, &source_slots)?;
1188 let (index, output) = last_relation_descriptor.as_ref().ok_or_else(|| {
1189 XlogError::Execution(
1190 "resident schema marker has no contribution operation".into(),
1191 )
1192 })?;
1193 if *output != contribution {
1194 return Err(XlogError::Execution(
1195 "resident schema marker does not match its contribution operation"
1196 .into(),
1197 ));
1198 }
1199 ops[*index] = ops[*index].with_schema_winner(head_index, schema_id);
1200 }
1201 ResidentRecordedOp::ChangedMark { relation } => {
1202 let relation = resident_compact_slot_ref(
1203 &ResidentBufferRef::Private(relation),
1204 assignments,
1205 &source_slots,
1206 )?;
1207 let (index, output) = last_relation_descriptor.as_ref().ok_or_else(|| {
1208 XlogError::Execution(
1209 "resident novelty marker has no completed delta copy".into(),
1210 )
1211 })?;
1212 if *output != relation || ops[*index].kind != ResidentScheduleOpKind::Project {
1213 return Err(XlogError::Execution(
1214 "resident novelty marker does not follow its completed delta copy"
1215 .into(),
1216 ));
1217 }
1218 ops[*index].flags |= RESIDENT_SCHEDULE_OP_MARK_NOVELTY;
1219 }
1220 ResidentRecordedOp::ChangedReset => {
1221 if region_flags != RESIDENT_SCHEDULE_REGION_RECURSIVE || logical_op_index != 0 {
1222 return Err(XlogError::Execution(
1223 "resident changed reset is not the recursive region entry".into(),
1224 ));
1225 }
1226 }
1227 #[cfg(test)]
1228 ResidentRecordedOp::Clear { .. } => {
1229 return Err(XlogError::Execution(
1230 "resident compact SSA still contains a Clear operation".into(),
1231 ));
1232 }
1233 }
1234 }
1235
1236 let last_op = u32::try_from(ops.len())
1237 .map_err(|_| XlogError::Execution("resident compact op count exceeds u32".into()))?;
1238 if last_op != first_op {
1239 waves.push(ResidentWaveDescriptor {
1241 first_op,
1242 op_count: last_op - first_op,
1243 flags: 0,
1244 reserved: 0,
1245 });
1246 }
1247
1248 for (slot, generation) in first_generations.into_iter().enumerate() {
1249 let baseline = if slot < physical_slots.len() && physical_slots[slot].permanent {
1250 0
1251 } else {
1252 generation.unwrap_or(0)
1253 };
1254 generation_bases.push(baseline);
1255 }
1256 let wave_count = u32::try_from(waves.len())
1257 .map_err(|_| XlogError::Execution("resident wave count exceeds u32".into()))?
1258 .checked_sub(first_wave)
1259 .ok_or_else(|| XlogError::Execution("resident wave range underflow".into()))?;
1260 regions.push(ResidentRegionDescriptor {
1261 first_wave,
1262 wave_count,
1263 iteration_limit: logical_region.iteration_limit,
1264 op_id: logical_region.op_id,
1265 flags: region_flags,
1266 first_slot: 0,
1267 slot_count: slot_count_u32,
1268 generation_offset,
1269 });
1270 }
1271
1272 if filter_cursor != filter_total || project_cursor != project_total {
1273 return Err(XlogError::Execution(
1274 "resident compact descriptor tables are not exactly covered".into(),
1275 ));
1276 }
1277
1278 Ok(ResidentCompactSchedulePlan {
1279 source_slots,
1280 ops,
1281 waves,
1282 regions,
1283 generation_bases,
1284 filter_comparisons: tables.filter_comparisons,
1285 project_expressions: tables.project_expressions,
1286 })
1287}
1288
1289#[cfg(test)]
1290impl ResidentCompactLogicalRegion {
1291 fn initializes(&self) -> bool {
1292 self.flags & RESIDENT_SCHEDULE_REGION_INITIALIZE != 0
1293 }
1294
1295 fn begins_scc(&self) -> bool {
1296 self.flags & RESIDENT_SCHEDULE_REGION_SCC_BEGIN != 0
1297 }
1298
1299 fn recursive(&self) -> bool {
1300 self.flags == RESIDENT_SCHEDULE_REGION_RECURSIVE
1301 }
1302
1303 fn finalizes(&self) -> bool {
1304 self.flags & RESIDENT_SCHEDULE_REGION_FINALIZE != 0
1305 }
1306}
1307
1308fn resident_compact_regions(
1309 initial_ops: Vec<ResidentRecordedOp>,
1310 phases: Vec<ResidentCapturePhase>,
1311 success_op_id: u32,
1312) -> Result<Vec<ResidentCompactLogicalRegion>> {
1313 let mut regions = Vec::new();
1314 let mut pending = initial_ops;
1315 let mut pending_seed_region = None;
1316
1317 for phase in phases {
1318 match phase {
1319 ResidentCapturePhase::Segment {
1320 mut ops,
1321 scc_begin: None,
1322 } => {
1323 if pending_seed_region.is_some() {
1324 return Err(XlogError::Execution(
1325 "resident SCC seed is not followed by its recursive body".into(),
1326 ));
1327 }
1328 pending.append(&mut ops);
1329 }
1330 ResidentCapturePhase::Segment {
1331 mut ops,
1332 scc_begin: Some((iteration_limit, op_id)),
1333 } => {
1334 if pending_seed_region.is_some() {
1335 return Err(XlogError::Execution(
1336 "resident SCC seed is not followed by its recursive body".into(),
1337 ));
1338 }
1339 pending.append(&mut ops);
1340 let mut flags = RESIDENT_SCHEDULE_REGION_SCC_BEGIN;
1341 if regions.is_empty() {
1342 flags |= RESIDENT_SCHEDULE_REGION_INITIALIZE;
1343 }
1344 regions.push(ResidentCompactLogicalRegion {
1345 ops: std::mem::take(&mut pending),
1346 iteration_limit,
1347 op_id,
1348 flags,
1349 });
1350 pending_seed_region = Some(regions.len() - 1);
1351 }
1352 ResidentCapturePhase::ConditionalWhile {
1353 ops,
1354 iteration_limit,
1355 convergence_op_id,
1356 } => {
1357 let seed_region = pending_seed_region.take().ok_or_else(|| {
1358 XlogError::Execution("resident recursive body has no preceding SCC seed".into())
1359 })?;
1360 if regions[seed_region].iteration_limit != iteration_limit {
1361 return Err(XlogError::Execution(
1362 "resident SCC seed and recursive body iteration limits differ".into(),
1363 ));
1364 }
1365 regions[seed_region].op_id = convergence_op_id;
1366 regions.push(ResidentCompactLogicalRegion {
1367 ops,
1368 iteration_limit,
1369 op_id: convergence_op_id,
1370 flags: RESIDENT_SCHEDULE_REGION_RECURSIVE,
1371 });
1372 }
1373 }
1374 }
1375 if pending_seed_region.is_some() {
1376 return Err(XlogError::Execution(
1377 "resident SCC seed is missing its recursive body".into(),
1378 ));
1379 }
1380
1381 let mut flags = RESIDENT_SCHEDULE_REGION_FINALIZE;
1382 if regions.is_empty() {
1383 flags |= RESIDENT_SCHEDULE_REGION_INITIALIZE;
1384 }
1385 regions.push(ResidentCompactLogicalRegion {
1386 ops: pending,
1387 iteration_limit: 1,
1388 op_id: success_op_id,
1389 flags,
1390 });
1391 Ok(regions)
1392}
1393
1394#[cfg(test)]
1395fn coalesce_resident_capture_phases(
1396 phases: Vec<ResidentCapturePhase>,
1397) -> Vec<ResidentCapturePhase> {
1398 let mut coalesced = Vec::new();
1399 let mut ordinary_ops = Vec::new();
1400 let flush_ordinary = |coalesced: &mut Vec<ResidentCapturePhase>,
1401 ordinary_ops: &mut Vec<ResidentRecordedOp>| {
1402 if !ordinary_ops.is_empty() {
1403 coalesced.push(ResidentCapturePhase::Segment {
1404 ops: std::mem::take(ordinary_ops),
1405 scc_begin: None,
1406 });
1407 }
1408 };
1409
1410 for phase in phases {
1411 match phase {
1412 ResidentCapturePhase::Segment {
1413 mut ops,
1414 scc_begin: None,
1415 } => ordinary_ops.append(&mut ops),
1416 boundary => {
1417 flush_ordinary(&mut coalesced, &mut ordinary_ops);
1418 coalesced.push(boundary);
1419 }
1420 }
1421 }
1422 flush_ordinary(&mut coalesced, &mut ordinary_ops);
1423 coalesced
1424}
1425
1426#[cfg(test)]
1427mod capture_phase_tests {
1428 use super::{coalesce_resident_capture_phases, ResidentCapturePhase, ResidentRecordedOp};
1429
1430 fn segment(output: usize, scc_begin: Option<(u32, u32)>) -> ResidentCapturePhase {
1431 ResidentCapturePhase::Segment {
1432 ops: vec![ResidentRecordedOp::Clear { output }],
1433 scc_begin,
1434 }
1435 }
1436
1437 fn assert_clear_range(phase: &ResidentCapturePhase, expected: std::ops::Range<usize>) {
1438 let ResidentCapturePhase::Segment {
1439 ops,
1440 scc_begin: None,
1441 } = phase
1442 else {
1443 panic!("expected an ordinary capture segment");
1444 };
1445 assert_eq!(ops.len(), expected.len());
1446 for (op, output) in ops.iter().zip(expected) {
1447 assert!(
1448 matches!(op, ResidentRecordedOp::Clear { output: actual } if *actual == output)
1449 );
1450 }
1451 }
1452
1453 #[test]
1454 fn coalesces_only_maximal_ordinary_phase_runs() {
1455 let mut phases = (0..1_000)
1456 .map(|output| segment(output, None))
1457 .collect::<Vec<_>>();
1458 phases.push(segment(10_000, Some((64, 41))));
1459 phases.extend((1_000..1_500).map(|output| segment(output, None)));
1460 phases.push(ResidentCapturePhase::ConditionalWhile {
1461 ops: vec![ResidentRecordedOp::Clear { output: 20_000 }],
1462 iteration_limit: 64,
1463 convergence_op_id: 42,
1464 });
1465 phases.extend((1_500..1_800).map(|output| segment(output, None)));
1466
1467 let coalesced = coalesce_resident_capture_phases(phases);
1468 assert_eq!(coalesced.len(), 5);
1469 assert_clear_range(&coalesced[0], 0..1_000);
1470 assert!(matches!(
1471 &coalesced[1],
1472 ResidentCapturePhase::Segment {
1473 ops,
1474 scc_begin: Some((64, 41)),
1475 } if matches!(ops.as_slice(), [ResidentRecordedOp::Clear { output: 10_000 }])
1476 ));
1477 assert_clear_range(&coalesced[2], 1_000..1_500);
1478 assert!(matches!(
1479 &coalesced[3],
1480 ResidentCapturePhase::ConditionalWhile {
1481 ops,
1482 iteration_limit: 64,
1483 convergence_op_id: 42,
1484 } if matches!(ops.as_slice(), [ResidentRecordedOp::Clear { output: 20_000 }])
1485 ));
1486 assert_clear_range(&coalesced[4], 1_500..1_800);
1487 }
1488}
1489
1490struct ResidentBuild<'executor> {
1491 executor: &'executor Executor,
1492 certificate: &'executor ResidentGraphRouteCertificate,
1493 capacity: u32,
1494 relations: Vec<ResidentLogicalRelation>,
1495 filter_workspaces: Vec<ResidentFilterPlan>,
1496 project_workspaces: Vec<ResidentProjectPlan>,
1497 heads: BTreeMap<String, usize>,
1498 head_winner_indices: BTreeMap<String, u32>,
1499 source_names: HashSet<String>,
1500 source_aliases: HashMap<String, usize>,
1501 next_op_id: u32,
1502 injection: Option<crate::resident_graph::ResidentGraphDeviceStatusTestInjection>,
1503 injection_recorded: bool,
1504}
1505
1506#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1507enum ResidentSourceBindingRoute {
1508 Direct,
1509 NormalizeEmpty,
1510}
1511
1512fn resident_source_logical_count(cached: Option<u32>) -> Result<u64> {
1513 cached.map(u64::from).ok_or_else(|| {
1514 XlogError::Execution("resident source requires a cold-path cached logical row count".into())
1515 })
1516}
1517
1518fn resident_source_binding_route(
1519 row_count: u64,
1520 count_tracked: bool,
1521 columns_tracked: bool,
1522) -> Result<ResidentSourceBindingRoute> {
1523 if count_tracked && columns_tracked {
1524 return Ok(ResidentSourceBindingRoute::Direct);
1525 }
1526 if row_count == 0 {
1527 return Ok(ResidentSourceBindingRoute::NormalizeEmpty);
1528 }
1529 Err(XlogError::Execution(
1530 "nonempty resident source is not fully runtime tracked".into(),
1531 ))
1532}
1533
1534#[derive(Debug, Clone)]
1535struct ResidentLogicalRelation {
1536 schema: Schema,
1537 initial_count: u32,
1538 permanent: bool,
1539}
1540
1541#[derive(Debug, Clone)]
1542struct ResidentFilterPlan {
1543 compact_comparisons: Vec<ResidentFilterComparisonDescriptor>,
1544}
1545
1546#[derive(Debug, Clone)]
1547struct ResidentProjectPlan {
1548 compact_expressions: Vec<ResidentProjectExpressionDescriptor>,
1549}
1550
1551#[derive(Debug, Clone, Copy)]
1552struct ResidentSlotAssignment {
1553 slot: usize,
1554 generation: u32,
1555}
1556
1557#[derive(Debug, Clone)]
1558struct ResidentPhysicalSlotPlan {
1559 schema: Schema,
1560 initial_count: u32,
1561 permanent: bool,
1562}
1563
1564#[derive(Debug, Clone)]
1565struct ResidentAllocationManifest {
1566 slots: Vec<ResidentPhysicalSlotPlan>,
1567 logical_to_slot: Vec<ResidentSlotAssignment>,
1568 required_bytes: u64,
1569 relation_bytes: u64,
1570 filter_scratch_bytes: u64,
1571 schedule_metadata_bytes: u64,
1572 fixed_workspace_bytes: u64,
1573 logical_relation_values: usize,
1574 permanent_slots: u32,
1575 scratch_slots: u32,
1576 filter_scratch_allocations: u32,
1577 max_row_bytes: u64,
1578}
1579
1580struct ResidentPhysicalBuild {
1581 relations: Vec<Option<ResidentRelation>>,
1582 filter_scratch: Option<ResidentFilterScratch>,
1583 set_workspace: ResidentSetWorkspace,
1584 join_workspace: ResidentJoinWorkspace,
1585 control: ResidentConvergenceControl,
1586}
1587
1588#[doc(hidden)]
1590#[derive(Default)]
1591pub struct ResidentPrepareDiagnostics {
1592 sample: u64,
1593 total_ns: u64,
1594 required_reservation_bytes: u64,
1595 logical_relation_values: usize,
1596 physical_relation_slots: usize,
1597 relation_device_allocation_calls: u64,
1598 compact_ops: usize,
1599 compact_waves: usize,
1600 compact_regions: usize,
1601 conditional_regions: usize,
1602 parent_graph_nodes: usize,
1603 conditional_body_nodes: usize,
1604 admission_and_source_snapshot_ns: u64,
1605 execution_domain_and_build_setup_ns: u64,
1606 logical_schedule_planning_ns: u64,
1607 manifest_compact_construction_ns: u64,
1608 schedule_lowering_ns: u64,
1609 reservation_ns: u64,
1610 relation_slot_allocation_ns: u64,
1611 relation_slot_allocation_ns_max: u64,
1612 relation_slot_allocation_bytes: u64,
1613 count_initialization_ns: u64,
1614 count_initialization_ns_max: u64,
1615 count_memset_calls: u64,
1616 workspace_provider_calls: u64,
1617 filter_scratch_allocation_ns: u64,
1618 filter_scratch_allocation_bytes: u64,
1619 set_workspace_allocation_ns: u64,
1620 set_workspace_allocation_bytes: u64,
1621 join_workspace_allocation_ns: u64,
1622 join_workspace_allocation_bytes: u64,
1623 control_allocation_ns: u64,
1624 control_allocation_bytes: u64,
1625 metadata_binding_construction_ns: u64,
1626 metadata_provider_calls: u64,
1627 device_trace_preparation_ns: u64,
1628 device_trace_reserved_bytes: u64,
1629 device_trace_initial_htod_calls: u64,
1630 device_trace_initial_htod_bytes: u64,
1631 schema_winners_preparation_ns: u64,
1632 schema_winners_reserved_bytes: u64,
1633 schema_winners_initial_htod_calls: u64,
1634 schema_winners_initial_htod_bytes: u64,
1635 receipt_preparation_ns: u64,
1636 receipt_reserved_bytes: u64,
1637 receipt_initial_htod_calls: u64,
1638 receipt_initial_htod_bytes: u64,
1639 schedule_program_preparation_ns: u64,
1640 schedule_program_reserved_bytes: u64,
1641 schedule_program_initial_htod_calls: u64,
1642 schedule_program_initial_htod_bytes: u64,
1643 reservation_validation_and_release_ns: u64,
1644 pinned_receipt_ns: u64,
1645 graph_body_capture_ns: u64,
1646 graph_instantiate_ns: u64,
1647 validation_owner_assembly_ns: u64,
1648}
1649
1650fn resident_prepare_diagnostics_for_sample(
1651 sample: Option<u64>,
1652) -> Option<ResidentPrepareDiagnostics> {
1653 sample.map(|sample| ResidentPrepareDiagnostics {
1654 sample,
1655 ..ResidentPrepareDiagnostics::default()
1656 })
1657}
1658
1659fn resident_prepare_elapsed_ns(started: std::time::Instant) -> u64 {
1660 u64::try_from(started.elapsed().as_nanos()).unwrap_or(u64::MAX)
1661}
1662
1663fn resident_schema_winners_initial_htod(default_count: usize) -> (u64, u64) {
1664 if default_count == 0 {
1665 return (0, 0);
1666 }
1667 (
1668 1,
1669 u64::try_from(default_count)
1670 .unwrap_or(u64::MAX)
1671 .saturating_mul(std::mem::size_of::<u32>() as u64),
1672 )
1673}
1674
1675fn resident_receipt_initial_htod(output_count: usize) -> (u64, u64) {
1676 let pointee_count = output_count.saturating_mul(2).saturating_add(4);
1677 if pointee_count == 0 {
1678 return (0, 0);
1679 }
1680 (
1681 1,
1682 u64::try_from(pointee_count)
1683 .unwrap_or(u64::MAX)
1684 .saturating_mul(std::mem::size_of::<u64>() as u64),
1685 )
1686}
1687
1688fn resident_schedule_initial_htod(reserved_bytes: u64) -> (u64, u64) {
1689 (8, reserved_bytes)
1690}
1691
1692impl ResidentPrepareDiagnostics {
1693 pub fn into_snapshot(self) -> ResidentGraphPrepareDiagnosticSnapshot {
1694 let workspace_allocation_ns = self
1695 .filter_scratch_allocation_ns
1696 .saturating_add(self.set_workspace_allocation_ns)
1697 .saturating_add(self.join_workspace_allocation_ns)
1698 .saturating_add(self.control_allocation_ns);
1699 let workspace_allocation_bytes = self
1700 .filter_scratch_allocation_bytes
1701 .saturating_add(self.set_workspace_allocation_bytes)
1702 .saturating_add(self.join_workspace_allocation_bytes)
1703 .saturating_add(self.control_allocation_bytes);
1704 let metadata_preparation_ns = self
1705 .device_trace_preparation_ns
1706 .saturating_add(self.schema_winners_preparation_ns)
1707 .saturating_add(self.receipt_preparation_ns)
1708 .saturating_add(self.schedule_program_preparation_ns);
1709 let metadata_reserved_bytes = self
1710 .device_trace_reserved_bytes
1711 .saturating_add(self.schema_winners_reserved_bytes)
1712 .saturating_add(self.receipt_reserved_bytes)
1713 .saturating_add(self.schedule_program_reserved_bytes);
1714 let additive = [
1715 self.admission_and_source_snapshot_ns,
1716 self.execution_domain_and_build_setup_ns,
1717 self.logical_schedule_planning_ns,
1718 self.manifest_compact_construction_ns,
1719 self.schedule_lowering_ns,
1720 self.reservation_ns,
1721 self.relation_slot_allocation_ns,
1722 self.count_initialization_ns,
1723 workspace_allocation_ns,
1724 self.metadata_binding_construction_ns,
1725 metadata_preparation_ns,
1726 self.reservation_validation_and_release_ns,
1727 self.pinned_receipt_ns,
1728 self.graph_body_capture_ns,
1729 self.graph_instantiate_ns,
1730 self.validation_owner_assembly_ns,
1731 ];
1732 let attributed_ns = additive.iter().copied().fold(0u64, u64::saturating_add);
1733 let unattributed_ns = self.total_ns.saturating_sub(attributed_ns);
1734 let metadata_initial_htod_calls = self
1735 .device_trace_initial_htod_calls
1736 .saturating_add(self.schema_winners_initial_htod_calls)
1737 .saturating_add(self.receipt_initial_htod_calls)
1738 .saturating_add(self.schedule_program_initial_htod_calls);
1739 let metadata_initial_htod_bytes = self
1740 .device_trace_initial_htod_bytes
1741 .saturating_add(self.schema_winners_initial_htod_bytes)
1742 .saturating_add(self.receipt_initial_htod_bytes)
1743 .saturating_add(self.schedule_program_initial_htod_bytes);
1744 let to_u64 = |value: usize| u64::try_from(value).unwrap_or(u64::MAX);
1745 ResidentGraphPrepareDiagnosticSnapshot {
1746 sample: self.sample,
1747 total_ns: self.total_ns,
1748 admission_and_source_snapshot_ns: self.admission_and_source_snapshot_ns,
1749 execution_domain_and_build_setup_ns: self.execution_domain_and_build_setup_ns,
1750 logical_schedule_planning_ns: self.logical_schedule_planning_ns,
1751 manifest_compact_construction_ns: self.manifest_compact_construction_ns,
1752 schedule_lowering_ns: self.schedule_lowering_ns,
1753 reservation_ns: self.reservation_ns,
1754 relation_preparation_ns: self.relation_slot_allocation_ns,
1755 count_initialization_ns: self.count_initialization_ns,
1756 workspace_preparation_ns: workspace_allocation_ns,
1757 metadata_binding_construction_ns: self.metadata_binding_construction_ns,
1758 metadata_preparation_ns,
1759 reservation_validation_and_release_ns: self.reservation_validation_and_release_ns,
1760 pinned_receipt_ns: self.pinned_receipt_ns,
1761 graph_body_capture_ns: self.graph_body_capture_ns,
1762 graph_instantiate_ns: self.graph_instantiate_ns,
1763 validation_owner_assembly_ns: self.validation_owner_assembly_ns,
1764 unattributed_ns,
1765 required_reservation_bytes: self.required_reservation_bytes,
1766 logical_relation_values: to_u64(self.logical_relation_values),
1767 physical_relation_slots: to_u64(self.physical_relation_slots),
1768 relation_device_allocation_calls: self.relation_device_allocation_calls,
1769 relation_reserved_bytes: self.relation_slot_allocation_bytes,
1770 relation_slot_preparation_ns_max: self.relation_slot_allocation_ns_max,
1771 count_memset_calls: self.count_memset_calls,
1772 count_memset_bytes: self
1773 .count_memset_calls
1774 .saturating_mul(std::mem::size_of::<u32>() as u64),
1775 count_initialization_ns_max: self.count_initialization_ns_max,
1776 workspace_provider_calls: self.workspace_provider_calls,
1777 workspace_reserved_bytes: workspace_allocation_bytes,
1778 filter_scratch_preparation_ns: self.filter_scratch_allocation_ns,
1779 filter_scratch_reserved_bytes: self.filter_scratch_allocation_bytes,
1780 set_workspace_preparation_ns: self.set_workspace_allocation_ns,
1781 set_workspace_reserved_bytes: self.set_workspace_allocation_bytes,
1782 join_workspace_preparation_ns: self.join_workspace_allocation_ns,
1783 join_workspace_reserved_bytes: self.join_workspace_allocation_bytes,
1784 control_preparation_ns: self.control_allocation_ns,
1785 control_reserved_bytes: self.control_allocation_bytes,
1786 metadata_provider_calls: self.metadata_provider_calls,
1787 metadata_reserved_bytes,
1788 metadata_initial_htod_calls,
1789 metadata_initial_htod_bytes,
1790 device_trace_preparation_ns: self.device_trace_preparation_ns,
1791 device_trace_reserved_bytes: self.device_trace_reserved_bytes,
1792 device_trace_initial_htod_calls: self.device_trace_initial_htod_calls,
1793 device_trace_initial_htod_bytes: self.device_trace_initial_htod_bytes,
1794 schema_winners_preparation_ns: self.schema_winners_preparation_ns,
1795 schema_winners_reserved_bytes: self.schema_winners_reserved_bytes,
1796 schema_winners_initial_htod_calls: self.schema_winners_initial_htod_calls,
1797 schema_winners_initial_htod_bytes: self.schema_winners_initial_htod_bytes,
1798 receipt_preparation_ns: self.receipt_preparation_ns,
1799 receipt_reserved_bytes: self.receipt_reserved_bytes,
1800 receipt_initial_htod_calls: self.receipt_initial_htod_calls,
1801 receipt_initial_htod_bytes: self.receipt_initial_htod_bytes,
1802 schedule_program_preparation_ns: self.schedule_program_preparation_ns,
1803 schedule_program_reserved_bytes: self.schedule_program_reserved_bytes,
1804 schedule_program_initial_htod_calls: self.schedule_program_initial_htod_calls,
1805 schedule_program_initial_htod_bytes: self.schedule_program_initial_htod_bytes,
1806 compact_ops: to_u64(self.compact_ops),
1807 compact_waves: to_u64(self.compact_waves),
1808 compact_regions: to_u64(self.compact_regions),
1809 conditional_regions: to_u64(self.conditional_regions),
1810 parent_graph_nodes: to_u64(self.parent_graph_nodes),
1811 conditional_body_nodes: to_u64(self.conditional_body_nodes),
1812 }
1813 }
1814}
1815
1816#[cfg(test)]
1817mod prepare_diagnostic_tests {
1818 use super::{
1819 resident_prepare_diagnostics_for_sample, resident_receipt_initial_htod,
1820 resident_schedule_initial_htod, resident_schema_winners_initial_htod,
1821 ResidentPrepareDiagnostics,
1822 };
1823
1824 #[test]
1825 fn prepare_diagnostic_counts_only_source_proven_initial_transfers() {
1826 assert_eq!(resident_schema_winners_initial_htod(0), (0, 0));
1827 assert_eq!(resident_schema_winners_initial_htod(3), (1, 12));
1828 assert_eq!(resident_receipt_initial_htod(0), (1, 32));
1829 assert_eq!(resident_receipt_initial_htod(3), (1, 80));
1830 assert_eq!(resident_schedule_initial_htod(4096), (8, 4096));
1831
1832 let (schema_calls, schema_bytes) = resident_schema_winners_initial_htod(3);
1833 let (receipt_calls, receipt_bytes) = resident_receipt_initial_htod(0);
1834 let (schedule_calls, schedule_bytes) = resident_schedule_initial_htod(4096);
1835 let diagnostics = ResidentPrepareDiagnostics {
1836 sample: 9,
1837 total_ns: 100,
1838 relation_slot_allocation_ns: 11,
1839 relation_slot_allocation_ns_max: 7,
1840 relation_slot_allocation_bytes: 2048,
1841 workspace_provider_calls: 4,
1842 count_initialization_ns: 10,
1843 count_initialization_ns_max: 5,
1844 count_memset_calls: 3,
1845 metadata_provider_calls: 4,
1846 schema_winners_initial_htod_calls: schema_calls,
1847 schema_winners_initial_htod_bytes: schema_bytes,
1848 receipt_initial_htod_calls: receipt_calls,
1849 receipt_initial_htod_bytes: receipt_bytes,
1850 schedule_program_initial_htod_calls: schedule_calls,
1851 schedule_program_initial_htod_bytes: schedule_bytes,
1852 ..ResidentPrepareDiagnostics::default()
1853 };
1854 let snapshot = diagnostics.into_snapshot();
1855 assert_eq!(snapshot.sample, 9);
1856 assert_eq!(snapshot.total_ns, 100);
1857 assert_eq!(snapshot.relation_preparation_ns, 11);
1858 assert_eq!(snapshot.relation_reserved_bytes, 2048);
1859 assert_eq!(snapshot.relation_slot_preparation_ns_max, 7);
1860 assert_eq!(snapshot.workspace_provider_calls, 4);
1861 assert_eq!(snapshot.count_memset_calls, 3);
1862 assert_eq!(snapshot.count_initialization_ns, 10);
1863 assert_eq!(snapshot.count_initialization_ns_max, 5);
1864 assert_eq!(
1865 snapshot.count_memset_bytes,
1866 3 * std::mem::size_of::<u32>() as u64
1867 );
1868 assert_eq!(snapshot.metadata_provider_calls, 4);
1869 assert_eq!(snapshot.device_trace_initial_htod_calls, 0);
1870 assert_eq!(snapshot.device_trace_initial_htod_bytes, 0);
1871 assert_eq!(snapshot.schema_winners_initial_htod_calls, 1);
1872 assert_eq!(snapshot.schema_winners_initial_htod_bytes, 12);
1873 assert_eq!(snapshot.receipt_initial_htod_calls, 1);
1874 assert_eq!(snapshot.receipt_initial_htod_bytes, 32);
1875 assert_eq!(snapshot.schedule_program_initial_htod_calls, 8);
1876 assert_eq!(snapshot.schedule_program_initial_htod_bytes, 4096);
1877 assert_eq!(snapshot.metadata_initial_htod_calls, 10);
1878 assert_eq!(snapshot.metadata_initial_htod_bytes, 4140);
1879 }
1880
1881 #[test]
1882 fn prepare_diagnostic_is_absent_when_sampling_is_disabled() {
1883 assert!(resident_prepare_diagnostics_for_sample(None).is_none());
1884 assert_eq!(
1885 resident_prepare_diagnostics_for_sample(Some(23))
1886 .expect("enabled diagnostics create raw timing state")
1887 .sample,
1888 23
1889 );
1890 }
1891
1892 #[test]
1893 fn prepare_diagnostic_keeps_setup_and_reservation_validation_separate() {
1894 let snapshot = ResidentPrepareDiagnostics {
1895 sample: 5,
1896 total_ns: 100,
1897 admission_and_source_snapshot_ns: 11,
1898 execution_domain_and_build_setup_ns: 13,
1899 metadata_binding_construction_ns: 17,
1900 reservation_validation_and_release_ns: 19,
1901 ..ResidentPrepareDiagnostics::default()
1902 }
1903 .into_snapshot();
1904
1905 assert_eq!(snapshot.admission_and_source_snapshot_ns, 11);
1906 assert_eq!(snapshot.execution_domain_and_build_setup_ns, 13);
1907 assert_eq!(snapshot.metadata_binding_construction_ns, 17);
1908 assert_eq!(snapshot.reservation_validation_and_release_ns, 19);
1909 assert_eq!(snapshot.unattributed_ns, 40);
1910 }
1911}
1912
1913impl ResidentAllocationManifest {
1914 fn finalize_compact_schedule(
1915 &mut self,
1916 schedule: &ResidentCompactSchedulePlan,
1917 head_count: usize,
1918 ) -> Result<()> {
1919 let (required_bytes, schedule_metadata_bytes) = resident_compact_allocation_bytes(
1920 self.relation_bytes,
1921 self.filter_scratch_bytes,
1922 self.fixed_workspace_bytes,
1923 self.slots.len(),
1924 head_count,
1925 schedule,
1926 )?;
1927 self.required_bytes = required_bytes;
1928 self.schedule_metadata_bytes = schedule_metadata_bytes;
1929 Ok(())
1930 }
1931}
1932
1933fn resident_output_indices(
1934 heads: &BTreeMap<String, usize>,
1935 assignments: &[ResidentSlotAssignment],
1936 slots: &[ResidentPhysicalSlotPlan],
1937) -> Result<Vec<(String, usize)>> {
1938 heads
1939 .iter()
1940 .map(|(name, logical)| {
1941 let assignment = assignments.get(*logical).ok_or_else(|| {
1942 XlogError::Execution("resident head has no physical slot assignment".into())
1943 })?;
1944 if !slots
1945 .get(assignment.slot)
1946 .is_some_and(|slot| slot.permanent)
1947 {
1948 return Err(XlogError::Execution(
1949 "resident head is not assigned to a permanent physical slot".into(),
1950 ));
1951 }
1952 Ok((name.clone(), assignment.slot))
1953 })
1954 .collect()
1955}
1956
1957fn resident_validate_exact_reservation(required: u64, used: u64, remaining: u64) -> Result<()> {
1958 if used == required && remaining == 0 {
1959 return Ok(());
1960 }
1961 Err(XlogError::Execution(format!(
1962 "resident allocation manifest accounted for {required} manager-tracked bytes but materialization consumed {used} and left {remaining} reserved bytes"
1963 )))
1964}
1965
1966#[derive(Debug)]
1967struct ResidentScratchSlotState {
1968 slot: usize,
1969 layout: Vec<ScalarType>,
1970 last_use: usize,
1971 generation: u32,
1972}
1973
1974fn resident_schema_layout(schema: &Schema) -> Vec<ScalarType> {
1975 schema.columns.iter().map(|(_, scalar)| *scalar).collect()
1976}
1977
1978fn resident_private_inputs(op: &ResidentRecordedOp, mut visit: impl FnMut(usize)) {
1979 let mut visit_ref = |reference: &ResidentBufferRef| {
1980 if let ResidentBufferRef::Private(index) = reference {
1981 visit(*index);
1982 }
1983 };
1984 match op {
1985 ResidentRecordedOp::Scan {
1986 relation: input, ..
1987 }
1988 | ResidentRecordedOp::Filter { input, .. }
1989 | ResidentRecordedOp::Project { input, .. } => visit_ref(input),
1990 ResidentRecordedOp::Union { left, right, .. }
1991 | ResidentRecordedOp::Diff { left, right, .. }
1992 | ResidentRecordedOp::Join { left, right, .. } => {
1993 visit_ref(left);
1994 visit_ref(right);
1995 }
1996 ResidentRecordedOp::ChangedMark { relation } => visit(*relation),
1997 ResidentRecordedOp::TraceDelta { semantic_guard, .. } => {
1998 if let Some(semantic_guard) = semantic_guard {
1999 visit_ref(semantic_guard);
2000 }
2001 }
2002 ResidentRecordedOp::SchemaWinnerMark { contribution, .. } => visit_ref(contribution),
2003 ResidentRecordedOp::Unit { .. }
2004 | ResidentRecordedOp::ChangedReset
2005 | ResidentRecordedOp::TestStatus(_) => {}
2006 #[cfg(test)]
2007 ResidentRecordedOp::Clear { .. } => {}
2008 }
2009}
2010
2011fn resident_private_output(op: &ResidentRecordedOp) -> Option<usize> {
2012 match op {
2013 ResidentRecordedOp::Unit { output, .. }
2014 | ResidentRecordedOp::Filter { output, .. }
2015 | ResidentRecordedOp::Project { output, .. }
2016 | ResidentRecordedOp::Union { output, .. }
2017 | ResidentRecordedOp::Diff { output, .. }
2018 | ResidentRecordedOp::Join { output, .. } => Some(*output),
2019 #[cfg(test)]
2020 ResidentRecordedOp::Clear { output } => Some(*output),
2021 ResidentRecordedOp::Scan { .. }
2022 | ResidentRecordedOp::TraceDelta { .. }
2023 | ResidentRecordedOp::ChangedReset
2024 | ResidentRecordedOp::ChangedMark { .. }
2025 | ResidentRecordedOp::SchemaWinnerMark { .. }
2026 | ResidentRecordedOp::TestStatus(_) => None,
2027 }
2028}
2029
2030fn resident_source_slot_map<'a>(
2031 private_slot_count: usize,
2032 sources: impl IntoIterator<Item = &'a str>,
2033) -> Result<BTreeMap<String, u32>> {
2034 let first_source_slot = u32::try_from(private_slot_count)
2035 .map_err(|_| XlogError::Execution("resident private slot count exceeds u32".into()))?;
2036 let names = sources
2037 .into_iter()
2038 .map(str::to_owned)
2039 .collect::<BTreeSet<_>>();
2040 names
2041 .into_iter()
2042 .enumerate()
2043 .map(|(offset, name)| {
2044 let offset = u32::try_from(offset).map_err(|_| {
2045 XlogError::Execution("resident source slot count exceeds u32".into())
2046 })?;
2047 let slot = first_source_slot.checked_add(offset).ok_or_else(|| {
2048 XlogError::Execution("resident source slot index overflow".into())
2049 })?;
2050 Ok((name, slot))
2051 })
2052 .collect()
2053}
2054
2055fn resident_record_lifetimes(
2056 ops: &[ResidentRecordedOp],
2057 relations: &[ResidentLogicalRelation],
2058 definitions: &mut [Option<usize>],
2059 last_uses: &mut [Option<usize>],
2060 ordinal: &mut usize,
2061) -> Result<(usize, usize)> {
2062 let start = *ordinal;
2063 for op in ops {
2064 let current = *ordinal;
2065 let mut invalid_input = None;
2066 let mut input_before_definition = None;
2067 resident_private_inputs(op, |logical| {
2068 let Some(relation) = relations.get(logical) else {
2069 invalid_input.get_or_insert(logical);
2070 return;
2071 };
2072 if !relation.permanent {
2073 if definitions[logical].is_none() {
2074 input_before_definition.get_or_insert(logical);
2075 } else {
2076 last_uses[logical] = Some(last_uses[logical].unwrap_or(current).max(current));
2077 }
2078 }
2079 });
2080 if let Some(logical) = invalid_input {
2081 return Err(XlogError::Execution(format!(
2082 "resident logical input relation {logical} is missing"
2083 )));
2084 }
2085 if let Some(logical) = input_before_definition {
2086 return Err(XlogError::Execution(format!(
2087 "resident scratch relation {logical} is used before its definition"
2088 )));
2089 }
2090 if let Some(logical) = resident_private_output(op) {
2091 let relation = relations.get(logical).ok_or_else(|| {
2092 XlogError::Execution(format!(
2093 "resident logical output relation {logical} is missing"
2094 ))
2095 })?;
2096 if !relation.permanent {
2097 if definitions[logical].replace(current).is_some() {
2098 return Err(XlogError::Execution(format!(
2099 "resident scratch relation {logical} has multiple definitions"
2100 )));
2101 }
2102 last_uses[logical] = Some(last_uses[logical].unwrap_or(current).max(current));
2103 }
2104 }
2105 *ordinal = ordinal
2106 .checked_add(1)
2107 .ok_or_else(|| XlogError::Execution("resident operation ordinal overflow".into()))?;
2108 }
2109 Ok((start, *ordinal))
2110}
2111
2112fn resident_validate_slot_assignments(
2113 relations: &[ResidentLogicalRelation],
2114 definitions: &[Option<usize>],
2115 last_uses: &[Option<usize>],
2116 slots: &[ResidentPhysicalSlotPlan],
2117 assignments: &[ResidentSlotAssignment],
2118) -> Result<()> {
2119 if definitions.len() != relations.len()
2120 || last_uses.len() != relations.len()
2121 || assignments.len() != relations.len()
2122 {
2123 return Err(XlogError::Execution(
2124 "resident slot validation vector length mismatch".into(),
2125 ));
2126 }
2127
2128 let mut occupied_permanent_slots = HashSet::new();
2129 let mut scratch_by_slot = BTreeMap::<usize, Vec<(usize, usize, usize, u32)>>::new();
2130 let mut assigned_slots = HashSet::new();
2131 for (logical, relation) in relations.iter().enumerate() {
2132 let assignment = assignments[logical];
2133 let slot = slots.get(assignment.slot).ok_or_else(|| {
2134 XlogError::Execution(format!(
2135 "resident logical relation {logical} maps to missing physical slot {}",
2136 assignment.slot
2137 ))
2138 })?;
2139 assigned_slots.insert(assignment.slot);
2140 if relation.permanent {
2141 if assignment.generation != 0 {
2142 return Err(XlogError::Execution(format!(
2143 "resident permanent relation {logical} has nonzero generation {}",
2144 assignment.generation
2145 )));
2146 }
2147 if !slot.permanent
2148 || slot.schema != relation.schema
2149 || slot.initial_count != relation.initial_count
2150 {
2151 return Err(XlogError::Execution(format!(
2152 "resident permanent relation {logical} has an incompatible physical slot"
2153 )));
2154 }
2155 if !occupied_permanent_slots.insert(assignment.slot) {
2156 return Err(XlogError::Execution(format!(
2157 "resident permanent physical slot {} has multiple owners",
2158 assignment.slot
2159 )));
2160 }
2161 continue;
2162 }
2163
2164 if slot.permanent
2165 || resident_schema_layout(&slot.schema) != resident_schema_layout(&relation.schema)
2166 {
2167 return Err(XlogError::Execution(format!(
2168 "resident scratch relation {logical} has an incompatible physical slot"
2169 )));
2170 }
2171 let definition = definitions[logical].ok_or_else(|| {
2172 XlogError::Execution(format!(
2173 "resident scratch relation {logical} is never defined"
2174 ))
2175 })?;
2176 let last_use = last_uses[logical].unwrap_or(definition);
2177 if last_use < definition {
2178 return Err(XlogError::Execution(format!(
2179 "resident scratch relation {logical} ends before its definition"
2180 )));
2181 }
2182 scratch_by_slot.entry(assignment.slot).or_default().push((
2183 definition,
2184 last_use,
2185 logical,
2186 assignment.generation,
2187 ));
2188 }
2189
2190 if assigned_slots.len() != slots.len() {
2191 return Err(XlogError::Execution(
2192 "resident allocation manifest contains an unassigned physical slot".into(),
2193 ));
2194 }
2195 for (slot, mut generations) in scratch_by_slot {
2196 generations.sort_by_key(|(definition, _, logical, _)| (*definition, *logical));
2197 let mut previous_last_use = None;
2198 for (expected_generation, (definition, last_use, logical, generation)) in
2199 generations.into_iter().enumerate()
2200 {
2201 let expected_generation = u32::try_from(expected_generation).map_err(|_| {
2202 XlogError::Execution("resident scratch generation exceeds u32".into())
2203 })?;
2204 if generation != expected_generation {
2205 return Err(XlogError::Execution(format!(
2206 "resident scratch relation {logical} in slot {slot} has generation {generation} but expected {expected_generation}"
2207 )));
2208 }
2209 if previous_last_use.is_some_and(|previous| previous >= definition) {
2210 return Err(XlogError::Execution(format!(
2211 "resident scratch generations overlap in physical slot {slot}"
2212 )));
2213 }
2214 previous_last_use = Some(last_use);
2215 }
2216 }
2217 Ok(())
2218}
2219
2220fn resident_checked_add(total: &mut u64, bytes: u64, label: &str) -> Result<()> {
2221 *total = total.checked_add(bytes).ok_or_else(|| {
2222 XlogError::Execution(format!("resident {label} allocation byte overflow"))
2223 })?;
2224 Ok(())
2225}
2226
2227pub struct PreparedResidentGraph {
2229 owners: ResidentRunOwners,
2230 preflight_report: ResidentGraphPreflightReport,
2231 has_device_status_writer: bool,
2232 source_set_snapshots: Vec<ResidentSourceSetSnapshot>,
2233 source_buffers_to_restore: Vec<(String, CudaBuffer)>,
2234 prepare_diagnostic: Option<ResidentPrepareDiagnostics>,
2235}
2236
2237#[derive(Debug, Clone, PartialEq, Eq)]
2239pub struct ResidentGraphPreflightReport {
2240 pub relation_capacity: u32,
2242 pub estimated_required_bytes: u64,
2244 pub available_bytes_at_admission: u64,
2246 pub tracked_device_allocation_bytes: u64,
2248 pub relation_device_bytes: u64,
2250 pub filter_descriptor_device_bytes: u64,
2252 pub filter_scratch_device_bytes: u64,
2254 pub project_descriptor_device_bytes: u64,
2256 pub fixed_workspace_device_bytes: u64,
2258 pub parent_graph_nodes: usize,
2260 pub conditional_while_nodes: usize,
2262 pub parent_graph_node_kinds: Vec<CudaGraphNodeKind>,
2264 pub conditional_body_node_kinds: Vec<Vec<CudaGraphNodeKind>>,
2266 pub conditional_body_kernel_counts: Vec<usize>,
2268 pub hierarchical_graph_nodes: usize,
2270 pub private_relation_slots: usize,
2272 pub logical_relation_values: usize,
2274 pub permanent_relation_slots: u32,
2276 pub staged_output_relations: usize,
2278 pub scratch_slots: u32,
2280 pub filter_scratch_allocations: u32,
2282 pub max_row_bytes: u64,
2284}
2285
2286pub struct ResidentGraphInFlight<'executor> {
2288 execution: PendingResidentOwners<ResidentLaunchOwners>,
2289 replay_metadata: ResidentGraphReplayMetadata,
2290 _executor: PhantomData<&'executor Executor>,
2291}
2292
2293struct ResidentGraphReplayMetadata {
2294 preflight_report: ResidentGraphPreflightReport,
2295 has_device_status_writer: bool,
2296 source_set_snapshots: Vec<ResidentSourceSetSnapshot>,
2297 prepare_diagnostic: Option<ResidentPrepareDiagnostics>,
2298}
2299
2300struct ResidentLaunchOwners {
2301 completion: Option<ResidentCompletionEvent>,
2304 timing_start: Option<CudaEvent>,
2305 timing_end: Option<CudaEvent>,
2306 owners: ResidentRunOwners,
2307}
2308
2309pub struct ResidentGraphSynchronized<'executor> {
2311 device_elapsed_ns: u64,
2312 owners: ResidentRunOwners,
2313 replay_metadata: ResidentGraphReplayMetadata,
2314 _executor: PhantomData<&'executor Executor>,
2315}
2316
2317struct StagedResidentOutput {
2318 name: String,
2319 buffer: CudaBuffer,
2320}
2321
2322pub struct ObservedResidentGraphReceipt {
2324 encoded_len: usize,
2325 device_elapsed_ns: u64,
2326 device_scan_invocations: u64,
2327 device_filter_invocations: u64,
2328 semantic_scan_invocations: u64,
2329 semantic_filter_invocations: u64,
2330 iterations: u32,
2331 terminal: std::result::Result<(), ResidentGraphExecutionError>,
2332 outputs: Vec<StagedResidentOutput>,
2333 source_epoch: u64,
2334 relation_registration: Vec<(RelId, String)>,
2335 transaction_identity: Arc<()>,
2336 provider: Arc<CudaKernelProvider>,
2337 phase_timings: Option<ResidentFinalObservationPhaseTimings>,
2338 prepared_for_reuse: Option<PreparedResidentGraph>,
2339}
2340
2341#[doc(hidden)]
2343#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2344pub struct ResidentFinalObservationPhaseTimings {
2345 pub receipt_d2h_ns: u64,
2347 pub decode_schema_staging_ns: u64,
2349}
2350
2351fn resident_source_set_snapshot(
2352 provider: &CudaKernelProvider,
2353 name: &str,
2354 version: u64,
2355 buffer: &CudaBuffer,
2356) -> std::result::Result<ResidentSourceSetSnapshot, ResidentGraphDeclineReason> {
2357 if !buffer.canonical_full_row_set_certified() {
2358 return Err(ResidentGraphDeclineReason::SourceSetUncertified {
2359 relation: name.to_owned(),
2360 });
2361 }
2362 let manager_ptr = Arc::as_ptr(provider.memory()) as usize;
2363 let runtime = provider.memory().runtime().ok_or_else(|| {
2364 ResidentGraphDeclineReason::SourceSetUncertified {
2365 relation: format!("{name} memory manager has no device runtime"),
2366 }
2367 })?;
2368 if !Arc::ptr_eq(provider.device(), provider.memory().device())
2369 || !Arc::ptr_eq(provider.device(), runtime.device())
2370 || u32::try_from(provider.device().ordinal()).ok() != Some(runtime.device_ordinal())
2371 {
2372 return Err(ResidentGraphDeclineReason::SourceSetUncertified {
2373 relation: format!("{name} memory manager does not match the resident provider"),
2374 });
2375 }
2376 let column_blocks = buffer
2377 .columns()
2378 .iter()
2379 .enumerate()
2380 .map(|(column_index, column)| {
2381 let CudaColumn::Owned(column) = column else {
2382 return Err(ResidentGraphDeclineReason::SourceSetUncertified {
2383 relation: format!("{name} column {column_index} is externally owned"),
2384 });
2385 };
2386 if column.memory_manager_ptr_value() != manager_ptr {
2387 return Err(ResidentGraphDeclineReason::SourceSetUncertified {
2388 relation: format!(
2389 "{name} column {column_index} belongs to another memory manager"
2390 ),
2391 });
2392 }
2393 let block = column.runtime_block().map(BlockId::from_block);
2394 if block.is_none() && buffer.num_rows() != 0 {
2395 return Err(ResidentGraphDeclineReason::SourceSetUncertified {
2396 relation: format!("{name} column {column_index} is not runtime tracked"),
2397 });
2398 }
2399 Ok(block)
2400 })
2401 .collect::<std::result::Result<Vec<_>, _>>()?;
2402 let row_count = buffer.num_rows_device();
2403 if row_count.memory_manager_ptr_value() != manager_ptr {
2404 return Err(ResidentGraphDeclineReason::SourceSetUncertified {
2405 relation: format!("{name} logical count belongs to another memory manager"),
2406 });
2407 }
2408 let row_count_block = row_count
2409 .runtime_block()
2410 .map(BlockId::from_block)
2411 .ok_or_else(|| ResidentGraphDeclineReason::SourceSetUncertified {
2412 relation: format!("{name} logical count is not runtime tracked"),
2413 })?;
2414 Ok(ResidentSourceSetSnapshot {
2415 name: name.to_owned(),
2416 version,
2417 schema: buffer.schema().clone(),
2418 row_capacity: buffer.num_rows(),
2419 column_blocks,
2420 row_count_block,
2421 })
2422}
2423
2424fn validate_resident_source_set_snapshots(
2425 executor: &Executor,
2426 source_epoch: u64,
2427 snapshots: &[ResidentSourceSetSnapshot],
2428) -> std::result::Result<(), ResidentGraphExecutionError> {
2429 if executor.store.mutation_epoch() != source_epoch {
2430 return Err(resident_decline_error(
2431 ResidentGraphDeclineReason::SourceSetUncertified {
2432 relation: "relation store changed after resident preparation".to_owned(),
2433 },
2434 ));
2435 }
2436 for snapshot in snapshots {
2437 let Some((buffer, version)) = executor.store.get_with_version(&snapshot.name) else {
2438 return Err(resident_decline_error(
2439 ResidentGraphDeclineReason::SourceSetUncertified {
2440 relation: snapshot.name.clone(),
2441 },
2442 ));
2443 };
2444 let current =
2445 resident_source_set_snapshot(&executor.provider, &snapshot.name, version, buffer)
2446 .map_err(resident_decline_error)?;
2447 if ¤t != snapshot {
2448 return Err(resident_decline_error(
2449 ResidentGraphDeclineReason::SourceSetUncertified {
2450 relation: snapshot.name.clone(),
2451 },
2452 ));
2453 }
2454 }
2455 Ok(())
2456}
2457
2458impl PreparedResidentGraph {
2459 pub fn preflight_report(&self) -> &ResidentGraphPreflightReport {
2461 &self.preflight_report
2462 }
2463
2464 #[doc(hidden)]
2466 pub fn take_prepare_diagnostic(&mut self) -> Option<ResidentPrepareDiagnostics> {
2467 self.prepare_diagnostic.take()
2468 }
2469
2470 pub fn refresh_source_set_snapshots(
2473 &mut self,
2474 executor: &Executor,
2475 ) -> std::result::Result<(), ResidentGraphExecutionError> {
2476 for snapshot in &mut self.source_set_snapshots {
2477 let Some((buffer, version)) = executor.store.get_with_version(&snapshot.name) else {
2478 return Err(resident_decline_error(
2479 ResidentGraphDeclineReason::SourceSetUncertified {
2480 relation: snapshot.name.clone(),
2481 },
2482 ));
2483 };
2484 let current =
2485 resident_source_set_snapshot(&executor.provider, &snapshot.name, version, buffer)
2486 .map_err(resident_decline_error)?;
2487 let mut expected = snapshot.clone();
2488 expected.version = version;
2489 if current != expected {
2490 return Err(resident_decline_error(
2491 ResidentGraphDeclineReason::SourceSetUncertified {
2492 relation: snapshot.name.clone(),
2493 },
2494 ));
2495 }
2496 snapshot.version = version;
2497 }
2498 self.owners.source_epoch = executor.store.mutation_epoch();
2499 Ok(())
2500 }
2501
2502 pub fn restore_outputs(
2505 &mut self,
2506 executor: &mut Executor,
2507 ) -> std::result::Result<(), ResidentGraphExecutionError> {
2508 for (name, index) in &self.owners.output_indices {
2509 let slot = self
2510 .owners
2511 .relations
2512 .get_mut(*index)
2513 .ok_or_else(|| runtime_error("resident output slot index is invalid"))?;
2514 if slot.is_some() {
2515 return Err(runtime_error("resident output slot was not staged"));
2516 }
2517 let buffer = executor.store.remove(name).ok_or_else(|| {
2518 runtime_error(format!("resident output {name} was not published"))
2519 })?;
2520 *slot = Some(ResidentRelation::from_buffer(buffer));
2521 }
2522 for (name, buffer) in self.source_buffers_to_restore.drain(..) {
2523 executor.store.put_owned(name, buffer);
2524 }
2525 Ok(())
2526 }
2527
2528 #[cfg(all(feature = "resident-graph-tests", test))]
2529 pub(crate) fn invalidate_expected_source_epoch(&mut self) {
2530 self.owners.source_epoch = self.owners.source_epoch.wrapping_add(1);
2531 }
2532
2533 pub fn launch<'executor>(
2535 self,
2536 source_guard: &'executor Executor,
2537 ) -> std::result::Result<ResidentGraphInFlight<'executor>, ResidentGraphExecutionError> {
2538 if !self.source_buffers_to_restore.is_empty() {
2539 return Err(runtime_error(
2540 "resident source buffers were not restored before graph launch",
2541 ));
2542 }
2543 validate_resident_source_set_snapshots(
2544 source_guard,
2545 self.owners.source_epoch,
2546 &self.source_set_snapshots,
2547 )?;
2548 let next_recorder = resident_replay_recorder(&self.owners, source_guard)?;
2549 for (_, index) in &self.owners.output_indices {
2550 private_relation(&self.owners.relations, *index)
2551 .map_err(runtime_error)?
2552 .invalidate_observed_row_count();
2553 }
2554 let replay_metadata = ResidentGraphReplayMetadata {
2555 preflight_report: self.preflight_report,
2556 has_device_status_writer: self.has_device_status_writer,
2557 source_set_snapshots: self.source_set_snapshots,
2558 prepare_diagnostic: self.prepare_diagnostic,
2559 };
2560 let execution = PendingResidentOwners::new(
2564 ResidentLaunchOwners {
2565 completion: None,
2566 timing_start: None,
2567 timing_end: None,
2568 owners: self.owners,
2569 },
2570 |execution| match execution.completion.as_mut() {
2571 Some(completion) => completion.synchronize().is_ok(),
2572 None => execution.owners.stream.synchronize().is_ok(),
2573 },
2574 );
2575 let (execution, ()) = execution.with_admission(
2576 |execution| reserve_uncaptured_stream(&execution.owners.stream).map_err(runtime_error),
2577 |execution| {
2578 let execution_domain = execution.owners.execution_domain.clone();
2579 let recorder = std::mem::replace(&mut execution.owners.recorder, next_recorder);
2580 let enqueued = unsafe {
2584 execution_domain.enqueue(recorder, |stream| {
2585 execution.timing_start = Some(
2586 stream
2587 .stream()
2588 .record_event(Some(CUevent_flags::CU_EVENT_DEFAULT))
2589 .map_err(|error| {
2590 runtime_error(format!(
2591 "resident timing-start event failed: {error}"
2592 ))
2593 })?,
2594 );
2595 execution
2596 .owners
2597 .graph
2598 .launch_in(stream)
2599 .map_err(runtime_error)?;
2600 execution.timing_end = Some(
2601 stream
2602 .stream()
2603 .record_event(Some(CUevent_flags::CU_EVENT_DEFAULT))
2604 .map_err(|error| {
2605 runtime_error(format!(
2606 "resident timing-end event failed after graph launch: {error}"
2607 ))
2608 })?,
2609 );
2610 execution.owners.runtime.record_conditional_graph_launch(
2611 replay_metadata.has_device_status_writer,
2612 );
2613 Ok::<(), ResidentGraphExecutionError>(())
2614 })
2615 }
2616 .map_err(runtime_error)?;
2617 enqueued.commit().map_err(runtime_error)?;
2618 execution.completion = Some(
2619 execution
2620 .owners
2621 .runtime
2622 .record_resident_completion_event(&execution.owners.stream)
2623 .map_err(runtime_error)?,
2624 );
2625 Ok::<(), ResidentGraphExecutionError>(())
2626 },
2627 )?;
2628 Ok(ResidentGraphInFlight {
2629 execution,
2630 replay_metadata,
2631 _executor: PhantomData,
2632 })
2633 }
2634}
2635
2636fn resident_replay_recorder(
2637 owners: &ResidentRunOwners,
2638 executor: &Executor,
2639) -> std::result::Result<LaunchRecorder, ResidentGraphExecutionError> {
2640 let mut recorder = owners.execution_domain.new_strict_recorder();
2641 for relation in &owners.relations {
2642 let relation = relation
2643 .as_ref()
2644 .ok_or_else(|| runtime_error("resident graph relation was not restored"))?;
2645 ResidentScheduleSlotBinding::permanent(relation.buffer(), 0).record_uses(&mut recorder);
2646 }
2647 for name in &owners.source_binding_names {
2648 let source = executor
2649 .store
2650 .get(name)
2651 .ok_or_else(|| runtime_error(format!("resident source {name} was not restored")))?;
2652 for column in source.columns() {
2653 recorder.read_column(column);
2654 }
2655 recorder.read(source.num_rows_device());
2656 }
2657 ResidentScheduleExternalBindings::new(
2658 owners._filter_scratch.as_ref(),
2659 &owners._set_workspace,
2660 &owners._join_workspace,
2661 &owners._control,
2662 &owners._device_trace,
2663 &owners._schema_winners,
2664 &owners.receipt,
2665 )
2666 .record_uses(&mut recorder);
2667 owners._schedule_program.record_uses(&mut recorder);
2668 Ok(recorder)
2669}
2670
2671impl<'executor> ResidentGraphInFlight<'executor> {
2672 pub fn synchronize_core(
2674 mut self,
2675 ) -> std::result::Result<ResidentGraphSynchronized<'executor>, ResidentGraphExecutionError>
2676 {
2677 let capture_exclusion =
2678 reserve_uncaptured_stream(&self.execution.owners.stream).map_err(runtime_error)?;
2679 self.execution
2680 .completion
2681 .as_mut()
2682 .expect("resident completion event missing after launch")
2683 .synchronize()
2684 .map_err(runtime_error)?;
2685 drop(capture_exclusion);
2686 let elapsed_ms = self
2687 .execution
2688 .timing_start
2689 .as_ref()
2690 .expect("resident timing-start event missing after launch")
2691 .elapsed_ms(
2692 self.execution
2693 .timing_end
2694 .as_ref()
2695 .expect("resident timing-end event missing after launch"),
2696 )
2697 .map_err(|error| {
2698 runtime_error(format!("resident CUDA-event timing failed: {error}"))
2699 })?;
2700 if !elapsed_ms.is_finite() || elapsed_ms < 0.0 {
2701 return Err(runtime_error("resident CUDA-event timing was not finite"));
2702 }
2703 let device_elapsed_ns = (f64::from(elapsed_ms) * 1_000_000.0)
2704 .round()
2705 .clamp(0.0, u64::MAX as f64) as u64;
2706 Ok(ResidentGraphSynchronized {
2707 device_elapsed_ns,
2708 owners: self.execution.into_inner().owners,
2709 replay_metadata: self.replay_metadata,
2710 _executor: PhantomData,
2711 })
2712 }
2713}
2714
2715impl<'executor> ResidentGraphSynchronized<'executor> {
2716 pub fn observe_final_receipt(
2718 self,
2719 ) -> std::result::Result<ObservedResidentGraphReceipt, ResidentGraphExecutionError> {
2720 let phase_diagnostics = resolve_bool(None, "XLOG_RESIDENT_LATENCY_DIAGNOSTICS", false)
2721 .map_err(runtime_error)?;
2722 let receipt_d2h_started = phase_diagnostics.then(Instant::now);
2723 let encoded_len = self.owners.receipt.len_bytes();
2724 let pending =
2727 PendingResidentOwners::new(self.owners, |owners| owners.stream.synchronize().is_ok());
2728 let (pending, bytes) = pending.with_admission(
2729 |owners| reserve_uncaptured_stream(&owners.stream).map_err(runtime_error),
2730 |owners| {
2731 owners
2732 .provider
2733 .observe_resident_packed_receipt(
2734 &owners.receipt,
2735 &mut owners.pinned_receipt,
2736 &owners.stream,
2737 )
2738 .map_err(runtime_error)
2739 },
2740 )?;
2741 let mut owners = pending.into_inner();
2742 let receipt_d2h_ns = receipt_d2h_started
2743 .map(|started| u64::try_from(started.elapsed().as_nanos()).unwrap_or(u64::MAX))
2744 .unwrap_or(0);
2745 let decode_started = phase_diagnostics.then(Instant::now);
2746 let relation_count_len = owners.output_indices.len();
2747 let schema_winner_count = owners.output_schema_plans.len();
2748 let expected_count_fields = relation_count_len + 4 + schema_winner_count;
2749 if owners.receipt.relation_count_len() as usize != relation_count_len
2750 || owners.receipt.device_trace_field_count() != 4
2751 || owners.receipt.schema_winner_count() as usize != schema_winner_count
2752 || owners.receipt.total_count_field_len() as usize != expected_count_fields
2753 || bytes.len() != encoded_len
2754 || encoded_len != 44 + 4 * expected_count_fields
2755 {
2756 return Err(runtime_error(format!(
2757 "malformed resident receipt length: got {}, expected {}",
2758 bytes.len(),
2759 44 + 4 * expected_count_fields
2760 )));
2761 }
2762 let field_u32 = |offset| {
2763 read_receipt_u32(&bytes, offset)
2764 .ok_or_else(|| runtime_error("truncated resident receipt u32 field"))
2765 };
2766 let field_u64 = |offset| {
2767 read_receipt_u64(&bytes, offset)
2768 .ok_or_else(|| runtime_error("truncated resident receipt u64 field"))
2769 };
2770 let code = field_u32(0)?;
2771 let op_id = field_u32(4)?;
2772 let resource_code = field_u32(8)?;
2773 let iterations = field_u32(12)?;
2774 let limit = field_u32(16)?;
2775 let reserved = field_u32(20)?;
2776 let required = field_u64(24)?;
2777 let capacity = field_u64(32)?;
2778 let changed = field_u32(40)?;
2779 if reserved != 0 || changed > 1 {
2780 return Err(runtime_error(
2781 "malformed resident terminal status reserved/changed field",
2782 ));
2783 }
2784 let terminal = match code {
2785 value if value == ResidentTerminalCode::Success as u32 => {
2786 if resource_code != ResidentResourceCode::None as u32
2787 || limit != 0
2788 || required != 0
2789 || capacity != 0
2790 {
2791 return Err(runtime_error("malformed resident success payload"));
2792 }
2793 Ok(())
2794 }
2795 value if value == ResidentTerminalCode::IterationLimit as u32 => {
2796 if resource_code != ResidentResourceCode::None as u32
2797 || required != 0
2798 || capacity != 0
2799 || iterations > limit
2800 {
2801 return Err(runtime_error("malformed resident iteration-limit payload"));
2802 }
2803 Err(ResidentGraphExecutionError::IterationLimit {
2804 limit,
2805 completed: iterations,
2806 })
2807 }
2808 value if value == ResidentTerminalCode::CapacityOverflow as u32 => {
2809 if resource_code != ResidentResourceCode::OutputRows as u32 || required <= capacity
2810 {
2811 return Err(runtime_error(
2812 "malformed resident capacity-overflow payload",
2813 ));
2814 }
2815 Err(ResidentGraphExecutionError::CapacityOverflow {
2816 op_id,
2817 required,
2818 capacity,
2819 })
2820 }
2821 value if value == ResidentTerminalCode::ResourceExhausted as u32 => {
2822 let resource = match resource_code {
2823 value
2824 if value == ResidentResourceCode::SetHashSlots as u32
2825 || value == ResidentResourceCode::JoinBuckets as u32
2826 || value == ResidentResourceCode::JoinChains as u32 =>
2827 {
2828 "workspace_slots"
2829 }
2830 value if value == ResidentResourceCode::InputRows as u32 => "input_rows",
2831 value if value == ResidentResourceCode::OutputRows as u32 => "output_rows",
2832 _ => return Err(runtime_error("unknown resident resource code")),
2833 };
2834 if required <= capacity {
2835 return Err(runtime_error("malformed resident resource payload"));
2836 }
2837 Err(ResidentGraphExecutionError::ResourceExhausted {
2838 op_id,
2839 resource,
2840 required,
2841 capacity,
2842 })
2843 }
2844 _ => {
2845 return Err(runtime_error(format!(
2846 "unknown resident terminal code {code}"
2847 )))
2848 }
2849 };
2850
2851 let mut counts = Vec::with_capacity(owners.output_indices.len());
2852 for index in 0..owners.output_indices.len() {
2853 counts.push(field_u32(44 + index * 4)?);
2854 }
2855 let device_scan_invocations = u64::from(field_u32(44 + relation_count_len * 4)?);
2856 let device_filter_invocations = u64::from(field_u32(48 + relation_count_len * 4)?);
2857 let semantic_scan_invocations = u64::from(field_u32(52 + relation_count_len * 4)?);
2858 let semantic_filter_invocations = u64::from(field_u32(56 + relation_count_len * 4)?);
2859 let schema_winner_offset = 60 + relation_count_len * 4;
2860 let mut schema_winner_ids = Vec::with_capacity(schema_winner_count);
2861 for index in 0..schema_winner_count {
2862 schema_winner_ids.push(field_u32(schema_winner_offset + index * 4)?);
2863 }
2864 let mut outputs = Vec::new();
2865 if terminal.is_ok() {
2866 let selected_schemas =
2867 resident_resolve_output_schemas(&owners.output_schema_plans, &schema_winner_ids)
2868 .map_err(runtime_error)?;
2869 let mut cache_entries = Vec::with_capacity(counts.len());
2870 for ((_, relation_index), count) in
2871 owners.output_indices.iter().zip(counts.iter().copied())
2872 {
2873 let relation =
2874 private_relation(&owners.relations, *relation_index).map_err(runtime_error)?;
2875 if count > relation.capacity() {
2876 return Err(runtime_error(format!(
2877 "resident receipt count {count} exceeds output capacity {}",
2878 relation.capacity()
2879 )));
2880 }
2881 cache_entries.push((relation.buffer(), count));
2882 }
2883 owners
2884 .provider
2885 .finalize_resident_logical_counts(&cache_entries)
2886 .map_err(runtime_error)?;
2887 for ((name, relation_index), schema) in
2888 owners.output_indices.iter().zip(selected_schemas)
2889 {
2890 let relation = owners
2891 .relations
2892 .get_mut(*relation_index)
2893 .and_then(Option::take)
2894 .ok_or_else(|| runtime_error("resident output relation owner missing"))?;
2895 outputs.push(StagedResidentOutput {
2896 name: name.clone(),
2897 buffer: relation
2898 .into_buffer_with_observed_schema(schema)
2899 .map_err(runtime_error)?,
2900 });
2901 }
2902 }
2903
2904 let decode_schema_staging_ns = decode_started
2905 .map(|started| u64::try_from(started.elapsed().as_nanos()).unwrap_or(u64::MAX))
2906 .unwrap_or(0);
2907 let source_epoch = owners.source_epoch;
2908 let relation_registration = owners.relation_registration.clone();
2909 let transaction_identity = Arc::clone(&owners.transaction_identity);
2910 let provider = Arc::clone(&owners.provider);
2911 let prepared_for_reuse = terminal.is_ok().then_some(PreparedResidentGraph {
2912 owners,
2913 preflight_report: self.replay_metadata.preflight_report,
2914 has_device_status_writer: self.replay_metadata.has_device_status_writer,
2915 source_set_snapshots: self.replay_metadata.source_set_snapshots,
2916 source_buffers_to_restore: Vec::new(),
2917 prepare_diagnostic: self.replay_metadata.prepare_diagnostic,
2918 });
2919 Ok(ObservedResidentGraphReceipt {
2920 encoded_len,
2921 device_elapsed_ns: self.device_elapsed_ns,
2922 device_scan_invocations,
2923 device_filter_invocations,
2924 semantic_scan_invocations,
2925 semantic_filter_invocations,
2926 iterations,
2927 terminal,
2928 outputs,
2929 source_epoch,
2930 relation_registration,
2931 transaction_identity,
2932 provider,
2933 phase_timings: phase_diagnostics.then_some(ResidentFinalObservationPhaseTimings {
2934 receipt_d2h_ns,
2935 decode_schema_staging_ns,
2936 }),
2937 prepared_for_reuse,
2938 })
2939 }
2940}
2941
2942impl ObservedResidentGraphReceipt {
2943 pub fn encoded_len(&self) -> usize {
2945 self.encoded_len
2946 }
2947
2948 #[doc(hidden)]
2950 pub fn phase_timings(&self) -> Option<ResidentFinalObservationPhaseTimings> {
2951 self.phase_timings
2952 }
2953
2954 #[doc(hidden)]
2956 pub fn relation_registration_count(&self) -> usize {
2957 self.relation_registration.len()
2958 }
2959
2960 pub fn device_elapsed_ns(&self) -> u64 {
2962 self.device_elapsed_ns
2963 }
2964
2965 pub fn device_scan_invocations(&self) -> u64 {
2967 self.device_scan_invocations
2968 }
2969
2970 pub fn device_filter_invocations(&self) -> u64 {
2972 self.device_filter_invocations
2973 }
2974
2975 pub fn semantic_scan_invocations(&self) -> u64 {
2977 self.semantic_scan_invocations
2978 }
2979
2980 pub fn semantic_filter_invocations(&self) -> u64 {
2982 self.semantic_filter_invocations
2983 }
2984
2985 pub fn staged_output_count(&self) -> u64 {
2987 self.outputs.len() as u64
2988 }
2989
2990 pub fn iterations(&self) -> u32 {
2992 self.iterations
2993 }
2994
2995 pub fn commit(
2997 self,
2998 executor: &mut Executor,
2999 ) -> std::result::Result<(), ResidentGraphExecutionError> {
3000 self.commit_inner(executor).map(|_| ())
3001 }
3002
3003 pub fn commit_for_reuse(
3005 self,
3006 executor: &mut Executor,
3007 ) -> std::result::Result<PreparedResidentGraph, ResidentGraphExecutionError> {
3008 self.commit_inner(executor)?.ok_or_else(|| {
3009 runtime_error("successful resident receipt did not retain its graph owner")
3010 })
3011 }
3012
3013 fn commit_inner(
3014 mut self,
3015 executor: &mut Executor,
3016 ) -> std::result::Result<Option<PreparedResidentGraph>, ResidentGraphExecutionError> {
3017 self.terminal?;
3018 if !Arc::ptr_eq(&self.transaction_identity, &executor.transaction_identity)
3019 || !Arc::ptr_eq(&self.provider, &executor.provider)
3020 || self.source_epoch != executor.store.mutation_epoch()
3021 {
3022 return Err(ResidentGraphExecutionError::Runtime(
3023 "resident transaction became stale before commit".into(),
3024 ));
3025 }
3026 let mut current_registration = executor
3027 .rel_names
3028 .iter()
3029 .map(|(rel, name)| (*rel, name.clone()))
3030 .collect::<Vec<_>>();
3031 current_registration.sort_by_key(|(rel, name)| (rel.0, name.clone()));
3032 if current_registration != self.relation_registration {
3033 return Err(ResidentGraphExecutionError::Runtime(
3034 "resident relation registration changed before commit".into(),
3035 ));
3036 }
3037 let additional = self
3038 .outputs
3039 .iter()
3040 .filter(|output| !executor.store.contains(&output.name))
3041 .count();
3042 executor
3043 .store
3044 .try_reserve_relations(additional)
3045 .map_err(runtime_error)?;
3046 executor.common_subexpression_cache.clear();
3047 for output in self.outputs.drain(..) {
3048 if let Some(&rel) = executor.name_to_rel.get(&output.name) {
3049 executor.join_index_cache.invalidate_rel(rel);
3050 }
3051 if let Some(prepared) = self.prepared_for_reuse.as_mut() {
3052 if prepared
3053 .source_set_snapshots
3054 .iter()
3055 .any(|source| source.name == output.name)
3056 {
3057 let source = executor.store.remove(&output.name).ok_or_else(|| {
3058 runtime_error(format!(
3059 "resident source {} disappeared before output publication",
3060 output.name
3061 ))
3062 })?;
3063 prepared
3064 .source_buffers_to_restore
3065 .push((output.name.clone(), source));
3066 }
3067 }
3068 executor.store.put_owned(output.name, output.buffer);
3069 }
3070 Ok(self.prepared_for_reuse.take())
3071 }
3072}
3073
3074fn runtime_error(error: impl std::fmt::Display) -> ResidentGraphExecutionError {
3075 ResidentGraphExecutionError::Runtime(error.to_string())
3076}
3077
3078impl ResidentBuild<'_> {
3079 fn source_reference(&mut self, name: &str) -> Result<ResidentBufferRef> {
3080 if let Some(index) = self.source_aliases.get(name) {
3081 return Ok(ResidentBufferRef::Private(*index));
3082 }
3083 let (row_count, count_tracked, columns_tracked, schema) = {
3084 let source = self.executor.store.get(name).ok_or_else(|| {
3085 XlogError::Execution(format!(
3086 "resident source {name} disappeared during planning"
3087 ))
3088 })?;
3089 (
3090 resident_source_logical_count(source.cached_row_count())?,
3091 source.num_rows_device().runtime_block().is_some(),
3092 source
3093 .columns()
3094 .iter()
3095 .all(|column| column.runtime_block().is_some()),
3096 source.schema().clone(),
3097 )
3098 };
3099 match resident_source_binding_route(row_count, count_tracked, columns_tracked)? {
3100 ResidentSourceBindingRoute::Direct => {
3101 self.source_names.insert(name.to_owned());
3102 Ok(ResidentBufferRef::Source(name.to_owned()))
3103 }
3104 ResidentSourceBindingRoute::NormalizeEmpty => {
3105 let index = self.allocate_permanent_relation(schema, 0)?;
3106 self.source_aliases.insert(name.to_owned(), index);
3107 Ok(ResidentBufferRef::Private(index))
3108 }
3109 }
3110 }
3111
3112 fn private(&self, index: usize) -> &ResidentLogicalRelation {
3113 &self.relations[index]
3114 }
3115
3116 fn schema(&self, reference: &ResidentBufferRef) -> Result<&Schema> {
3117 match reference {
3118 ResidentBufferRef::Source(name) => self
3119 .executor
3120 .store
3121 .get(name)
3122 .map(CudaBuffer::schema)
3123 .ok_or_else(|| {
3124 XlogError::Execution(format!("missing resident source relation {name}"))
3125 }),
3126 ResidentBufferRef::Private(index) => Ok(&self.private(*index).schema),
3127 }
3128 }
3129
3130 fn allocate_relation(
3131 &mut self,
3132 schema: Schema,
3133 initial_count: u32,
3134 permanent: bool,
3135 ) -> Result<usize> {
3136 if initial_count > 1 {
3137 return Err(XlogError::Execution(
3138 "resident logical initial count must be zero or one".into(),
3139 ));
3140 }
3141 let index = self.relations.len();
3142 self.relations.push(ResidentLogicalRelation {
3143 schema,
3144 initial_count,
3145 permanent,
3146 });
3147 Ok(index)
3148 }
3149
3150 fn allocate_permanent_relation(&mut self, schema: Schema, initial_count: u32) -> Result<usize> {
3151 self.allocate_relation(schema, initial_count, true)
3152 }
3153
3154 fn allocate_scratch_relation(&mut self, schema: Schema) -> Result<usize> {
3155 self.allocate_relation(schema, 0, false)
3156 }
3157
3158 fn allocation_manifest(
3159 &self,
3160 initial_ops: &[ResidentRecordedOp],
3161 phases: &[ResidentCapturePhase],
3162 ) -> Result<ResidentAllocationManifest> {
3163 let mut definitions = vec![None; self.relations.len()];
3164 let mut last_uses = vec![None; self.relations.len()];
3165 let mut ordinal = 0usize;
3166 let mut phase_ranges = Vec::with_capacity(phases.len() + 1);
3167 phase_ranges.push(resident_record_lifetimes(
3168 initial_ops,
3169 &self.relations,
3170 &mut definitions,
3171 &mut last_uses,
3172 &mut ordinal,
3173 )?);
3174 for phase in phases {
3175 let ops = match phase {
3176 ResidentCapturePhase::Segment { ops, .. }
3177 | ResidentCapturePhase::ConditionalWhile { ops, .. } => ops,
3178 };
3179 phase_ranges.push(resident_record_lifetimes(
3180 ops,
3181 &self.relations,
3182 &mut definitions,
3183 &mut last_uses,
3184 &mut ordinal,
3185 )?);
3186 }
3187
3188 let mut slots = Vec::<ResidentPhysicalSlotPlan>::new();
3189 let mut logical_to_slot = vec![
3190 ResidentSlotAssignment {
3191 slot: usize::MAX,
3192 generation: 0,
3193 };
3194 self.relations.len()
3195 ];
3196 for (logical, relation) in self.relations.iter().enumerate() {
3197 if relation.permanent {
3198 let slot = slots.len();
3199 slots.push(ResidentPhysicalSlotPlan {
3200 schema: relation.schema.clone(),
3201 initial_count: relation.initial_count,
3202 permanent: true,
3203 });
3204 logical_to_slot[logical] = ResidentSlotAssignment {
3205 slot,
3206 generation: 0,
3207 };
3208 }
3209 }
3210
3211 let mut scratch_relations = self
3212 .relations
3213 .iter()
3214 .enumerate()
3215 .filter(|(_, relation)| !relation.permanent)
3216 .map(|(logical, relation)| {
3217 let definition = definitions[logical].ok_or_else(|| {
3218 XlogError::Execution(format!(
3219 "resident scratch relation {logical} is never defined"
3220 ))
3221 })?;
3222 let last_use = last_uses[logical].unwrap_or(definition);
3223 if !phase_ranges
3224 .iter()
3225 .any(|(start, end)| *start <= definition && last_use < *end)
3226 {
3227 return Err(XlogError::Execution(format!(
3228 "resident scratch relation {logical} crosses a capture phase boundary"
3229 )));
3230 }
3231 Ok((
3232 logical,
3233 definition,
3234 last_use,
3235 resident_schema_layout(&relation.schema),
3236 ))
3237 })
3238 .collect::<Result<Vec<_>>>()?;
3239 scratch_relations.sort_by_key(|(logical, definition, _, _)| (*definition, *logical));
3240
3241 let mut scratch_slots = Vec::<ResidentScratchSlotState>::new();
3242 for (logical, definition, last_use, layout) in scratch_relations {
3243 let reusable = scratch_slots
3244 .iter_mut()
3245 .filter(|slot| slot.layout == layout && slot.last_use < definition)
3246 .min_by_key(|slot| slot.last_use);
3247 let assignment = if let Some(slot) = reusable {
3248 slot.generation = slot.generation.checked_add(1).ok_or_else(|| {
3249 XlogError::Execution("resident scratch generation overflow".into())
3250 })?;
3251 slot.last_use = last_use;
3252 ResidentSlotAssignment {
3253 slot: slot.slot,
3254 generation: slot.generation,
3255 }
3256 } else {
3257 let slot_index = slots.len();
3258 slots.push(ResidentPhysicalSlotPlan {
3259 schema: self.relations[logical].schema.clone(),
3260 initial_count: 0,
3261 permanent: false,
3262 });
3263 scratch_slots.push(ResidentScratchSlotState {
3264 slot: slot_index,
3265 layout,
3266 last_use,
3267 generation: 0,
3268 });
3269 ResidentSlotAssignment {
3270 slot: slot_index,
3271 generation: 0,
3272 }
3273 };
3274 logical_to_slot[logical] = assignment;
3275 }
3276 if logical_to_slot
3277 .iter()
3278 .any(|assignment| assignment.slot == usize::MAX)
3279 {
3280 return Err(XlogError::Execution(
3281 "resident logical relation has no physical slot assignment".into(),
3282 ));
3283 }
3284 resident_validate_slot_assignments(
3285 &self.relations,
3286 &definitions,
3287 &last_uses,
3288 &slots,
3289 &logical_to_slot,
3290 )?;
3291
3292 let capacity = u64::from(self.capacity);
3293 let mut relation_bytes = 0u64;
3294 let mut max_row_bytes = 1u64;
3295 for slot in &slots {
3296 max_row_bytes = max_row_bytes.max(slot.schema.row_size_bytes() as u64);
3297 resident_checked_add(
3298 &mut relation_bytes,
3299 resident_relation_device_bytes(&slot.schema, capacity)?,
3300 "relation",
3301 )?;
3302 }
3303 let filter_scratch_bytes = if self.filter_workspaces.is_empty() {
3304 0
3305 } else {
3306 resident_filter_scratch_device_bytes(capacity)?
3307 };
3308 let set_candidate_capacity = capacity.checked_mul(2).ok_or_else(|| {
3309 XlogError::Execution("resident set candidate capacity overflow".into())
3310 })?;
3311 let mut fixed_workspace_bytes = 0u64;
3312 for bytes in [
3313 resident_set_workspace_device_bytes(set_candidate_capacity)?,
3314 resident_join_workspace_device_bytes(capacity)?,
3315 resident_control_device_bytes(),
3316 resident_device_trace_bytes(),
3317 resident_schema_winners_device_bytes(self.heads.len())?,
3318 resident_packed_receipt_with_schema_winners_device_bytes(self.heads.len())?,
3319 ] {
3320 resident_checked_add(&mut fixed_workspace_bytes, bytes, "fixed workspace")?;
3321 }
3322 let mut required_bytes = 0u64;
3323 for bytes in [relation_bytes, filter_scratch_bytes, fixed_workspace_bytes] {
3324 resident_checked_add(&mut required_bytes, bytes, "manifest")?;
3325 }
3326
3327 let permanent_slots = u32::try_from(slots.iter().filter(|slot| slot.permanent).count())
3328 .map_err(|_| {
3329 XlogError::Execution("resident permanent slot count exceeds u32".into())
3330 })?;
3331 Ok(ResidentAllocationManifest {
3332 slots,
3333 logical_to_slot,
3334 required_bytes,
3335 relation_bytes,
3336 filter_scratch_bytes,
3337 schedule_metadata_bytes: 0,
3338 fixed_workspace_bytes,
3339 logical_relation_values: self.relations.len(),
3340 permanent_slots,
3341 scratch_slots: u32::try_from(scratch_slots.len()).map_err(|_| {
3342 XlogError::Execution("resident scratch slot count exceeds u32".into())
3343 })?,
3344 filter_scratch_allocations: u32::from(!self.filter_workspaces.is_empty()),
3345 max_row_bytes,
3346 })
3347 }
3348
3349 fn materialize(
3350 &self,
3351 manifest: &ResidentAllocationManifest,
3352 reservation: &mut GpuMemoryReservation,
3353 mut diagnostics: Option<&mut ResidentPrepareDiagnostics>,
3354 ) -> Result<ResidentPhysicalBuild> {
3355 let capacity = u64::from(self.capacity);
3356 let allocation_bytes_before = diagnostics.as_ref().map(|_| reservation.used_bytes());
3357 let allocation_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
3358 let relations = self
3359 .executor
3360 .provider
3361 .prepare_resident_relations_in_reservation(
3362 manifest.slots.iter().map(|slot| &slot.schema),
3363 capacity,
3364 reservation,
3365 )?
3366 .into_iter()
3367 .map(Some)
3368 .collect();
3369 if let Some(diagnostics) = diagnostics.as_deref_mut() {
3370 let allocation_ns = resident_prepare_elapsed_ns(
3371 allocation_started.expect("diagnostic timer exists when enabled"),
3372 );
3373 let allocation_bytes = reservation.used_bytes().saturating_sub(
3374 allocation_bytes_before.expect("diagnostic byte snapshot exists when enabled"),
3375 );
3376 diagnostics.relation_slot_allocation_ns = diagnostics
3377 .relation_slot_allocation_ns
3378 .saturating_add(allocation_ns);
3379 diagnostics.relation_slot_allocation_ns_max = diagnostics
3380 .relation_slot_allocation_ns_max
3381 .max(allocation_ns);
3382 diagnostics.relation_slot_allocation_bytes = diagnostics
3383 .relation_slot_allocation_bytes
3384 .saturating_add(allocation_bytes);
3385 diagnostics.relation_device_allocation_calls = diagnostics
3386 .relation_device_allocation_calls
3387 .saturating_add(u64::from(!manifest.slots.is_empty()));
3388 }
3389 let filter_scratch = if self.filter_workspaces.is_empty() {
3390 None
3391 } else {
3392 let bytes_before = diagnostics.as_ref().map(|_| reservation.used_bytes());
3393 let started = diagnostics.as_ref().map(|_| std::time::Instant::now());
3394 let scratch = self
3395 .executor
3396 .provider
3397 .prepare_resident_filter_scratch_in_reservation(capacity, reservation)?;
3398 if let Some(diagnostics) = diagnostics.as_deref_mut() {
3399 let elapsed_ns = resident_prepare_elapsed_ns(
3400 started.expect("diagnostic timer exists when enabled"),
3401 );
3402 let reserved_bytes = reservation.used_bytes().saturating_sub(
3403 bytes_before.expect("diagnostic byte snapshot exists when enabled"),
3404 );
3405 diagnostics.workspace_provider_calls =
3406 diagnostics.workspace_provider_calls.saturating_add(1);
3407 diagnostics.filter_scratch_allocation_ns = elapsed_ns;
3408 diagnostics.filter_scratch_allocation_bytes = reserved_bytes;
3409 }
3410 Some(scratch)
3411 };
3412 let set_candidate_capacity = capacity.checked_mul(2).ok_or_else(|| {
3413 XlogError::Execution("resident set candidate capacity overflow".into())
3414 })?;
3415 let set_bytes_before = diagnostics.as_ref().map(|_| reservation.used_bytes());
3416 let set_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
3417 let set_workspace = self
3418 .executor
3419 .provider
3420 .prepare_resident_set_workspace_in_reservation(set_candidate_capacity, reservation)?;
3421 if let Some(diagnostics) = diagnostics.as_mut() {
3422 let set_ns = resident_prepare_elapsed_ns(
3423 set_started.expect("diagnostic timer exists when enabled"),
3424 );
3425 let set_reserved_bytes = reservation.used_bytes().saturating_sub(
3426 set_bytes_before.expect("diagnostic byte snapshot exists when enabled"),
3427 );
3428 diagnostics.workspace_provider_calls =
3429 diagnostics.workspace_provider_calls.saturating_add(1);
3430 diagnostics.set_workspace_allocation_ns = set_ns;
3431 diagnostics.set_workspace_allocation_bytes = set_reserved_bytes;
3432 }
3433 let join_bytes_before = diagnostics.as_ref().map(|_| reservation.used_bytes());
3434 let join_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
3435 let join_workspace = self
3436 .executor
3437 .provider
3438 .prepare_resident_join_workspace_in_reservation(capacity, reservation)?;
3439 if let Some(diagnostics) = diagnostics.as_deref_mut() {
3440 let join_ns = resident_prepare_elapsed_ns(
3441 join_started.expect("diagnostic timer exists when enabled"),
3442 );
3443 let join_reserved_bytes = reservation.used_bytes().saturating_sub(
3444 join_bytes_before.expect("diagnostic byte snapshot exists when enabled"),
3445 );
3446 diagnostics.workspace_provider_calls =
3447 diagnostics.workspace_provider_calls.saturating_add(1);
3448 diagnostics.join_workspace_allocation_ns = join_ns;
3449 diagnostics.join_workspace_allocation_bytes = join_reserved_bytes;
3450 }
3451 let control_bytes_before = diagnostics.as_ref().map(|_| reservation.used_bytes());
3452 let control_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
3453 let control = self
3454 .executor
3455 .provider
3456 .prepare_resident_convergence_control_in_reservation(reservation)?;
3457 if let Some(diagnostics) = diagnostics {
3458 let control_ns = resident_prepare_elapsed_ns(
3459 control_started.expect("diagnostic timer exists when enabled"),
3460 );
3461 let control_reserved_bytes = reservation.used_bytes().saturating_sub(
3462 control_bytes_before.expect("diagnostic byte snapshot exists when enabled"),
3463 );
3464 diagnostics.workspace_provider_calls =
3465 diagnostics.workspace_provider_calls.saturating_add(1);
3466 diagnostics.control_allocation_ns = control_ns;
3467 diagnostics.control_allocation_bytes = control_reserved_bytes;
3468 }
3469 Ok(ResidentPhysicalBuild {
3470 relations,
3471 filter_scratch,
3472 set_workspace,
3473 join_workspace,
3474 control,
3475 })
3476 }
3477
3478 fn next_op_id(&mut self) -> Result<u32> {
3479 let op_id = self.next_op_id;
3480 self.next_op_id = self
3481 .next_op_id
3482 .checked_add(1)
3483 .ok_or_else(|| XlogError::Execution("resident physical op id overflow".into()))?;
3484 Ok(op_id)
3485 }
3486
3487 fn push_physical_op(
3488 &mut self,
3489 ops: &mut Vec<ResidentRecordedOp>,
3490 op: ResidentRecordedOp,
3491 op_id: u32,
3492 ) {
3493 ops.push(op);
3494 if !self.injection_recorded
3495 && self
3496 .injection
3497 .as_ref()
3498 .is_some_and(|injection| injection.after_op == op_id)
3499 {
3500 let status =
3501 terminal_status_for_injection(&self.injection.as_ref().expect("checked").status);
3502 ops.push(ResidentRecordedOp::TestStatus(status));
3503 self.injection_recorded = true;
3504 }
3505 }
3506
3507 fn scan_reference(
3508 &mut self,
3509 rel: RelId,
3510 override_scan: Option<(RelId, usize, usize)>,
3511 occurrences: &mut HashMap<RelId, usize>,
3512 ) -> Result<ResidentBufferRef> {
3513 let occurrence = occurrences.entry(rel).or_insert(0);
3514 let current = *occurrence;
3515 *occurrence += 1;
3516 if let Some((target, target_occurrence, delta)) = override_scan {
3517 if rel == target && current == target_occurrence {
3518 return Ok(ResidentBufferRef::Private(delta));
3519 }
3520 }
3521 let name = self
3522 .executor
3523 .rel_names
3524 .get(&rel)
3525 .ok_or_else(|| XlogError::Execution(format!("unknown resident relation id {rel:?}")))?
3526 .clone();
3527 if let Some(index) = self.heads.get(&name) {
3528 Ok(ResidentBufferRef::Private(*index))
3529 } else {
3530 self.source_reference(&name)
3531 }
3532 }
3533
3534 fn plan_node(
3535 &mut self,
3536 node: &RirNode,
3537 override_scan: Option<(RelId, usize, usize)>,
3538 occurrences: &mut HashMap<RelId, usize>,
3539 ops: &mut Vec<ResidentRecordedOp>,
3540 ) -> Result<ResidentBufferRef> {
3541 match node {
3542 RirNode::Unit => {
3543 let op_id = self.next_op_id()?;
3544 let (reference, op) = resident_new_phase_unit(&mut self.relations, op_id)?;
3545 self.push_physical_op(ops, op, op_id);
3546 Ok(reference)
3547 }
3548 RirNode::Scan { rel } => {
3549 let reference = self.scan_reference(*rel, override_scan, occurrences)?;
3550 let op_id = self.next_op_id()?;
3551 Ok(resident_record_scan_leaf(
3552 reference,
3553 op_id,
3554 resident_semantic_trace_guard(override_scan),
3555 ops,
3556 |ops, op, op_id| self.push_physical_op(ops, op, op_id),
3557 ))
3558 }
3559 RirNode::Filter { input, predicate } => {
3560 let input_ref = self.plan_node(input, override_scan, occurrences, ops)?;
3561 let input_schema = self.schema(&input_ref)?.clone();
3562 let compact_comparisons =
3563 resident_compact_filter_descriptors(predicate, &input_schema)?;
3564 let workspace = ResidentFilterPlan {
3565 compact_comparisons,
3566 };
3567 let workspace_index = self.filter_workspaces.len();
3568 self.filter_workspaces.push(workspace);
3569 let schema = self
3570 .certificate
3571 .node_schema(node)
3572 .ok_or_else(|| XlogError::Execution("resident filter schema missing".into()))?;
3573 let output = self.allocate_scratch_relation(schema)?;
3574 let op_id = self.next_op_id()?;
3575 self.push_physical_op(
3576 ops,
3577 ResidentRecordedOp::Filter {
3578 input: input_ref,
3579 output,
3580 workspace: workspace_index,
3581 op_id,
3582 },
3583 op_id,
3584 );
3585 ops.push(ResidentRecordedOp::TraceDelta {
3586 scan_delta: 0,
3587 filter_delta: 1,
3588 semantic_guard: resident_semantic_trace_guard(override_scan),
3589 });
3590 Ok(ResidentBufferRef::Private(output))
3591 }
3592 RirNode::Project { input, columns } => {
3593 let input_ref = self.plan_node(input, override_scan, occurrences, ops)?;
3594 let input_schema = self.schema(&input_ref)?.clone();
3595 let certified_schema = self.certificate.node_schema(node).ok_or_else(|| {
3596 XlogError::Execution("resident project schema missing".into())
3597 })?;
3598 let schema = self.executor.project_schema(&input_schema, columns)?;
3599 if schema.arity() != certified_schema.arity()
3600 || (0..schema.arity()).any(|column| {
3601 schema.column_type(column) != certified_schema.column_type(column)
3602 })
3603 {
3604 return Err(XlogError::Execution(
3605 "resident project physical schema differs from certificate".into(),
3606 ));
3607 }
3608 let compact_expressions =
3609 resident_compact_project_descriptors(columns, &input_schema, &schema)?;
3610 let workspace = ResidentProjectPlan {
3611 compact_expressions,
3612 };
3613 let workspace_index = self.project_workspaces.len();
3614 self.project_workspaces.push(workspace);
3615 let output = self.allocate_scratch_relation(schema)?;
3616 let op_id = self.next_op_id()?;
3617 self.push_physical_op(
3618 ops,
3619 ResidentRecordedOp::Project {
3620 input: input_ref,
3621 output,
3622 workspace: workspace_index,
3623 op_id,
3624 },
3625 op_id,
3626 );
3627 Ok(ResidentBufferRef::Private(output))
3628 }
3629 RirNode::Join {
3630 left,
3631 right,
3632 left_keys,
3633 right_keys,
3634 join_type,
3635 } => {
3636 let left_ref = self.plan_node(left, override_scan, occurrences, ops)?;
3637 let right_ref = self.plan_node(right, override_scan, occurrences, ops)?;
3638 let certified_schema = self
3639 .certificate
3640 .node_schema(node)
3641 .ok_or_else(|| XlogError::Execution("resident join schema missing".into()))?;
3642 let kind = match join_type {
3643 JoinType::Inner => ResidentJoinKind::Inner,
3644 JoinType::Semi => ResidentJoinKind::Semi,
3645 _ => return Err(XlogError::Execution("uncertified resident join".into())),
3646 };
3647 let left_schema = self.schema(&left_ref)?.clone();
3652 let right_schema = self.schema(&right_ref)?.clone();
3653 let schema = match kind {
3654 ResidentJoinKind::Semi => left_schema,
3655 ResidentJoinKind::Inner => {
3656 let mut columns = left_schema.columns;
3657 columns.extend(right_schema.columns);
3658 Schema::new(columns)
3659 }
3660 };
3661 if schema.arity() != certified_schema.arity()
3662 || (0..schema.arity()).any(|column| {
3663 schema.column_type(column) != certified_schema.column_type(column)
3664 })
3665 {
3666 return Err(XlogError::Execution(
3667 "resident join physical schema differs from certificate".into(),
3668 ));
3669 }
3670 let output = self.allocate_scratch_relation(schema).map_err(|error| {
3671 XlogError::Execution(format!(
3672 "resident join intermediate arity {} at {node:?} could not be allocated: {error}",
3673 certified_schema.arity()
3674 ))
3675 })?;
3676 let op_id = self.next_op_id()?;
3677 self.push_physical_op(
3678 ops,
3679 ResidentRecordedOp::Join {
3680 kind,
3681 left: left_ref,
3682 left_key: left_keys[0],
3683 right: right_ref,
3684 right_key: right_keys[0],
3685 output,
3686 op_id,
3687 },
3688 op_id,
3689 );
3690 Ok(ResidentBufferRef::Private(output))
3691 }
3692 RirNode::ChainJoin { fallback, .. } | RirNode::MultiWayJoin { fallback, .. } => {
3693 self.plan_node(fallback, override_scan, occurrences, ops)
3694 }
3695 RirNode::Distinct { input, .. } => {
3696 let input_ref = self.plan_node(input, override_scan, occurrences, ops)?;
3697 self.plan_dedup(input_ref, node, ops)
3698 }
3699 RirNode::Diff { left, right } => {
3700 let left_ref = self.plan_node(left, override_scan, occurrences, ops)?;
3701 let right_ref = self.plan_node(right, override_scan, occurrences, ops)?;
3702 self.plan_diff(left_ref, right_ref, node, ops)
3703 }
3704 RirNode::Union { inputs } => {
3705 let fold_mode = resident_union_fold_mode(inputs.len())?;
3706 let mut iter = inputs.iter();
3707 let first = iter
3708 .next()
3709 .expect("union fold mode rejected the empty case");
3710 let mut result = self.plan_node(first, override_scan, occurrences, ops)?;
3711 if fold_mode == ResidentUnionFoldMode::SelfUnion {
3712 return self.plan_union(result.clone(), result, node, ops);
3713 }
3714 for input in iter {
3715 let right = self.plan_node(input, override_scan, occurrences, ops)?;
3716 result = self.plan_union(result, right, node, ops)?;
3717 }
3718 Ok(result)
3719 }
3720 RirNode::Fixpoint { .. }
3721 | RirNode::GroupBy { .. }
3722 | RirNode::TensorMaskedJoin { .. } => Err(XlogError::Execution(
3723 "uncertified node reached resident planner".into(),
3724 )),
3725 }
3726 }
3727
3728 fn plan_dedup(
3729 &mut self,
3730 input: ResidentBufferRef,
3731 node: &RirNode,
3732 ops: &mut Vec<ResidentRecordedOp>,
3733 ) -> Result<ResidentBufferRef> {
3734 let schema = self
3735 .certificate
3736 .node_schema(node)
3737 .ok_or_else(|| XlogError::Execution("resident dedup schema missing".into()))?;
3738 let output = self.allocate_scratch_relation(schema)?;
3739 let op_id = self.next_op_id()?;
3741 self.push_physical_op(
3742 ops,
3743 ResidentRecordedOp::Union {
3744 left: input.clone(),
3745 right: input,
3746 output,
3747 op_id,
3748 },
3749 op_id,
3750 );
3751 Ok(ResidentBufferRef::Private(output))
3752 }
3753
3754 fn plan_union(
3755 &mut self,
3756 left: ResidentBufferRef,
3757 right: ResidentBufferRef,
3758 schema_node: &RirNode,
3759 ops: &mut Vec<ResidentRecordedOp>,
3760 ) -> Result<ResidentBufferRef> {
3761 let schema = self
3762 .certificate
3763 .node_schema(schema_node)
3764 .ok_or_else(|| XlogError::Execution("resident union schema missing".into()))?;
3765 self.plan_union_schema(left, right, schema, ops)
3766 }
3767
3768 fn plan_union_schema(
3769 &mut self,
3770 left: ResidentBufferRef,
3771 right: ResidentBufferRef,
3772 schema: Schema,
3773 ops: &mut Vec<ResidentRecordedOp>,
3774 ) -> Result<ResidentBufferRef> {
3775 let output = self.allocate_scratch_relation(schema)?;
3776 let op_id = self.next_op_id()?;
3777 self.push_physical_op(
3778 ops,
3779 ResidentRecordedOp::Union {
3780 left,
3781 right,
3782 output,
3783 op_id,
3784 },
3785 op_id,
3786 );
3787 Ok(ResidentBufferRef::Private(output))
3788 }
3789
3790 fn plan_diff(
3791 &mut self,
3792 left: ResidentBufferRef,
3793 right: ResidentBufferRef,
3794 schema_node: &RirNode,
3795 ops: &mut Vec<ResidentRecordedOp>,
3796 ) -> Result<ResidentBufferRef> {
3797 let schema = self
3798 .certificate
3799 .node_schema(schema_node)
3800 .ok_or_else(|| XlogError::Execution("resident diff schema missing".into()))?;
3801 self.plan_diff_schema(left, right, schema, ops)
3802 }
3803
3804 fn plan_diff_schema(
3805 &mut self,
3806 left: ResidentBufferRef,
3807 right: ResidentBufferRef,
3808 schema: Schema,
3809 ops: &mut Vec<ResidentRecordedOp>,
3810 ) -> Result<ResidentBufferRef> {
3811 let output = self.allocate_scratch_relation(schema)?;
3812 let op_id = self.next_op_id()?;
3813 self.push_physical_op(
3814 ops,
3815 ResidentRecordedOp::Diff {
3816 left,
3817 right,
3818 output,
3819 op_id,
3820 },
3821 op_id,
3822 );
3823 Ok(ResidentBufferRef::Private(output))
3824 }
3825
3826 fn plan_copy(
3827 &mut self,
3828 input: ResidentBufferRef,
3829 output: usize,
3830 schema: &Schema,
3831 ops: &mut Vec<ResidentRecordedOp>,
3832 ) -> Result<()> {
3833 let compact_expressions = (0..schema.arity())
3834 .map(|column| {
3835 let scalar = schema.column_type(column).ok_or_else(|| {
3836 XlogError::Execution("resident identity project column is missing".into())
3837 })?;
3838 Ok(ResidentProjectExpressionDescriptor::column(
3839 u32::try_from(column).map_err(|_| {
3840 XlogError::Execution("resident project column exceeds u32".into())
3841 })?,
3842 u32::try_from(scalar.size_bytes()).map_err(|_| {
3843 XlogError::Execution("resident project scalar width exceeds u32".into())
3844 })?,
3845 ))
3846 })
3847 .collect::<Result<Vec<_>>>()?;
3848 let workspace = ResidentProjectPlan {
3849 compact_expressions,
3850 };
3851 let workspace_index = self.project_workspaces.len();
3852 self.project_workspaces.push(workspace);
3853 let op_id = self.next_op_id()?;
3854 self.push_physical_op(
3855 ops,
3856 ResidentRecordedOp::Project {
3857 input,
3858 output,
3859 workspace: workspace_index,
3860 op_id,
3861 },
3862 op_id,
3863 );
3864 Ok(())
3865 }
3866
3867 fn normalize_to_schema(
3868 &mut self,
3869 input: ResidentBufferRef,
3870 schema: &Schema,
3871 ops: &mut Vec<ResidentRecordedOp>,
3872 ) -> Result<ResidentBufferRef> {
3873 let input_schema = self.schema(&input)?;
3874 if input_schema == schema {
3875 return Ok(input);
3876 }
3877 if input_schema.arity() != schema.arity()
3878 || input_schema
3879 .columns
3880 .iter()
3881 .zip(&schema.columns)
3882 .any(|(left, right)| left.1 != right.1)
3883 {
3884 return Err(XlogError::Execution(format!(
3885 "resident rule output schema {:?} cannot be normalized to head schema {:?}",
3886 input_schema, schema
3887 )));
3888 }
3889 let output = self.allocate_scratch_relation(schema.clone())?;
3890 self.plan_copy(input, output, schema, ops)?;
3891 Ok(ResidentBufferRef::Private(output))
3892 }
3893
3894 fn merge_phase_contribution(
3895 &mut self,
3896 current: Option<ResidentBufferRef>,
3897 contribution: ResidentBufferRef,
3898 schema: &Schema,
3899 ops: &mut Vec<ResidentRecordedOp>,
3900 ) -> Result<ResidentBufferRef> {
3901 let contribution = self.normalize_to_schema(contribution, schema, ops)?;
3902 resident_phase_merge(current, contribution, |step| match step {
3903 ResidentPhaseMergeStep::Deduplicate(input) => {
3904 self.plan_union_schema(input.clone(), input, schema.clone(), ops)
3905 }
3906 ResidentPhaseMergeStep::Union(left, right) => {
3907 self.plan_union_schema(left, right, schema.clone(), ops)
3908 }
3909 })
3910 }
3911}
3912
3913fn private_relation(
3914 relations: &[Option<ResidentRelation>],
3915 index: usize,
3916) -> Result<&ResidentRelation> {
3917 relations
3918 .get(index)
3919 .and_then(Option::as_ref)
3920 .ok_or_else(|| XlogError::Execution(format!("resident relation slot {index} is missing")))
3921}
3922
3923fn collect_recorded_scans(node: &RirNode, output: &mut Vec<RelId>) {
3924 match node {
3925 RirNode::Unit | RirNode::TensorMaskedJoin { .. } => {}
3926 RirNode::Scan { rel } => output.push(*rel),
3927 RirNode::Filter { input, .. }
3928 | RirNode::Project { input, .. }
3929 | RirNode::GroupBy { input, .. }
3930 | RirNode::Distinct { input, .. } => collect_recorded_scans(input, output),
3931 RirNode::Join { left, right, .. } | RirNode::Diff { left, right } => {
3932 collect_recorded_scans(left, output);
3933 collect_recorded_scans(right, output);
3934 }
3935 RirNode::ChainJoin { fallback, .. } | RirNode::MultiWayJoin { fallback, .. } => {
3936 collect_recorded_scans(fallback, output);
3937 }
3938 RirNode::Union { inputs } => {
3939 for input in inputs {
3940 collect_recorded_scans(input, output);
3941 }
3942 }
3943 RirNode::Fixpoint {
3944 base, recursive, ..
3945 } => {
3946 collect_recorded_scans(base, output);
3947 collect_recorded_scans(recursive, output);
3948 }
3949 }
3950}
3951
3952fn resident_decline_error(reason: ResidentGraphDeclineReason) -> ResidentGraphExecutionError {
3953 ResidentGraphExecutionError::Declined(reason)
3954}
3955
3956fn conditional_graph_error(error: CudaConditionalGraphUnavailable) -> ResidentGraphExecutionError {
3957 if error.is_unsupported() {
3958 resident_decline_error(ResidentGraphDeclineReason::ConditionalGraphUnavailable {
3959 detail: error.decline_detail(),
3960 })
3961 } else {
3962 runtime_error(error.decline_detail())
3963 }
3964}
3965
3966fn read_receipt_u32(bytes: &[u8], offset: usize) -> Option<u32> {
3967 bytes
3968 .get(offset..offset.checked_add(4)?)?
3969 .try_into()
3970 .ok()
3971 .map(u32::from_le_bytes)
3972}
3973
3974fn read_receipt_u64(bytes: &[u8], offset: usize) -> Option<u64> {
3975 bytes
3976 .get(offset..offset.checked_add(8)?)?
3977 .try_into()
3978 .ok()
3979 .map(u64::from_le_bytes)
3980}
3981
3982fn resident_compact_scalar(value: &ConstValue) -> Result<(ScalarType, u64)> {
3983 match value {
3984 ConstValue::Symbol(value) => Ok((ScalarType::Symbol, u64::from(symbol::intern(value)))),
3985 ConstValue::U32(value) => Ok((ScalarType::U32, u64::from(*value))),
3986 ConstValue::U64(value) => Ok((ScalarType::U64, *value)),
3987 _ => Err(XlogError::Execution(
3988 "uncertified resident constant type".into(),
3989 )),
3990 }
3991}
3992
3993fn resident_compact_filter_descriptors(
3994 expression: &Expr,
3995 input_schema: &Schema,
3996) -> Result<Vec<ResidentFilterComparisonDescriptor>> {
3997 fn operand(expression: &Expr, input_schema: &Schema) -> Result<(u32, u32, ScalarType, u64)> {
3998 match expression {
3999 Expr::Column(column) => {
4000 let scalar = input_schema.column_type(*column).ok_or_else(|| {
4001 XlogError::Execution("resident filter column is out of range".into())
4002 })?;
4003 Ok((
4004 0,
4005 u32::try_from(*column).map_err(|_| {
4006 XlogError::Execution("resident filter column exceeds u32".into())
4007 })?,
4008 scalar,
4009 0,
4010 ))
4011 }
4012 Expr::Const(value) => {
4013 let (scalar, bits) = resident_compact_scalar(value)?;
4014 Ok((1, 0, scalar, bits))
4015 }
4016 _ => Err(XlogError::Execution(
4017 "uncertified resident filter operand".into(),
4018 )),
4019 }
4020 }
4021
4022 fn append(
4023 expression: &Expr,
4024 input_schema: &Schema,
4025 output: &mut Vec<ResidentFilterComparisonDescriptor>,
4026 ) -> Result<()> {
4027 match expression {
4028 Expr::And(parts) => {
4029 for part in parts {
4030 append(part, input_schema, output)?;
4031 }
4032 Ok(())
4033 }
4034 Expr::Compare { left, op, right } => {
4035 let (left_kind, left_column, left_type, left_constant) =
4036 operand(left, input_schema)?;
4037 let (right_kind, right_column, right_type, right_constant) =
4038 operand(right, input_schema)?;
4039 if left_type != right_type {
4040 return Err(XlogError::Execution(
4041 "resident filter operand scalar types differ".into(),
4042 ));
4043 }
4044 let width = u32::try_from(left_type.size_bytes()).map_err(|_| {
4045 XlogError::Execution("resident filter scalar width exceeds u32".into())
4046 })?;
4047 if !matches!(width, 4 | 8) {
4048 return Err(XlogError::Execution(
4049 "resident filter scalar width is unsupported".into(),
4050 ));
4051 }
4052 output.push(ResidentFilterComparisonDescriptor {
4053 left_kind,
4054 left_column,
4055 right_kind,
4056 right_column,
4057 op: u32::from(resident_compare_op(*op) as u8),
4058 width,
4059 reserved_zero: 0,
4060 reserved_one: 0,
4061 left_constant,
4062 right_constant,
4063 });
4064 Ok(())
4065 }
4066 _ => Err(XlogError::Execution(
4067 "uncertified resident filter expression".into(),
4068 )),
4069 }
4070 }
4071
4072 let mut output = Vec::new();
4073 append(expression, input_schema, &mut output)?;
4074 Ok(output)
4075}
4076
4077fn resident_compact_project_descriptors(
4078 columns: &[ProjectExpr],
4079 input_schema: &Schema,
4080 output_schema: &Schema,
4081) -> Result<Vec<ResidentProjectExpressionDescriptor>> {
4082 if columns.len() != output_schema.arity() {
4083 return Err(XlogError::Execution(
4084 "resident project expression count differs from output arity".into(),
4085 ));
4086 }
4087 columns
4088 .iter()
4089 .enumerate()
4090 .map(|(output_column, expression)| {
4091 let output_type = output_schema.column_type(output_column).ok_or_else(|| {
4092 XlogError::Execution("resident project output column is out of range".into())
4093 })?;
4094 let width = u32::try_from(output_type.size_bytes()).map_err(|_| {
4095 XlogError::Execution("resident project scalar width exceeds u32".into())
4096 })?;
4097 if !matches!(width, 4 | 8) {
4098 return Err(XlogError::Execution(
4099 "resident project scalar width is unsupported".into(),
4100 ));
4101 }
4102 match expression {
4103 ProjectExpr::Column(input_column) => {
4104 if input_schema.column_type(*input_column) != Some(output_type) {
4105 return Err(XlogError::Execution(
4106 "resident project column scalar type differs from output".into(),
4107 ));
4108 }
4109 Ok(ResidentProjectExpressionDescriptor::column(
4110 u32::try_from(*input_column).map_err(|_| {
4111 XlogError::Execution("resident project column exceeds u32".into())
4112 })?,
4113 width,
4114 ))
4115 }
4116 ProjectExpr::Computed(Expr::Const(value), declared_type) => {
4117 let (constant_type, bits) = resident_compact_scalar(value)?;
4118 if constant_type != *declared_type || constant_type != output_type {
4119 return Err(XlogError::Execution(
4120 "resident project constant scalar type differs from output".into(),
4121 ));
4122 }
4123 Ok(ResidentProjectExpressionDescriptor::constant(width, bits))
4124 }
4125 _ => Err(XlogError::Execution(
4126 "uncertified resident projection expression".into(),
4127 )),
4128 }
4129 })
4130 .collect()
4131}
4132
4133fn resident_compare_op(op: RirCompareOp) -> CudaCompareOp {
4134 match op {
4135 RirCompareOp::Eq => CudaCompareOp::Eq,
4136 RirCompareOp::Ne => CudaCompareOp::Ne,
4137 RirCompareOp::Lt => CudaCompareOp::Lt,
4138 RirCompareOp::Le => CudaCompareOp::Le,
4139 RirCompareOp::Gt => CudaCompareOp::Gt,
4140 RirCompareOp::Ge => CudaCompareOp::Ge,
4141 }
4142}
4143
4144fn terminal_status_for_injection(status: &ResidentGraphDeviceStatus) -> ResidentTerminalStatus {
4145 match status {
4146 ResidentGraphDeviceStatus::Success { iterations } => ResidentTerminalStatus {
4147 code: ResidentTerminalCode::Success as u32,
4148 iterations: *iterations,
4149 ..ResidentTerminalStatus::default()
4150 },
4151 ResidentGraphDeviceStatus::IterationLimit { limit, completed } => ResidentTerminalStatus {
4152 code: ResidentTerminalCode::IterationLimit as u32,
4153 iterations: *completed,
4154 limit: *limit,
4155 ..ResidentTerminalStatus::default()
4156 },
4157 ResidentGraphDeviceStatus::CapacityOverflow {
4158 op_id,
4159 required,
4160 capacity,
4161 } => ResidentTerminalStatus {
4162 code: ResidentTerminalCode::CapacityOverflow as u32,
4163 op_id: *op_id,
4164 resource_code: ResidentResourceCode::OutputRows as u32,
4165 required: *required,
4166 capacity: *capacity,
4167 ..ResidentTerminalStatus::default()
4168 },
4169 ResidentGraphDeviceStatus::ResourceExhausted {
4170 op_id,
4171 required,
4172 capacity,
4173 ..
4174 } => ResidentTerminalStatus {
4175 code: ResidentTerminalCode::ResourceExhausted as u32,
4176 op_id: *op_id,
4177 resource_code: ResidentResourceCode::SetHashSlots as u32,
4178 required: *required,
4179 capacity: *capacity,
4180 ..ResidentTerminalStatus::default()
4181 },
4182 }
4183}
4184
4185fn validate_compact_resident_node_envelope(
4186 node: &RirNode,
4187 schema_for: &impl Fn(&RirNode) -> std::result::Result<Schema, ResidentGraphDeclineReason>,
4188) -> std::result::Result<(), ResidentGraphDeclineReason> {
4189 match node {
4190 RirNode::Unit | RirNode::Scan { .. } => Ok(()),
4191 RirNode::Filter { input, .. } | RirNode::Project { input, .. } => {
4192 validate_compact_resident_node_envelope(input, schema_for)
4193 }
4194 RirNode::Distinct { input, key_cols } => {
4195 validate_compact_resident_node_envelope(input, schema_for)?;
4196 let arity = schema_for(input)?.arity();
4197 if key_cols.len() != arity
4198 || key_cols
4199 .iter()
4200 .copied()
4201 .enumerate()
4202 .any(|(column, key)| column != key)
4203 {
4204 return Err(resident_workspace_decline(format!(
4205 "compact resident Distinct requires canonical full-row key columns 0..{arity}"
4206 )));
4207 }
4208 Ok(())
4209 }
4210 RirNode::Union { inputs } => {
4211 if inputs.is_empty() {
4212 return Err(resident_workspace_decline(
4213 "compact resident Union requires at least one input",
4214 ));
4215 }
4216 for input in inputs {
4217 validate_compact_resident_node_envelope(input, schema_for)?;
4218 }
4219 Ok(())
4220 }
4221 RirNode::Diff { left, right } => {
4222 validate_compact_resident_node_envelope(left, schema_for)?;
4223 validate_compact_resident_node_envelope(right, schema_for)
4224 }
4225 RirNode::Join {
4226 left,
4227 right,
4228 left_keys,
4229 right_keys,
4230 join_type,
4231 } => {
4232 if !matches!(join_type, JoinType::Inner | JoinType::Semi) {
4233 return Err(resident_workspace_decline(format!(
4234 "compact resident Join does not support {join_type:?}"
4235 )));
4236 }
4237 if left_keys.len() != 1 || right_keys.len() != 1 {
4238 return Err(resident_workspace_decline(
4239 "compact resident Join requires exactly one key per input",
4240 ));
4241 }
4242 validate_compact_resident_node_envelope(left, schema_for)?;
4243 validate_compact_resident_node_envelope(right, schema_for)?;
4244 let left_schema = schema_for(left)?;
4245 let right_schema = schema_for(right)?;
4246 let left_key = left_keys[0];
4247 let right_key = right_keys[0];
4248 if left_key >= left_schema.arity() || right_key >= right_schema.arity() {
4249 return Err(resident_workspace_decline(
4250 "compact resident Join key is outside its input schema",
4251 ));
4252 }
4253 let left_key_type = left_schema
4254 .column_type(left_key)
4255 .expect("left key bounds checked");
4256 let right_key_type = right_schema
4257 .column_type(right_key)
4258 .expect("right key bounds checked");
4259 if left_key_type != right_key_type
4260 || !matches!(
4261 left_key_type,
4262 ScalarType::U32 | ScalarType::U64 | ScalarType::Symbol
4263 )
4264 {
4265 return Err(resident_workspace_decline(
4266 "compact resident Join requires matching U32, U64, or Symbol key types",
4267 ));
4268 }
4269 if left_key_type.size_bytes() != right_key_type.size_bytes() {
4270 return Err(resident_workspace_decline(
4271 "compact resident Join key widths differ",
4272 ));
4273 }
4274 let output_arity = match join_type {
4275 JoinType::Inner => left_schema
4276 .arity()
4277 .checked_add(right_schema.arity())
4278 .ok_or_else(|| {
4279 resident_workspace_decline("compact resident Join arity overflow")
4280 })?,
4281 JoinType::Semi => left_schema.arity(),
4282 _ => unreachable!("join kind checked above"),
4283 };
4284 if output_arity > 17 {
4285 return Err(resident_workspace_decline(
4286 "compact resident Join output arity exceeds 17",
4287 ));
4288 }
4289 Ok(())
4290 }
4291 RirNode::ChainJoin { fallback, .. } | RirNode::MultiWayJoin { fallback, .. } => {
4292 validate_compact_resident_node_envelope(fallback, schema_for)
4293 }
4294 RirNode::Fixpoint { .. } | RirNode::GroupBy { .. } | RirNode::TensorMaskedJoin { .. } => {
4295 Err(resident_workspace_decline(
4296 "compact resident route contains an unsupported node",
4297 ))
4298 }
4299 }
4300}
4301
4302fn resident_generated_query_heads(
4303 plan: &ExecutionPlan,
4304) -> std::result::Result<BTreeSet<String>, ResidentGraphDeclineReason> {
4305 let mut heads = BTreeSet::new();
4306 let mut rule_positions = HashSet::new();
4307 for (position, provenance) in plan.generated_query_rules.iter().enumerate() {
4308 if provenance.query_index != position {
4309 return Err(resident_workspace_decline(format!(
4310 "generated query provenance position {position} carries query index {}",
4311 provenance.query_index
4312 )));
4313 }
4314 if !rule_positions.insert((provenance.scc_index, provenance.rule_index)) {
4315 return Err(resident_workspace_decline(format!(
4316 "generated query provenance {} reuses compiled rule scc={} rule={}",
4317 provenance.query_index, provenance.scc_index, provenance.rule_index
4318 )));
4319 }
4320 let expected_head = format!("__xlog_query_{}", provenance.query_index);
4321 let rule = plan
4322 .rules_by_scc
4323 .get(provenance.scc_index)
4324 .and_then(|rules| rules.get(provenance.rule_index))
4325 .ok_or_else(|| {
4326 resident_workspace_decline(format!(
4327 "generated query provenance {} references missing compiled rule scc={} rule={}",
4328 provenance.query_index, provenance.scc_index, provenance.rule_index
4329 ))
4330 })?;
4331 if rule.head != expected_head {
4332 return Err(resident_workspace_decline(format!(
4333 "generated query provenance {} expects head {expected_head} but references authored head {}",
4334 provenance.query_index, rule.head
4335 )));
4336 }
4337 let occurrence_count = plan
4338 .rules_by_scc
4339 .iter()
4340 .flatten()
4341 .filter(|candidate| candidate.head == expected_head)
4342 .count();
4343 if occurrence_count != 1 {
4344 return Err(resident_workspace_decline(format!(
4345 "generated query head {expected_head} must have exactly one compiled rule, found {occurrence_count}"
4346 )));
4347 }
4348 heads.insert(expected_head);
4349 }
4350 Ok(heads)
4351}
4352
4353impl Executor {
4354 pub fn prepare_resident_graph(
4356 &self,
4357 plan: &ExecutionPlan,
4358 certificate: &ResidentGraphRouteCertificate,
4359 options: ResidentGraphPrepareOptions,
4360 ) -> std::result::Result<PreparedResidentGraph, ResidentGraphExecutionError> {
4361 if !certificate.matches_plan(plan).map_err(runtime_error)? {
4362 return Err(resident_decline_error(
4363 ResidentGraphDeclineReason::WorkspaceUnbounded {
4364 detail: "route certificate does not match the plan being prepared".into(),
4365 },
4366 ));
4367 }
4368 if !certificate.is_supported() {
4369 return Err(resident_decline_error(
4370 certificate.declines().first().cloned().unwrap_or_else(|| {
4371 ResidentGraphDeclineReason::WorkspaceUnbounded {
4372 detail: "route certificate is not resident-capable".into(),
4373 }
4374 }),
4375 ));
4376 }
4377
4378 self.prepare_resident_graph_after_certification(plan, certificate, options)
4379 }
4380
4381 pub fn prepare_certified_resident_graph(
4383 &self,
4384 certified: &ResidentGraphCertifiedPlan,
4385 options: ResidentGraphPrepareOptions,
4386 ) -> std::result::Result<PreparedResidentGraph, ResidentGraphExecutionError> {
4387 let certificate = certified.certificate();
4388 if !certificate.is_supported() {
4389 return Err(resident_decline_error(
4390 certificate.declines().first().cloned().unwrap_or_else(|| {
4391 ResidentGraphDeclineReason::WorkspaceUnbounded {
4392 detail: "route certificate is not resident-capable".into(),
4393 }
4394 }),
4395 ));
4396 }
4397 self.prepare_resident_graph_after_certification(certified.plan(), certificate, options)
4398 }
4399
4400 fn prepare_resident_graph_after_certification(
4401 &self,
4402 plan: &ExecutionPlan,
4403 certificate: &ResidentGraphRouteCertificate,
4404 options: ResidentGraphPrepareOptions,
4405 ) -> std::result::Result<PreparedResidentGraph, ResidentGraphExecutionError> {
4406 let mut diagnostics =
4407 resident_prepare_diagnostics_for_sample(options.latency_diagnostic_sample);
4408 let total_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
4409 let admission_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
4410 let source_epoch = self.store.mutation_epoch();
4411 let resident_head_names = plan
4412 .rules_by_scc
4413 .iter()
4414 .flat_map(|rules| rules.iter().map(|rule| rule.head.clone()))
4415 .collect::<HashSet<_>>();
4416 let mut source_set_snapshots = Vec::new();
4417 let mut scans = Vec::new();
4418 for rules in &plan.rules_by_scc {
4419 for rule in rules {
4420 collect_recorded_scans(&rule.body, &mut scans);
4421 }
4422 }
4423 scans.sort_by_key(|rel| rel.0);
4424 scans.dedup();
4425 for rel in scans {
4426 let name = self
4427 .rel_names
4428 .get(&rel)
4429 .ok_or_else(|| runtime_error(format!("unknown resident relation id {rel:?}")))?;
4430 match self.store.get_with_version(name) {
4431 Some((buffer, version)) => {
4432 let schema = certificate.schema_for(rel).ok_or_else(|| {
4433 runtime_error(format!("resident scan {name} has no certified schema"))
4434 })?;
4435 if buffer.schema() != schema {
4436 return Err(runtime_error(format!(
4437 "resident scan {name} store schema differs from its certificate"
4438 )));
4439 }
4440 source_set_snapshots.push(
4441 resident_source_set_snapshot(&self.provider, name, version, buffer)
4442 .map_err(resident_decline_error)?,
4443 );
4444 }
4445 None if resident_head_names.contains(name) => {}
4446 None => {
4447 return Err(runtime_error(format!(
4448 "missing resident source relation {name}"
4449 )))
4450 }
4451 }
4452 }
4453 let admission = self
4454 .resident_workspace_admission_after_certification(plan, certificate)
4455 .map_err(resident_decline_error)?;
4456 for (name, schema) in &admission.head_schemas {
4457 if let Some(existing) = self.store.get(name) {
4458 if !resident_schemas_type_compatible(existing.schema(), schema) {
4459 return Err(runtime_error(format!(
4460 "existing resident head {name} has a physically incompatible schema: existing={:?}, staged={schema:?}",
4461 existing.schema()
4462 )));
4463 }
4464 }
4465 }
4466 if let Some(diagnostics) = diagnostics.as_mut() {
4467 diagnostics.admission_and_source_snapshot_ns = resident_prepare_elapsed_ns(
4468 admission_started.expect("diagnostic timer exists when enabled"),
4469 );
4470 }
4471
4472 let execution_setup_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
4473 let runtime =
4474 Arc::clone(self.provider.memory().runtime().ok_or_else(|| {
4475 runtime_error("resident graph requires the async device runtime")
4476 })?);
4477 let stream_id = if let Some(stream_id) = self.resident_graph_stream.get() {
4478 *stream_id
4479 } else {
4480 let acquired = runtime.stream_pool().acquire().map_err(runtime_error)?;
4481 match self.resident_graph_stream.set(acquired) {
4482 Ok(()) => acquired,
4483 Err(_) => *self
4484 .resident_graph_stream
4485 .get()
4486 .ok_or_else(|| runtime_error("resident stream initialization raced"))?,
4487 }
4488 };
4489 let stream = runtime
4490 .stream_pool()
4491 .resolve(stream_id)
4492 .ok_or_else(|| runtime_error("resident graph stream is no longer live"))?;
4493 let execution_domain = self
4494 .provider
4495 .bind_resident_execution_domain(Arc::clone(&runtime), stream_id, Arc::clone(&stream))
4496 .map_err(runtime_error)?;
4497
4498 let mut relation_registration = self
4499 .rel_names
4500 .iter()
4501 .map(|(rel, name)| (*rel, name.clone()))
4502 .collect::<Vec<_>>();
4503 relation_registration.sort_by_key(|(rel, name)| (rel.0, name.clone()));
4504
4505 let mut build = ResidentBuild {
4506 executor: self,
4507 certificate,
4508 capacity: admission.relation_capacity,
4509 relations: Vec::new(),
4510 filter_workspaces: Vec::new(),
4511 project_workspaces: Vec::new(),
4512 heads: BTreeMap::new(),
4513 head_winner_indices: BTreeMap::new(),
4514 source_names: HashSet::new(),
4515 source_aliases: HashMap::new(),
4516 next_op_id: 0,
4517 injection: options.test_device_status,
4518 injection_recorded: false,
4519 };
4520 if let Some(diagnostics) = diagnostics.as_mut() {
4521 diagnostics.execution_domain_and_build_setup_ns = resident_prepare_elapsed_ns(
4522 execution_setup_started.expect("diagnostic timer exists when enabled"),
4523 );
4524 }
4525 let logical_planning_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
4526 for (winner_index, (name, schema)) in admission.head_schemas.iter().enumerate() {
4527 let index = build
4528 .allocate_permanent_relation(schema.clone(), 0)
4529 .map_err(runtime_error)?;
4530 build.heads.insert(name.clone(), index);
4531 build.head_winner_indices.insert(
4532 name.clone(),
4533 u32::try_from(winner_index)
4534 .map_err(|_| runtime_error("resident head count exceeds u32"))?,
4535 );
4536 }
4537
4538 let mut initial_ops = Vec::new();
4539 for (name, schema) in &admission.head_schemas {
4540 if self.store.contains(name) {
4541 let source = build.source_reference(name).map_err(runtime_error)?;
4542 build
4543 .plan_copy(source.clone(), build.heads[name], schema, &mut initial_ops)
4544 .map_err(runtime_error)?;
4545 resident_record_schema_winner_mark(
4546 &mut initial_ops,
4547 ResidentBufferRef::Private(build.heads[name]),
4548 build.head_winner_indices[name],
4549 0,
4550 );
4551 }
4552 }
4553
4554 let mut scc_by_id = HashMap::new();
4555 for (index, scc) in plan.sccs.iter().enumerate() {
4556 if scc_by_id.insert(scc.id, index).is_some() {
4557 return Err(runtime_error(format!(
4558 "duplicate resident SCC id {}",
4559 scc.id
4560 )));
4561 }
4562 }
4563 let ordered_sccs = if plan.strata.is_empty() {
4564 (0..plan.sccs.len()).collect::<Vec<_>>()
4565 } else {
4566 let mut ordered = Vec::new();
4567 for stratum in &plan.strata {
4568 for scc_id in &stratum.sccs {
4569 ordered.push(*scc_by_id.get(scc_id).ok_or_else(|| {
4570 runtime_error(format!(
4571 "stratum {} references unknown SCC {scc_id}",
4572 stratum.id
4573 ))
4574 })?);
4575 }
4576 }
4577 ordered
4578 };
4579 let mut seen_sccs = HashSet::new();
4580 for index in &ordered_sccs {
4581 if !seen_sccs.insert(*index) {
4582 return Err(runtime_error(format!(
4583 "resident execution schedule repeats SCC {}",
4584 plan.sccs[*index].id
4585 )));
4586 }
4587 }
4588 if seen_sccs.len() != plan.sccs.len() {
4589 return Err(runtime_error(
4590 "resident execution schedule omits one or more SCCs",
4591 ));
4592 }
4593
4594 let mut phases = Vec::new();
4595 for scc_index in ordered_sccs {
4596 let scc = &plan.sccs[scc_index];
4597 let rules = plan.rules_by_scc.get(scc_index).ok_or_else(|| {
4598 runtime_error(format!("resident SCC {scc_index} has no rule vector"))
4599 })?;
4600 if rules.is_empty() {
4601 continue;
4602 }
4603 if !scc.is_recursive {
4604 let mut ops = Vec::new();
4605 let mut phase_heads = BTreeMap::<String, ResidentBufferRef>::new();
4606 for (rule_index, rule) in rules.iter().enumerate() {
4607 let mut occurrences = HashMap::new();
4608 let contribution = build
4609 .plan_node(&rule.body, None, &mut occurrences, &mut ops)
4610 .map_err(runtime_error)?;
4611 let schema_id =
4612 admission.rule_schema_ids[scc_index][rule_index].ok_or_else(|| {
4613 runtime_error(format!(
4614 "resident rule {} has no schema winner id",
4615 rule.head
4616 ))
4617 })?;
4618 ops.push(ResidentRecordedOp::SchemaWinnerMark {
4619 contribution: contribution.clone(),
4620 head_index: build.head_winner_indices[&rule.head],
4621 schema_id,
4622 });
4623 let target = *build.heads.get(&rule.head).ok_or_else(|| {
4624 runtime_error(format!("resident head {} was not staged", rule.head))
4625 })?;
4626 let target_schema = build
4627 .schema(&ResidentBufferRef::Private(target))
4628 .map_err(runtime_error)?
4629 .clone();
4630 let current = phase_heads
4631 .remove(&rule.head)
4632 .unwrap_or(ResidentBufferRef::Private(target));
4633 let merged = build
4634 .merge_phase_contribution(
4635 Some(current),
4636 contribution,
4637 &target_schema,
4638 &mut ops,
4639 )
4640 .map_err(runtime_error)?;
4641 phase_heads.insert(rule.head.clone(), merged);
4642 }
4643 for (head, value) in phase_heads {
4644 let target = build.heads[&head];
4645 let target_schema = build
4646 .schema(&ResidentBufferRef::Private(target))
4647 .map_err(runtime_error)?
4648 .clone();
4649 build
4650 .plan_copy(value, target, &target_schema, &mut ops)
4651 .map_err(runtime_error)?;
4652 }
4653 phases.push(ResidentCapturePhase::Segment {
4654 ops,
4655 scc_begin: None,
4656 });
4657 continue;
4658 }
4659
4660 let mut recursive_heads = BTreeMap::new();
4661 for rule in rules {
4662 recursive_heads
4663 .entry(rule.head.clone())
4664 .or_insert_with(|| rule.meta.schema.clone());
4665 }
4666 let mut delta_heads = BTreeMap::new();
4667 for (name, schema) in &recursive_heads {
4668 delta_heads.insert(
4669 name.clone(),
4670 build
4671 .allocate_permanent_relation(schema.clone(), 0)
4672 .map_err(runtime_error)?,
4673 );
4674 }
4675
4676 let mut seed_ops = Vec::new();
4677 let mut seed_values = BTreeMap::<String, ResidentBufferRef>::new();
4678 for (rule_index, rule) in rules.iter().enumerate() {
4679 let mut occurrences = HashMap::new();
4680 let contribution = build
4681 .plan_node(&rule.body, None, &mut occurrences, &mut seed_ops)
4682 .map_err(runtime_error)?;
4683 let schema_id =
4684 admission.rule_schema_ids[scc_index][rule_index].ok_or_else(|| {
4685 runtime_error(format!(
4686 "resident rule {} has no schema winner id",
4687 rule.head
4688 ))
4689 })?;
4690 seed_ops.push(ResidentRecordedOp::SchemaWinnerMark {
4691 contribution: contribution.clone(),
4692 head_index: build.head_winner_indices[&rule.head],
4693 schema_id,
4694 });
4695 let current = seed_values.remove(&rule.head);
4696 let merged = build
4697 .merge_phase_contribution(
4698 current,
4699 contribution,
4700 &rule.meta.schema,
4701 &mut seed_ops,
4702 )
4703 .map_err(runtime_error)?;
4704 seed_values.insert(rule.head.clone(), merged);
4705 }
4706 for (name, schema) in &recursive_heads {
4707 let full = build.heads[name];
4708 let seed = seed_values.remove(name).ok_or_else(|| {
4709 runtime_error(format!(
4710 "recursive resident seed has no contribution for {name}"
4711 ))
4712 })?;
4713 let candidate = build
4714 .plan_union_schema(
4715 ResidentBufferRef::Private(full),
4716 seed,
4717 schema.clone(),
4718 &mut seed_ops,
4719 )
4720 .map_err(runtime_error)?;
4721 let novel = build
4722 .plan_diff_schema(
4723 candidate.clone(),
4724 ResidentBufferRef::Private(full),
4725 schema.clone(),
4726 &mut seed_ops,
4727 )
4728 .map_err(runtime_error)?;
4729 build
4730 .plan_copy(candidate, full, schema, &mut seed_ops)
4731 .map_err(runtime_error)?;
4732 build
4733 .plan_copy(novel, delta_heads[name], schema, &mut seed_ops)
4734 .map_err(runtime_error)?;
4735 }
4736 let begin_op_id = build.next_op_id().map_err(runtime_error)?;
4737 phases.push(ResidentCapturePhase::Segment {
4738 ops: seed_ops,
4739 scc_begin: Some((self.config.max_iterations, begin_op_id)),
4740 });
4741
4742 let mut body_ops = vec![ResidentRecordedOp::ChangedReset];
4743 let mut body_values = BTreeMap::<String, ResidentBufferRef>::new();
4744 let recursive_names = recursive_heads.keys().cloned().collect::<HashSet<_>>();
4745 for (rule_index, rule) in rules.iter().enumerate() {
4746 let mut rule_scans = Vec::new();
4747 collect_recorded_scans(&rule.body, &mut rule_scans);
4748 let mut per_relation_occurrence = HashMap::<RelId, usize>::new();
4749 for rel in rule_scans {
4750 let occurrence = per_relation_occurrence.entry(rel).or_insert(0);
4751 let current = *occurrence;
4752 *occurrence += 1;
4753 let Some(name) = self.rel_names.get(&rel) else {
4754 return Err(runtime_error(format!(
4755 "recursive resident scan has unknown relation id {rel:?}"
4756 )));
4757 };
4758 if !recursive_names.contains(name) {
4759 continue;
4760 }
4761 let mut planning_occurrences = HashMap::new();
4762 let contribution = build
4763 .plan_node(
4764 &rule.body,
4765 Some((rel, current, delta_heads[name])),
4766 &mut planning_occurrences,
4767 &mut body_ops,
4768 )
4769 .map_err(runtime_error)?;
4770 let schema_id =
4771 admission.rule_schema_ids[scc_index][rule_index].ok_or_else(|| {
4772 runtime_error(format!(
4773 "resident rule {} has no schema winner id",
4774 rule.head
4775 ))
4776 })?;
4777 body_ops.push(ResidentRecordedOp::SchemaWinnerMark {
4778 contribution: contribution.clone(),
4779 head_index: build.head_winner_indices[&rule.head],
4780 schema_id,
4781 });
4782 let current = body_values.remove(&rule.head);
4783 let merged = build
4784 .merge_phase_contribution(
4785 current,
4786 contribution,
4787 &rule.meta.schema,
4788 &mut body_ops,
4789 )
4790 .map_err(runtime_error)?;
4791 body_values.insert(rule.head.clone(), merged);
4792 }
4793 }
4794 for (name, schema) in &recursive_heads {
4795 let full = build.heads[name];
4796 let accumulated = match body_values.remove(name) {
4797 Some(value) => value,
4798 None => build
4799 .plan_diff_schema(
4800 ResidentBufferRef::Private(full),
4801 ResidentBufferRef::Private(full),
4802 schema.clone(),
4803 &mut body_ops,
4804 )
4805 .map_err(runtime_error)?,
4806 };
4807 let novel = build
4808 .plan_diff_schema(
4809 accumulated,
4810 ResidentBufferRef::Private(full),
4811 schema.clone(),
4812 &mut body_ops,
4813 )
4814 .map_err(runtime_error)?;
4815 let next_full = build
4816 .plan_union_schema(
4817 ResidentBufferRef::Private(full),
4818 novel.clone(),
4819 schema.clone(),
4820 &mut body_ops,
4821 )
4822 .map_err(runtime_error)?;
4823 build
4824 .plan_copy(next_full, full, schema, &mut body_ops)
4825 .map_err(runtime_error)?;
4826 build
4827 .plan_copy(novel, delta_heads[name], schema, &mut body_ops)
4828 .map_err(runtime_error)?;
4829 body_ops.push(ResidentRecordedOp::ChangedMark {
4832 relation: delta_heads[name],
4833 });
4834 }
4835 let convergence_op_id = build.next_op_id().map_err(runtime_error)?;
4836 phases.push(ResidentCapturePhase::ConditionalWhile {
4837 ops: body_ops,
4838 iteration_limit: self.config.max_iterations,
4839 convergence_op_id,
4840 });
4841 }
4842
4843 let success_op_id = build.next_op_id().map_err(runtime_error)?;
4844 if let Some(diagnostics) = diagnostics.as_mut() {
4845 diagnostics.logical_schedule_planning_ns = resident_prepare_elapsed_ns(
4846 logical_planning_started.expect("diagnostic timer exists when enabled"),
4847 );
4848 }
4849 let manifest_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
4850 let mut manifest = build
4851 .allocation_manifest(&initial_ops, &phases)
4852 .map_err(runtime_error)?;
4853 let compact_tables =
4854 resident_compact_tables(&build.filter_workspaces, &build.project_workspaces)
4855 .map_err(runtime_error)?;
4856 let compact_regions =
4857 resident_compact_regions(initial_ops, phases, success_op_id).map_err(runtime_error)?;
4858 if let Some(diagnostics) = diagnostics.as_mut() {
4859 diagnostics.manifest_compact_construction_ns = resident_prepare_elapsed_ns(
4860 manifest_started.expect("diagnostic timer exists when enabled"),
4861 );
4862 diagnostics.compact_regions = compact_regions.len();
4863 diagnostics.conditional_regions = compact_regions
4864 .iter()
4865 .filter(|region| region.iteration_limit != 0)
4866 .count();
4867 }
4868 let schedule_lowering_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
4869 let compact_schedule = resident_lower_compact_regions(
4870 compact_regions,
4871 &manifest.slots,
4872 &manifest.logical_to_slot,
4873 build.source_names.iter().map(String::as_str),
4874 compact_tables,
4875 )
4876 .map_err(runtime_error)?;
4877 manifest
4878 .finalize_compact_schedule(&compact_schedule, build.heads.len())
4879 .map_err(runtime_error)?;
4880 if let Some(diagnostics) = diagnostics.as_mut() {
4881 diagnostics.schedule_lowering_ns = resident_prepare_elapsed_ns(
4882 schedule_lowering_started.expect("diagnostic timer exists when enabled"),
4883 );
4884 diagnostics.required_reservation_bytes = manifest.required_bytes;
4885 diagnostics.logical_relation_values = manifest.logical_relation_values;
4886 diagnostics.physical_relation_slots = manifest.slots.len();
4887 diagnostics.compact_ops = compact_schedule.ops.len();
4888 diagnostics.compact_waves = compact_schedule.waves.len();
4889 }
4890 let reservation_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
4891 let manifest_available_bytes = self.provider.memory().remaining_bytes();
4892 let mut reservation = self
4893 .provider
4894 .memory()
4895 .reserve_bytes(manifest.required_bytes)
4896 .map_err(|error| {
4897 resident_decline_error(ResidentGraphDeclineReason::WorkspaceUnbounded {
4898 detail: format!(
4899 "resident allocation manifest reservation of {} bytes failed: {error}",
4900 manifest.required_bytes
4901 ),
4902 })
4903 })?;
4904 if let Some(diagnostics) = diagnostics.as_mut() {
4905 diagnostics.reservation_ns = resident_prepare_elapsed_ns(
4906 reservation_started.expect("diagnostic timer exists when enabled"),
4907 );
4908 }
4909 let physical = build
4910 .materialize(&manifest, &mut reservation, diagnostics.as_mut())
4911 .map_err(runtime_error)?;
4912 let count_initialization_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
4913 let mut count_initialization = execution_domain.new_strict_recorder();
4914 for slot_index in 0..manifest.slots.len() {
4915 let relation =
4916 private_relation(&physical.relations, slot_index).map_err(runtime_error)?;
4917 count_initialization.write(relation.num_rows_device());
4918 }
4919 let count_initialization = unsafe {
4920 execution_domain.enqueue(count_initialization, |enqueue| {
4921 for (slot_index, slot) in manifest.slots.iter().enumerate() {
4922 let relation = private_relation(&physical.relations, slot_index)?;
4923 self.provider
4924 .record_resident_relation_count_initialize_on_stream(
4925 relation,
4926 slot.initial_count,
4927 enqueue,
4928 )?;
4929 }
4930 Ok::<(), XlogError>(())
4931 })
4932 }
4933 .map_err(runtime_error)?;
4934 count_initialization.commit().map_err(runtime_error)?;
4935 if let Some(diagnostics) = diagnostics.as_mut() {
4936 diagnostics.count_initialization_ns = resident_prepare_elapsed_ns(
4937 count_initialization_started.expect("diagnostic timer exists when enabled"),
4938 );
4939 diagnostics.count_initialization_ns_max = diagnostics.count_initialization_ns;
4940 diagnostics.count_memset_calls =
4941 u64::try_from(manifest.slots.len()).unwrap_or(u64::MAX);
4942 }
4943
4944 if build.injection.is_some() && !build.injection_recorded {
4945 return Err(runtime_error(
4946 "test device status requested after a nonexistent physical op",
4947 ));
4948 }
4949 let metadata_binding_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
4950 let output_indices =
4951 resident_output_indices(&build.heads, &manifest.logical_to_slot, &manifest.slots)
4952 .map_err(runtime_error)?;
4953 let output_relations = output_indices
4954 .iter()
4955 .map(|(_, index)| private_relation(&physical.relations, *index))
4956 .collect::<Result<Vec<_>>>()
4957 .map_err(runtime_error)?;
4958 let output_schema_plans = output_indices
4959 .iter()
4960 .map(|(name, _)| {
4961 let candidates = admission
4962 .head_schema_choices
4963 .get(name)
4964 .cloned()
4965 .ok_or_else(|| {
4966 runtime_error(format!("resident head {name} has no schema plan"))
4967 })?;
4968 let selection = if let Some(selection) = admission.head_schema_selections.get(name)
4969 {
4970 let source_output = usize::try_from(
4971 *build
4972 .head_winner_indices
4973 .get(&selection.source_head)
4974 .ok_or_else(|| {
4975 runtime_error(format!(
4976 "resident schema source {} is not staged",
4977 selection.source_head
4978 ))
4979 })?,
4980 )
4981 .map_err(|_| runtime_error("resident schema source index overflow"))?;
4982 let source_candidates = admission
4983 .head_schema_choices
4984 .get(&selection.source_head)
4985 .ok_or_else(|| runtime_error("resident schema source is missing"))?;
4986 if source_candidates.len() != selection.output_schemas_by_source_winner.len() {
4987 return Err(runtime_error(
4988 "resident schema source mapping has the wrong length",
4989 ));
4990 }
4991 ResidentOutputSchemaSelection::SourceWinner {
4992 source_output,
4993 schemas: selection.output_schemas_by_source_winner.clone(),
4994 }
4995 } else {
4996 ResidentOutputSchemaSelection::OwnWinner
4997 };
4998 Ok(ResidentOutputSchemaPlan {
4999 candidates,
5000 selection,
5001 })
5002 })
5003 .collect::<std::result::Result<Vec<_>, ResidentGraphExecutionError>>()?;
5004 if let Some(diagnostics) = diagnostics.as_mut() {
5005 let metadata_binding_ns = resident_prepare_elapsed_ns(
5006 metadata_binding_started.expect("diagnostic timer exists when enabled"),
5007 );
5008 diagnostics.metadata_binding_construction_ns = diagnostics
5009 .metadata_binding_construction_ns
5010 .saturating_add(metadata_binding_ns);
5011 }
5012 let device_trace_bytes_before = diagnostics.as_ref().map(|_| reservation.used_bytes());
5013 let device_trace_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
5014 let device_trace = self
5015 .provider
5016 .prepare_resident_device_trace_in_reservation(&mut reservation)
5017 .map_err(runtime_error)?;
5018 if let Some(diagnostics) = diagnostics.as_mut() {
5019 let device_trace_ns = resident_prepare_elapsed_ns(
5020 device_trace_started.expect("diagnostic timer exists when enabled"),
5021 );
5022 let device_trace_reserved_bytes = reservation.used_bytes().saturating_sub(
5023 device_trace_bytes_before.expect("diagnostic byte snapshot exists when enabled"),
5024 );
5025 diagnostics.metadata_provider_calls =
5026 diagnostics.metadata_provider_calls.saturating_add(1);
5027 diagnostics.device_trace_preparation_ns = device_trace_ns;
5028 diagnostics.device_trace_reserved_bytes = device_trace_reserved_bytes;
5029 }
5030 let schema_defaults_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
5031 let default_schema_ids =
5032 resident_compact_schema_defaults(&compact_schedule.ops, output_relations.len())
5033 .map_err(runtime_error)?;
5034 if let Some(diagnostics) = diagnostics.as_mut() {
5035 let schema_defaults_ns = resident_prepare_elapsed_ns(
5036 schema_defaults_started.expect("diagnostic timer exists when enabled"),
5037 );
5038 diagnostics.metadata_binding_construction_ns = diagnostics
5039 .metadata_binding_construction_ns
5040 .saturating_add(schema_defaults_ns);
5041 }
5042 let schema_winners_bytes_before = diagnostics.as_ref().map(|_| reservation.used_bytes());
5043 let schema_winners_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
5044 let schema_winners = self
5045 .provider
5046 .prepare_resident_schema_winners_in_reservation(&default_schema_ids, &mut reservation)
5047 .map_err(runtime_error)?;
5048 if let Some(diagnostics) = diagnostics.as_mut() {
5049 let schema_winners_ns = resident_prepare_elapsed_ns(
5050 schema_winners_started.expect("diagnostic timer exists when enabled"),
5051 );
5052 let schema_winners_reserved_bytes = reservation.used_bytes().saturating_sub(
5053 schema_winners_bytes_before.expect("diagnostic byte snapshot exists when enabled"),
5054 );
5055 diagnostics.metadata_provider_calls =
5056 diagnostics.metadata_provider_calls.saturating_add(1);
5057 diagnostics.schema_winners_preparation_ns = schema_winners_ns;
5058 diagnostics.schema_winners_reserved_bytes = schema_winners_reserved_bytes;
5059 (
5060 diagnostics.schema_winners_initial_htod_calls,
5061 diagnostics.schema_winners_initial_htod_bytes,
5062 ) = resident_schema_winners_initial_htod(default_schema_ids.len());
5063 }
5064 let receipt_bytes_before = diagnostics.as_ref().map(|_| reservation.used_bytes());
5065 let receipt_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
5066 let receipt = self
5067 .provider
5068 .prepare_resident_packed_receipt_with_trace_and_schema_winners_in_reservation(
5069 &output_relations,
5070 &device_trace,
5071 &schema_winners,
5072 &mut reservation,
5073 )
5074 .map_err(runtime_error)?;
5075 if let Some(diagnostics) = diagnostics.as_mut() {
5076 let receipt_ns = resident_prepare_elapsed_ns(
5077 receipt_started.expect("diagnostic timer exists when enabled"),
5078 );
5079 let receipt_reserved_bytes = reservation.used_bytes().saturating_sub(
5080 receipt_bytes_before.expect("diagnostic byte snapshot exists when enabled"),
5081 );
5082 diagnostics.metadata_provider_calls =
5083 diagnostics.metadata_provider_calls.saturating_add(1);
5084 diagnostics.receipt_preparation_ns = receipt_ns;
5085 diagnostics.receipt_reserved_bytes = receipt_reserved_bytes;
5086 (
5087 diagnostics.receipt_initial_htod_calls,
5088 diagnostics.receipt_initial_htod_bytes,
5089 ) = resident_receipt_initial_htod(output_relations.len());
5090 }
5091 let schedule_bindings_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
5092 let mut schedule_bindings = Vec::with_capacity(
5093 manifest
5094 .slots
5095 .len()
5096 .checked_add(compact_schedule.source_slots.len())
5097 .ok_or_else(|| runtime_error("resident compact slot binding count overflow"))?,
5098 );
5099 for (slot_index, slot) in manifest.slots.iter().enumerate() {
5100 let relation =
5101 private_relation(&physical.relations, slot_index).map_err(runtime_error)?;
5102 schedule_bindings.push(if slot.permanent {
5103 ResidentScheduleSlotBinding::permanent(relation.buffer(), 0)
5104 } else {
5105 ResidentScheduleSlotBinding::scratch(relation.buffer(), 0)
5106 });
5107 }
5108 for (name, slot) in &compact_schedule.source_slots {
5109 let expected_slot = u32::try_from(schedule_bindings.len())
5110 .map_err(|_| runtime_error("resident compact slot binding count exceeds u32"))?;
5111 if *slot != expected_slot {
5112 return Err(runtime_error(
5113 "resident compact source bindings are not contiguous",
5114 ));
5115 }
5116 let source = self.store.get(name).ok_or_else(|| {
5117 runtime_error(format!("resident source {name} disappeared during prepare"))
5118 })?;
5119 schedule_bindings
5120 .push(ResidentScheduleSlotBinding::source(source, 0).map_err(runtime_error)?);
5121 }
5122 let receipt_slots = output_indices
5123 .iter()
5124 .map(|(_, slot)| {
5125 u32::try_from(*slot)
5126 .map_err(|_| runtime_error("resident receipt slot index exceeds u32"))
5127 })
5128 .collect::<std::result::Result<Vec<_>, _>>()?;
5129 if let Some(diagnostics) = diagnostics.as_mut() {
5130 let schedule_bindings_ns = resident_prepare_elapsed_ns(
5131 schedule_bindings_started.expect("diagnostic timer exists when enabled"),
5132 );
5133 diagnostics.metadata_binding_construction_ns = diagnostics
5134 .metadata_binding_construction_ns
5135 .saturating_add(schedule_bindings_ns);
5136 }
5137 let schedule_program_bytes_before = diagnostics.as_ref().map(|_| reservation.used_bytes());
5138 let schedule_program_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
5139 let schedule_program = self
5140 .provider
5141 .prepare_resident_schedule_program_in_reservation(
5142 &execution_domain,
5143 &schedule_bindings,
5144 &compact_schedule.ops,
5145 &compact_schedule.waves,
5146 &compact_schedule.regions,
5147 &compact_schedule.generation_bases,
5148 &compact_schedule.filter_comparisons,
5149 &compact_schedule.project_expressions,
5150 &receipt_slots,
5151 ResidentScheduleExternalBindings::new(
5152 physical.filter_scratch.as_ref(),
5153 &physical.set_workspace,
5154 &physical.join_workspace,
5155 &physical.control,
5156 &device_trace,
5157 &schema_winners,
5158 &receipt,
5159 ),
5160 &mut reservation,
5161 )
5162 .map_err(runtime_error)?;
5163 if let Some(diagnostics) = diagnostics.as_mut() {
5164 let schedule_program_ns = resident_prepare_elapsed_ns(
5165 schedule_program_started.expect("diagnostic timer exists when enabled"),
5166 );
5167 let schedule_program_reserved_bytes = reservation.used_bytes().saturating_sub(
5168 schedule_program_bytes_before
5169 .expect("diagnostic byte snapshot exists when enabled"),
5170 );
5171 diagnostics.metadata_provider_calls =
5172 diagnostics.metadata_provider_calls.saturating_add(1);
5173 diagnostics.schedule_program_preparation_ns = schedule_program_ns;
5174 diagnostics.schedule_program_reserved_bytes = schedule_program_reserved_bytes;
5175 (
5176 diagnostics.schedule_program_initial_htod_calls,
5177 diagnostics.schedule_program_initial_htod_bytes,
5178 ) = resident_schedule_initial_htod(diagnostics.schedule_program_reserved_bytes);
5179 }
5180 let reservation_validation_started =
5181 diagnostics.as_ref().map(|_| std::time::Instant::now());
5182 let tracked_device_allocation_bytes = reservation.used_bytes();
5183 resident_validate_exact_reservation(
5184 manifest.required_bytes,
5185 tracked_device_allocation_bytes,
5186 reservation.remaining_bytes(),
5187 )
5188 .map_err(runtime_error)?;
5189 drop(reservation);
5190 if let Some(diagnostics) = diagnostics.as_mut() {
5191 diagnostics.reservation_validation_and_release_ns = resident_prepare_elapsed_ns(
5192 reservation_validation_started.expect("diagnostic timer exists when enabled"),
5193 );
5194 }
5195 let pinned_receipt_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
5196 let pinned_receipt = self
5197 .provider
5198 .prepare_resident_pinned_receipt(&receipt)
5199 .map_err(runtime_error)?;
5200 if let Some(diagnostics) = diagnostics.as_mut() {
5201 diagnostics.pinned_receipt_ns = resident_prepare_elapsed_ns(
5202 pinned_receipt_started.expect("diagnostic timer exists when enabled"),
5203 );
5204 }
5205
5206 let record_launch = || {
5209 let mut recorder = execution_domain.new_strict_recorder();
5210 for binding in &schedule_bindings {
5211 binding.record_uses(&mut recorder);
5212 }
5213 ResidentScheduleExternalBindings::new(
5214 physical.filter_scratch.as_ref(),
5215 &physical.set_workspace,
5216 &physical.join_workspace,
5217 &physical.control,
5218 &device_trace,
5219 &schema_winners,
5220 &receipt,
5221 )
5222 .record_uses(&mut recorder);
5223 schedule_program.record_uses(&mut recorder);
5224 recorder
5225 };
5226 let graph_capture_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
5227 let mut sequence =
5228 ConditionalCudaGraphSequenceBuilder::new(&stream).map_err(conditional_graph_error)?;
5229 let capture_topology =
5230 resident_compact_topology(&compact_schedule.regions).map_err(runtime_error)?;
5231 let mut conditional_body_node_kinds =
5232 Vec::with_capacity(capture_topology.conditional_body_kernel_counts.len());
5233 for ((region_index, region), parent_kind) in compact_schedule
5234 .regions
5235 .iter()
5236 .enumerate()
5237 .zip(capture_topology.parent_kinds.iter().copied())
5238 {
5239 let region_index = u32::try_from(region_index)
5240 .map_err(|_| runtime_error("resident compact region index exceeds u32"))?;
5241 if parent_kind == ResidentCaptureParentKind::Conditional {
5242 sequence
5243 .add_conditional_while(u32::from(region.iteration_limit != 0), true, |body| {
5244 body.capture_on_stream(&stream, || unsafe {
5245 execution_domain
5246 .enqueue(record_launch(), |enqueue| {
5247 self.provider.record_resident_schedule_region_on_stream(
5248 &schedule_program,
5249 region_index,
5250 Some(body),
5251 enqueue,
5252 )
5253 })
5254 .map_err(|error| xlog_core::XlogError::Kernel(error.to_string()))?
5255 .commit()
5256 })?;
5257 conditional_body_node_kinds.push(body.linear_chain_node_kinds()?);
5258 Ok(())
5259 })
5260 .map_err(conditional_graph_error)?;
5261 } else {
5262 sequence
5263 .capture_segment_on_stream(&stream, || unsafe {
5264 execution_domain
5265 .enqueue(record_launch(), |enqueue| {
5266 self.provider.record_resident_schedule_region_on_stream(
5267 &schedule_program,
5268 region_index,
5269 None,
5270 enqueue,
5271 )
5272 })
5273 .map_err(|error| xlog_core::XlogError::Kernel(error.to_string()))?
5274 .commit()
5275 })
5276 .map_err(conditional_graph_error)?;
5277 }
5278 }
5279 if let Some(diagnostics) = diagnostics.as_mut() {
5280 diagnostics.graph_body_capture_ns = resident_prepare_elapsed_ns(
5281 graph_capture_started.expect("diagnostic timer exists when enabled"),
5282 );
5283 }
5284 let graph_instantiate_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
5285 let graph = sequence
5286 .instantiate()
5287 .map_err(conditional_graph_error)?
5288 .bind_resident_lifecycle(&runtime);
5289 if let Some(diagnostics) = diagnostics.as_mut() {
5290 diagnostics.graph_instantiate_ns = resident_prepare_elapsed_ns(
5291 graph_instantiate_started.expect("diagnostic timer exists when enabled"),
5292 );
5293 }
5294
5295 let validation_started = diagnostics.as_ref().map(|_| std::time::Instant::now());
5296 let recorder = record_launch();
5297
5298 let graph_node_kinds = graph.linear_chain_node_kinds().map_err(runtime_error)?;
5299 resident_validate_parent_graph_kinds(&graph_node_kinds, &capture_topology.parent_kinds)
5300 .map_err(runtime_error)?;
5301 let conditional_body_kernel_counts = resident_validate_conditional_body_node_kinds(
5302 &conditional_body_node_kinds,
5303 capture_topology.conditional_body_kernel_counts.len(),
5304 )
5305 .map_err(runtime_error)?;
5306 if conditional_body_kernel_counts != capture_topology.conditional_body_kernel_counts {
5307 return Err(runtime_error(
5308 "resident conditional body topology differs from the compact schedule",
5309 ));
5310 }
5311 let body_node_count =
5312 conditional_body_node_kinds
5313 .iter()
5314 .try_fold(0usize, |total, kinds| {
5315 total.checked_add(kinds.len()).ok_or_else(|| {
5316 runtime_error("resident hierarchical graph node count overflow")
5317 })
5318 })?;
5319 let hierarchical_graph_nodes = graph_node_kinds
5320 .len()
5321 .checked_add(body_node_count)
5322 .ok_or_else(|| runtime_error("resident hierarchical graph node count overflow"))?;
5323 if hierarchical_graph_nodes != capture_topology.hierarchical_node_count {
5324 return Err(runtime_error(
5325 "resident hierarchical graph topology differs from the compact schedule",
5326 ));
5327 }
5328 let (
5329 filter_descriptor_device_bytes,
5330 project_descriptor_device_bytes,
5331 fixed_workspace_device_bytes,
5332 ) = resident_compact_preflight_device_bytes(&manifest, &compact_schedule)
5333 .map_err(runtime_error)?;
5334 let parent_graph_node_count = graph_node_kinds.len();
5335 let preflight_report = ResidentGraphPreflightReport {
5336 relation_capacity: admission.relation_capacity,
5337 estimated_required_bytes: manifest.required_bytes,
5338 available_bytes_at_admission: manifest_available_bytes,
5339 tracked_device_allocation_bytes,
5340 relation_device_bytes: manifest.relation_bytes,
5341 filter_descriptor_device_bytes,
5342 filter_scratch_device_bytes: manifest.filter_scratch_bytes,
5343 project_descriptor_device_bytes,
5344 fixed_workspace_device_bytes,
5345 parent_graph_nodes: graph_node_kinds.len(),
5346 conditional_while_nodes: graph_node_kinds
5347 .iter()
5348 .filter(|kind| **kind == CudaGraphNodeKind::Conditional)
5349 .count(),
5350 parent_graph_node_kinds: graph_node_kinds,
5351 conditional_body_node_kinds,
5352 conditional_body_kernel_counts,
5353 hierarchical_graph_nodes,
5354 private_relation_slots: physical.relations.len(),
5355 logical_relation_values: manifest.logical_relation_values,
5356 permanent_relation_slots: manifest.permanent_slots,
5357 staged_output_relations: output_indices.len(),
5358 scratch_slots: manifest.scratch_slots,
5359 filter_scratch_allocations: manifest.filter_scratch_allocations,
5360 max_row_bytes: manifest.max_row_bytes,
5361 };
5362 let has_device_status_writer = build.injection_recorded;
5363 let owners = ResidentRunOwners {
5364 provider: Arc::clone(&self.provider),
5365 runtime,
5366 stream,
5367 graph,
5368 execution_domain,
5369 _schedule_program: schedule_program,
5370 recorder,
5371 relations: physical.relations,
5372 source_binding_names: compact_schedule.source_slots.keys().cloned().collect(),
5373 output_indices,
5374 _filter_scratch: physical.filter_scratch,
5375 _set_workspace: physical.set_workspace,
5376 _join_workspace: physical.join_workspace,
5377 _control: physical.control,
5378 _device_trace: device_trace,
5379 _schema_winners: schema_winners,
5380 receipt,
5381 pinned_receipt,
5382 source_epoch,
5383 relation_registration,
5384 transaction_identity: Arc::clone(&self.transaction_identity),
5385 output_schema_plans,
5386 };
5387 let validation_owner_assembly_ns = diagnostics.as_ref().map(|_| {
5388 resident_prepare_elapsed_ns(
5389 validation_started.expect("diagnostic timer exists when enabled"),
5390 )
5391 });
5392 let prepare_total_ns = diagnostics.as_ref().map(|_| {
5393 resident_prepare_elapsed_ns(
5394 total_started.expect("diagnostic total timer exists when enabled"),
5395 )
5396 });
5397 let prepare_diagnostic = diagnostics.map(|mut diagnostics| {
5398 diagnostics.total_ns =
5399 prepare_total_ns.expect("diagnostic total sample exists when enabled");
5400 diagnostics.validation_owner_assembly_ns = validation_owner_assembly_ns
5401 .expect("diagnostic validation sample exists when enabled");
5402 diagnostics.parent_graph_nodes = parent_graph_node_count;
5403 diagnostics.conditional_body_nodes = body_node_count;
5404 diagnostics
5405 });
5406 Ok(PreparedResidentGraph {
5407 owners,
5408 preflight_report,
5409 has_device_status_writer,
5410 source_set_snapshots,
5411 source_buffers_to_restore: Vec::new(),
5412 prepare_diagnostic,
5413 })
5414 }
5415
5416 fn resident_schema_variants(
5417 &self,
5418 node: &RirNode,
5419 certificate: &ResidentGraphRouteCertificate,
5420 head_schema_choices: &BTreeMap<String, Vec<Schema>>,
5421 ) -> std::result::Result<ResidentSchemaVariants, ResidentGraphDeclineReason> {
5422 match node {
5423 RirNode::Unit => certificate
5424 .node_schema(node)
5425 .map(ResidentSchemaVariants::Fixed)
5426 .ok_or_else(|| resident_workspace_decline("resident node schema missing")),
5427 RirNode::Scan { rel } => {
5428 let name = self.rel_names.get(rel).ok_or_else(|| {
5429 resident_workspace_decline(format!(
5430 "resident scan relation {rel:?} is not registered"
5431 ))
5432 })?;
5433 if let Some(schemas) = head_schema_choices.get(name) {
5434 return resident_schema_variants_from_source(name.clone(), schemas.clone());
5435 }
5436 let schema = self
5437 .store
5438 .get(name)
5439 .map(|buffer| buffer.schema().clone())
5440 .or_else(|| certificate.schema_for(*rel).cloned())
5441 .ok_or_else(|| {
5442 resident_workspace_decline(format!("resident scan {name} has no schema"))
5443 })?;
5444 Ok(ResidentSchemaVariants::Fixed(schema))
5445 }
5446 RirNode::Filter { input, .. } | RirNode::Distinct { input, .. } => {
5447 self.resident_schema_variants(input, certificate, head_schema_choices)
5448 }
5449 RirNode::Project { input, columns } => {
5450 match self.resident_schema_variants(input, certificate, head_schema_choices)? {
5451 ResidentSchemaVariants::Fixed(schema) => self
5452 .project_schema(&schema, columns)
5453 .map(ResidentSchemaVariants::Fixed)
5454 .map_err(|error| resident_workspace_decline(error.to_string())),
5455 ResidentSchemaVariants::Dynamic {
5456 source_head,
5457 schemas,
5458 } => {
5459 let schemas = schemas
5460 .iter()
5461 .map(|schema| self.project_schema(schema, columns))
5462 .collect::<Result<Vec<_>>>()
5463 .map_err(|error| resident_workspace_decline(error.to_string()))?;
5464 resident_schema_variants_from_source(source_head, schemas)
5465 }
5466 }
5467 }
5468 RirNode::Join {
5469 left,
5470 right,
5471 join_type,
5472 ..
5473 } => {
5474 let left = self.resident_schema_variants(left, certificate, head_schema_choices)?;
5475 match join_type {
5476 JoinType::Semi => Ok(left),
5477 JoinType::Inner => {
5478 let right =
5479 self.resident_schema_variants(right, certificate, head_schema_choices)?;
5480 match (left, right) {
5481 (
5482 ResidentSchemaVariants::Fixed(left),
5483 ResidentSchemaVariants::Fixed(right),
5484 ) => {
5485 let mut columns = left.columns;
5486 columns.extend(right.columns);
5487 Ok(ResidentSchemaVariants::Fixed(Schema::new(columns)))
5488 }
5489 (
5490 ResidentSchemaVariants::Dynamic {
5491 source_head,
5492 schemas,
5493 },
5494 ResidentSchemaVariants::Fixed(right),
5495 ) => {
5496 let schemas = schemas
5497 .into_iter()
5498 .map(|left| {
5499 let mut columns = left.columns;
5500 columns.extend(right.columns.iter().cloned());
5501 Schema::new(columns)
5502 })
5503 .collect();
5504 resident_schema_variants_from_source(source_head, schemas)
5505 }
5506 (
5507 ResidentSchemaVariants::Fixed(left),
5508 ResidentSchemaVariants::Dynamic {
5509 source_head,
5510 schemas,
5511 },
5512 ) => {
5513 let schemas = schemas
5514 .into_iter()
5515 .map(|right| {
5516 let mut columns = left.columns.clone();
5517 columns.extend(right.columns);
5518 Schema::new(columns)
5519 })
5520 .collect();
5521 resident_schema_variants_from_source(source_head, schemas)
5522 }
5523 (
5524 ResidentSchemaVariants::Dynamic {
5525 source_head: left_source,
5526 schemas: left_schemas,
5527 },
5528 ResidentSchemaVariants::Dynamic {
5529 source_head: right_source,
5530 schemas: right_schemas,
5531 },
5532 ) => {
5533 if left_source != right_source
5534 || left_schemas.len() != right_schemas.len()
5535 {
5536 return Err(resident_workspace_decline(
5537 "resident join has multiple schema sources",
5538 ));
5539 }
5540 let schemas = left_schemas
5541 .into_iter()
5542 .zip(right_schemas)
5543 .map(|(left, right)| {
5544 let mut columns = left.columns;
5545 columns.extend(right.columns);
5546 Schema::new(columns)
5547 })
5548 .collect();
5549 resident_schema_variants_from_source(left_source, schemas)
5550 }
5551 }
5552 }
5553 _ => Err(resident_workspace_decline(
5554 "uncertified join reached resident schema planning",
5555 )),
5556 }
5557 }
5558 RirNode::ChainJoin { fallback, .. } | RirNode::MultiWayJoin { fallback, .. } => {
5559 self.resident_schema_variants(fallback, certificate, head_schema_choices)
5560 }
5561 RirNode::Diff { left, .. } => {
5562 self.resident_schema_variants(left, certificate, head_schema_choices)
5563 }
5564 RirNode::Union { inputs } => {
5565 if inputs.is_empty() {
5566 return Err(resident_workspace_decline(
5567 "resident schema union has no inputs",
5568 ));
5569 }
5570 let mut fixed = None;
5571 for input in inputs {
5572 match self.resident_schema_variants(input, certificate, head_schema_choices)? {
5573 ResidentSchemaVariants::Fixed(schema) => match &fixed {
5574 Some(existing) if existing != &schema => {
5575 return Err(resident_workspace_decline(
5576 "resident schema union has ambiguous input schemas",
5577 ));
5578 }
5579 Some(_) => {}
5580 None => fixed = Some(schema),
5581 },
5582 ResidentSchemaVariants::Dynamic { .. } => {
5583 return Err(resident_workspace_decline(
5584 "resident schema union has ambiguous dynamic lineage",
5585 ));
5586 }
5587 }
5588 }
5589 fixed.map(ResidentSchemaVariants::Fixed).ok_or_else(|| {
5590 resident_workspace_decline("resident schema union has no inputs")
5591 })
5592 }
5593 RirNode::Fixpoint { .. }
5594 | RirNode::GroupBy { .. }
5595 | RirNode::TensorMaskedJoin { .. } => Err(resident_workspace_decline(
5596 "unsupported node reached resident schema planning",
5597 )),
5598 }
5599 }
5600
5601 fn resident_workspace_admission_after_certification(
5602 &self,
5603 plan: &ExecutionPlan,
5604 certificate: &ResidentGraphRouteCertificate,
5605 ) -> std::result::Result<ResidentWorkspaceAdmission, ResidentGraphDeclineReason> {
5606 for rule in plan.rules_by_scc.iter().flatten() {
5607 validate_compact_resident_node_envelope(&rule.body, &|node| {
5608 certificate.node_schema(node).ok_or_else(|| {
5609 resident_workspace_decline("compact resident node schema is missing")
5610 })
5611 })?;
5612 }
5613
5614 let generated_query_heads = resident_generated_query_heads(plan)?;
5615
5616 let mut generated_query_occurrences = generated_query_heads
5617 .iter()
5618 .map(|head| (head.as_str(), 0usize))
5619 .collect::<BTreeMap<_, _>>();
5620 for rule in plan.rules_by_scc.iter().flatten() {
5621 if let Some(count) = generated_query_occurrences.get_mut(rule.head.as_str()) {
5622 *count = count.checked_add(1).ok_or_else(|| {
5623 resident_workspace_decline(format!(
5624 "generated query head {} rule count overflowed",
5625 rule.head
5626 ))
5627 })?;
5628 }
5629 }
5630 for (head, count) in generated_query_occurrences {
5631 if count != 1 {
5632 return Err(resident_workspace_decline(format!(
5633 "generated query head {head} must have exactly one compiled rule, found {count}"
5634 )));
5635 }
5636 }
5637
5638 let (required_row_capacity, capacity_witness) =
5639 self.resident_required_row_capacity(plan, certificate)?;
5640 let required_row_capacity = required_row_capacity.max(1);
5641 let required_row_capacity = u32::try_from(required_row_capacity).map_err(|_| {
5642 ResidentGraphDeclineReason::WorkspaceUnbounded {
5643 detail: format!(
5644 "certified resident row bound {required_row_capacity} exceeds the u32 count ABI; witness: {capacity_witness}"
5645 ),
5646 }
5647 })?;
5648 let relation_capacity = checked_capacity_class(required_row_capacity).map_err(|error| {
5649 ResidentGraphDeclineReason::WorkspaceUnbounded {
5650 detail: format!(
5651 "{error}; certified_required_rows={required_row_capacity}; witness: {capacity_witness}"
5652 ),
5653 }
5654 })?;
5655
5656 let mut head_schemas = BTreeMap::<String, Schema>::new();
5657 for rules in &plan.rules_by_scc {
5658 for rule in rules {
5659 match head_schemas.get(&rule.head) {
5660 Some(existing) if existing != &rule.meta.schema => {
5661 return Err(ResidentGraphDeclineReason::WorkspaceUnbounded {
5662 detail: format!(
5663 "relation {} has inconsistent compiled head schemas",
5664 rule.head
5665 ),
5666 });
5667 }
5668 Some(_) => {}
5669 None => {
5670 head_schemas.insert(rule.head.clone(), rule.meta.schema.clone());
5671 }
5672 }
5673 }
5674 }
5675
5676 let mut head_schema_choices = head_schemas
5677 .keys()
5678 .map(|name| {
5679 let mut candidates = Vec::new();
5680 if let Some(existing) = self.store.get(name) {
5681 let physical = &head_schemas[name];
5682 if !resident_schemas_type_compatible(existing.schema(), physical) {
5683 return Err(resident_workspace_decline(format!(
5684 "existing resident head {name} is physically incompatible with its compiled schema"
5685 )));
5686 }
5687 if !(generated_query_heads.contains(name)
5688 && existing.cached_row_count() == Some(0))
5689 {
5690 candidates.push(existing.schema().clone());
5691 }
5692 }
5693 Ok((name.clone(), candidates))
5694 })
5695 .collect::<std::result::Result<BTreeMap<_, _>, _>>()?;
5696 let mut rule_schema_ids = plan
5697 .rules_by_scc
5698 .iter()
5699 .map(|rules| vec![None; rules.len()])
5700 .collect::<Vec<_>>();
5701 let mut head_schema_selections = BTreeMap::new();
5702
5703 for (scc_index, rules) in plan.rules_by_scc.iter().enumerate() {
5704 let recursive = plan.sccs.get(scc_index).is_some_and(|scc| scc.is_recursive);
5705 for (rule_index, rule) in rules.iter().enumerate() {
5706 if generated_query_heads.contains(&rule.head) {
5707 continue;
5708 }
5709 let variants =
5710 self.resident_schema_variants(&rule.body, certificate, &head_schema_choices)?;
5711 let physical = &head_schemas[&rule.head];
5712 let validate_schema = |schema: &Schema| {
5713 if !resident_schemas_type_compatible(schema, physical) {
5714 return Err(resident_workspace_decline(format!(
5715 "resident rule contribution for {} is physically incompatible with its compiled head",
5716 rule.head
5717 )));
5718 }
5719 if recursive {
5720 let rel = self.name_to_rel.get(&rule.head).ok_or_else(|| {
5721 resident_workspace_decline(format!(
5722 "recursive resident head {} is not registered",
5723 rule.head
5724 ))
5725 })?;
5726 let candidate = certificate.schema_for(*rel).ok_or_else(|| {
5727 resident_workspace_decline(format!(
5728 "recursive resident head {} has no catalog schema candidate",
5729 rule.head
5730 ))
5731 })?;
5732 if !resident_schemas_type_compatible(schema, candidate) {
5733 return Err(resident_workspace_decline(format!(
5734 "recursive resident head {} contribution schema is physically incompatible with its catalog candidate: contribution={schema:?}, catalog={candidate:?}",
5735 rule.head
5736 )));
5737 }
5738 if schema.key_columns != candidate.key_columns {
5739 return Err(resident_workspace_decline(format!(
5740 "recursive resident head {} contribution key columns do not equal its catalog candidate: contribution={:?}, catalog={:?}",
5741 rule.head, schema.key_columns, candidate.key_columns
5742 )));
5743 }
5744 }
5745 Ok(())
5746 };
5747 match &variants {
5748 ResidentSchemaVariants::Fixed(schema) => validate_schema(schema)?,
5749 ResidentSchemaVariants::Dynamic { schemas, .. } => {
5750 for schema in schemas {
5751 validate_schema(schema)?;
5752 }
5753 }
5754 }
5755 match variants {
5756 ResidentSchemaVariants::Fixed(schema) => {
5757 let schema_id = resident_intern_schema(
5758 head_schema_choices
5759 .get_mut(&rule.head)
5760 .expect("compiled head choice exists"),
5761 schema,
5762 )
5763 .map_err(|error| resident_workspace_decline(error.to_string()))?;
5764 rule_schema_ids[scc_index][rule_index] = Some(schema_id);
5765 }
5766 ResidentSchemaVariants::Dynamic {
5767 source_head,
5768 schemas,
5769 } => {
5770 resident_register_schema_selection(
5771 &rule.head,
5772 ResidentHeadSchemaSelection {
5773 source_head,
5774 output_schemas_by_source_winner: schemas.clone(),
5775 },
5776 &head_schema_choices,
5777 &mut head_schema_selections,
5778 )?;
5779 for schema in schemas {
5780 resident_intern_schema(
5781 head_schema_choices
5782 .get_mut(&rule.head)
5783 .expect("compiled head choice exists"),
5784 schema,
5785 )
5786 .map_err(|error| resident_workspace_decline(error.to_string()))?;
5787 }
5788 rule_schema_ids[scc_index][rule_index] = Some(RESIDENT_DYNAMIC_SCHEMA_ID);
5789 }
5790 }
5791 }
5792 }
5793
5794 let mut seen_query_heads = HashSet::new();
5795 for (scc_index, rules) in plan.rules_by_scc.iter().enumerate() {
5796 for (rule_index, rule) in rules.iter().enumerate() {
5797 if !generated_query_heads.contains(&rule.head) {
5798 continue;
5799 }
5800 if !seen_query_heads.insert(rule.head.clone()) {
5801 return Err(resident_workspace_decline(format!(
5802 "synthetic query head {} has more than one compiled rule",
5803 rule.head
5804 )));
5805 }
5806 let variants =
5807 self.resident_schema_variants(&rule.body, certificate, &head_schema_choices)?;
5808 let physical = &head_schemas[&rule.head];
5809 match variants {
5810 ResidentSchemaVariants::Fixed(schema) => {
5811 if !resident_schemas_type_compatible(&schema, physical) {
5812 return Err(resident_workspace_decline(format!(
5813 "synthetic query head {} is physically incompatible with its compiled head",
5814 rule.head
5815 )));
5816 }
5817 let schema_id = resident_intern_schema(
5818 head_schema_choices
5819 .get_mut(&rule.head)
5820 .expect("compiled query head choice exists"),
5821 schema,
5822 )
5823 .map_err(|error| resident_workspace_decline(error.to_string()))?;
5824 rule_schema_ids[scc_index][rule_index] = Some(schema_id);
5825 }
5826 ResidentSchemaVariants::Dynamic {
5827 source_head,
5828 schemas,
5829 } => {
5830 if schemas
5831 .iter()
5832 .any(|schema| !resident_schemas_type_compatible(schema, physical))
5833 {
5834 return Err(resident_workspace_decline(format!(
5835 "synthetic query head {} has a physically incompatible dynamic schema",
5836 rule.head
5837 )));
5838 }
5839 for schema in &schemas {
5840 resident_intern_schema(
5841 head_schema_choices
5842 .get_mut(&rule.head)
5843 .expect("compiled query head choice exists"),
5844 schema.clone(),
5845 )
5846 .map_err(|error| resident_workspace_decline(error.to_string()))?;
5847 }
5848 rule_schema_ids[scc_index][rule_index] = Some(RESIDENT_DYNAMIC_SCHEMA_ID);
5849 resident_register_schema_selection(
5850 &rule.head,
5851 ResidentHeadSchemaSelection {
5852 source_head,
5853 output_schemas_by_source_winner: schemas,
5854 },
5855 &head_schema_choices,
5856 &mut head_schema_selections,
5857 )?;
5858 }
5859 }
5860 }
5861 }
5862
5863 for (name, choices) in &head_schema_choices {
5864 if choices.is_empty() {
5865 return Err(resident_workspace_decline(format!(
5866 "resident head {name} has no schema candidate"
5867 )));
5868 }
5869 }
5870 Ok(ResidentWorkspaceAdmission {
5871 relation_capacity,
5872 head_schemas,
5873 head_schema_choices,
5874 rule_schema_ids,
5875 head_schema_selections,
5876 })
5877 }
5878
5879 fn resident_required_row_capacity(
5880 &self,
5881 plan: &ExecutionPlan,
5882 certificate: &ResidentGraphRouteCertificate,
5883 ) -> std::result::Result<(u64, String), ResidentGraphDeclineReason> {
5884 let mut relation_domains = HashMap::<RelId, ResidentColumnDomains>::new();
5885 let mut synthetic_domains = HashMap::<String, ResidentColumnDomains>::new();
5886 let mut source_rows = HashMap::<RelId, u64>::new();
5887 let plan_relations =
5888 resident_plan_relation_ids(plan.rules_by_scc.iter().flatten(), &self.name_to_rel);
5889 for (relation, name) in &self.rel_names {
5890 if !plan_relations.contains(relation) {
5891 continue;
5892 }
5893 let schema = certificate.schema_for(*relation).ok_or_else(|| {
5894 resident_workspace_decline(format!(
5895 "relation {name} has no schema in the resident certificate"
5896 ))
5897 })?;
5898 let mut columns = vec![BTreeMap::new(); schema.arity()];
5899 let rows = self.store.get(name).map(CudaBuffer::num_rows).unwrap_or(0);
5900 if rows != 0 {
5901 for (column, domain) in columns.iter_mut().enumerate() {
5902 domain.insert(format!("source:{}:{column}", relation.0), rows);
5903 }
5904 }
5905 relation_domains.insert(*relation, columns);
5906 source_rows.insert(*relation, rows);
5907 }
5908
5909 loop {
5910 let mut changed = false;
5911 for rules in &plan.rules_by_scc {
5912 for rule in rules {
5913 let contribution = resident_node_domains(&rule.body, &relation_domains)?;
5914 if contribution.len() != rule.meta.schema.arity() {
5915 return Err(resident_workspace_decline(format!(
5916 "resident rule head {} has {} certified domains for schema arity {}",
5917 rule.head,
5918 contribution.len(),
5919 rule.meta.schema.arity()
5920 )));
5921 }
5922 let target = if let Some(head) = self.name_to_rel.get(&rule.head) {
5923 relation_domains.get_mut(head).ok_or_else(|| {
5924 resident_workspace_decline(format!(
5925 "resident rule head {} has no domain slot",
5926 rule.head
5927 ))
5928 })?
5929 } else {
5930 synthetic_domains
5931 .entry(rule.head.clone())
5932 .or_insert_with(|| vec![BTreeMap::new(); rule.meta.schema.arity()])
5933 };
5934 changed |= merge_resident_domains(&rule.head, target, contribution)?;
5935 }
5936 }
5937 if !changed {
5938 break;
5939 }
5940 }
5941
5942 let mut relation_set_bounds = source_rows.clone();
5943 let mut synthetic_set_bounds = HashMap::<String, u64>::new();
5944 let mut required = source_rows.values().copied().max().unwrap_or(1).max(1);
5945 let mut witness = source_rows
5946 .iter()
5947 .max_by_key(|(_, rows)| *rows)
5948 .map(|(relation, rows)| {
5949 let name = self
5950 .rel_names
5951 .get(relation)
5952 .map(String::as_str)
5953 .unwrap_or("<unregistered>");
5954 format!("source relation {name} ({relation:?}) rows={rows}")
5955 })
5956 .unwrap_or_else(|| "unit relation bound=1".to_string());
5957
5958 let mut scc_by_id = HashMap::new();
5959 for (index, scc) in plan.sccs.iter().enumerate() {
5960 if scc_by_id.insert(scc.id, index).is_some() {
5961 return Err(resident_workspace_decline(format!(
5962 "resident row proof found duplicate SCC id {}",
5963 scc.id
5964 )));
5965 }
5966 }
5967 let ordered_sccs = if plan.strata.is_empty() {
5968 (0..plan.sccs.len()).collect::<Vec<_>>()
5969 } else {
5970 let mut ordered = Vec::new();
5971 for stratum in &plan.strata {
5972 for scc_id in &stratum.sccs {
5973 ordered.push(*scc_by_id.get(scc_id).ok_or_else(|| {
5974 resident_workspace_decline(format!(
5975 "resident row proof stratum {} references unknown SCC {scc_id}",
5976 stratum.id
5977 ))
5978 })?);
5979 }
5980 }
5981 ordered
5982 };
5983 let mut seen_sccs = HashSet::new();
5984 for scc_index in ordered_sccs {
5985 if !seen_sccs.insert(scc_index) {
5986 return Err(resident_workspace_decline(format!(
5987 "resident row proof schedule repeats SCC {}",
5988 plan.sccs[scc_index].id
5989 )));
5990 }
5991 let scc = &plan.sccs[scc_index];
5992 let rules = plan.rules_by_scc.get(scc_index).ok_or_else(|| {
5993 resident_workspace_decline(format!(
5994 "resident row proof SCC {scc_index} has no rule vector"
5995 ))
5996 })?;
5997
5998 if scc.is_recursive {
5999 for name in &scc.predicates {
6000 let relation = self.name_to_rel.get(name).ok_or_else(|| {
6001 resident_workspace_decline(format!(
6002 "recursive relation {name} has no registered relation id"
6003 ))
6004 })?;
6005 let columns = relation_domains.get(relation).ok_or_else(|| {
6006 resident_workspace_decline(format!(
6007 "recursive relation {relation:?} has no active-domain proof"
6008 ))
6009 })?;
6010 let context = format!("recursive relation {name} ({relation:?})");
6011 let finite_cap = resident_domain_product(columns, &context)?;
6012 relation_set_bounds.insert(*relation, finite_cap);
6013 if finite_cap > required {
6014 required = finite_cap;
6015 witness = format!(
6016 "{context} finite_domain_cap={finite_cap} domains={}",
6017 resident_domain_description(columns)
6018 );
6019 }
6020 }
6021 }
6022
6023 let mut additions = BTreeMap::<String, u64>::new();
6024 let mut raw_additions = BTreeMap::<String, u64>::new();
6025 let mut addition_witnesses = BTreeMap::<String, String>::new();
6026 for (rule_index, rule) in rules.iter().enumerate() {
6027 let path = format!("scc[{scc_index}].rule[{rule_index}] head={}", rule.head);
6028 let proof = resident_node_row_bound(
6029 &rule.body,
6030 &source_rows,
6031 &relation_set_bounds,
6032 &relation_domains,
6033 &path,
6034 )?;
6035 let rows = proof.rows;
6036 let transient = proof.peak;
6037 let rule_domains = resident_node_domains(&rule.body, &relation_domains)?;
6038 let set_rows = resident_domain_product_capped(
6039 &rule_domains,
6040 rows,
6041 &format!("{path} projected set"),
6042 )?;
6043 if transient > required {
6044 required = transient;
6045 witness = format!(
6046 "{path} transient_bound={transient} proof={} body={:?}",
6047 proof.peak_detail, rule.body
6048 );
6049 }
6050 let total = additions.entry(rule.head.clone()).or_default();
6051 *total = total.checked_add(set_rows).ok_or_else(|| {
6052 resident_workspace_decline(format!(
6053 "{path} rule-output addition overflow: partial={total} addition={set_rows} raw_rows={rows}"
6054 ))
6055 })?;
6056 let raw_total = raw_additions.entry(rule.head.clone()).or_default();
6057 *raw_total = raw_total.checked_add(rows).ok_or_else(|| {
6058 resident_workspace_decline(format!(
6059 "{path} raw rule-output addition overflow: partial={raw_total} addition={rows}"
6060 ))
6061 })?;
6062 addition_witnesses.insert(
6063 rule.head.clone(),
6064 format!(
6065 "{path} body={:?} raw_rows={rows} per_clause_set_cap={set_rows} domains={}",
6066 rule.body,
6067 resident_domain_description(&rule_domains)
6068 ),
6069 );
6070 }
6071
6072 for (head, addition) in additions {
6073 let (current, relation) = if let Some(relation) = self.name_to_rel.get(&head) {
6074 (
6075 relation_set_bounds.get(relation).copied().unwrap_or(0),
6076 Some(*relation),
6077 )
6078 } else {
6079 (synthetic_set_bounds.get(&head).copied().unwrap_or(0), None)
6080 };
6081 let path = addition_witnesses
6082 .get(&head)
6083 .map(String::as_str)
6084 .unwrap_or("unknown rule");
6085 let raw_addition = raw_additions.get(&head).copied().unwrap_or(0);
6086 let raw_candidate = current.checked_add(raw_addition).ok_or_else(|| {
6087 resident_workspace_decline(format!(
6088 "{path} staged candidate addition overflow: prior={current} raw_rule_rows={raw_addition}"
6089 ))
6090 })?;
6091 if raw_candidate > required {
6092 required = raw_candidate;
6093 witness = format!(
6094 "{path} staged_candidate_bound={raw_candidate} prior={current} raw_rule_rows={raw_addition}"
6095 );
6096 }
6097 if scc.is_recursive {
6098 let candidate = current.checked_add(addition).ok_or_else(|| {
6099 resident_workspace_decline(format!(
6100 "{path} recursive set candidate addition overflow: finite_cap={current} set_rule_rows={addition}"
6101 ))
6102 })?;
6103 if candidate > required {
6104 required = candidate;
6105 witness = format!(
6106 "{path} recursive_candidate_bound={candidate} finite_cap={current} raw_rule_rows={addition}"
6107 );
6108 }
6109 } else {
6110 let summed_bound = current.checked_add(addition).ok_or_else(|| {
6111 resident_workspace_decline(format!(
6112 "{path} nonrecursive set addition overflow: prior={current} rule_rows={addition}"
6113 ))
6114 })?;
6115 let columns = if let Some(relation) = relation {
6116 relation_domains.get(&relation)
6117 } else {
6118 synthetic_domains.get(&head)
6119 }
6120 .ok_or_else(|| {
6121 resident_workspace_decline(format!(
6122 "{path} nonrecursive head {head} has no active-domain proof"
6123 ))
6124 })?;
6125 let set_bound = resident_domain_product_capped(
6126 columns,
6127 summed_bound,
6128 &format!("{path} merged set {head}"),
6129 )?;
6130 if let Some(relation) = relation {
6131 relation_set_bounds.insert(relation, set_bound);
6132 } else {
6133 synthetic_set_bounds.insert(head.clone(), set_bound);
6134 }
6135 if set_bound > required {
6136 required = set_bound;
6137 witness = format!(
6138 "{path} nonrecursive_set_bound={set_bound} prior={current} rule_rows={addition}"
6139 );
6140 }
6141 }
6142 }
6143 }
6144 if seen_sccs.len() != plan.sccs.len() {
6145 return Err(resident_workspace_decline(
6146 "resident row proof schedule omits one or more SCCs",
6147 ));
6148 }
6149 Ok((required, witness))
6150 }
6151}
6152
6153type ResidentDomain = BTreeMap<String, u64>;
6154type ResidentColumnDomains = Vec<ResidentDomain>;
6155
6156fn resident_plan_relation_ids<'a>(
6157 rules: impl Iterator<Item = &'a CompiledRule>,
6158 name_to_rel: &HashMap<String, RelId>,
6159) -> HashSet<RelId> {
6160 let mut relations = HashSet::new();
6161 for rule in rules {
6162 relations.extend(rule.body.referenced_relations());
6163 if let Some(head) = name_to_rel.get(&rule.head) {
6164 relations.insert(*head);
6165 }
6166 }
6167 relations
6168}
6169
6170fn resident_workspace_decline(detail: impl Into<String>) -> ResidentGraphDeclineReason {
6171 ResidentGraphDeclineReason::WorkspaceUnbounded {
6172 detail: detail.into(),
6173 }
6174}
6175
6176fn merge_resident_domains(
6177 relation: &str,
6178 target: &mut ResidentColumnDomains,
6179 contribution: ResidentColumnDomains,
6180) -> std::result::Result<bool, ResidentGraphDeclineReason> {
6181 if target.len() != contribution.len() {
6182 return Err(resident_workspace_decline(format!(
6183 "resident rule head {relation} domain arity changed"
6184 )));
6185 }
6186 let mut changed = false;
6187 for (target_domain, contribution_domain) in target.iter_mut().zip(contribution) {
6188 for (source, bound) in contribution_domain {
6189 match target_domain.entry(source) {
6190 std::collections::btree_map::Entry::Vacant(entry) => {
6191 entry.insert(bound);
6192 changed = true;
6193 }
6194 std::collections::btree_map::Entry::Occupied(mut entry) if *entry.get() < bound => {
6195 entry.insert(bound);
6196 changed = true;
6197 }
6198 std::collections::btree_map::Entry::Occupied(_) => {}
6199 }
6200 }
6201 }
6202 Ok(changed)
6203}
6204
6205fn resident_node_domains(
6206 node: &RirNode,
6207 relations: &HashMap<RelId, ResidentColumnDomains>,
6208) -> std::result::Result<ResidentColumnDomains, ResidentGraphDeclineReason> {
6209 match node {
6210 RirNode::Unit => Ok(Vec::new()),
6211 RirNode::Scan { rel } => relations.get(rel).cloned().ok_or_else(|| {
6212 resident_workspace_decline(format!(
6213 "resident scan {rel:?} has no source-domain certificate"
6214 ))
6215 }),
6216 RirNode::Filter { input, predicate } => {
6217 let mut domains = resident_node_domains(input, relations)?;
6218 resident_refine_filter_domains(&mut domains, predicate)?;
6219 Ok(domains)
6220 }
6221 RirNode::Distinct { input, .. } => resident_node_domains(input, relations),
6222 RirNode::Project { input, columns } => {
6223 let input_domains = resident_node_domains(input, relations)?;
6224 resident_project_domains(&input_domains, columns)
6225 }
6226 RirNode::Join {
6227 left,
6228 right,
6229 join_type,
6230 ..
6231 } => {
6232 let mut left_domains = resident_node_domains(left, relations)?;
6233 match join_type {
6234 JoinType::Inner => {
6235 left_domains.extend(resident_node_domains(right, relations)?);
6236 Ok(left_domains)
6237 }
6238 JoinType::Semi => Ok(left_domains),
6239 other => Err(resident_workspace_decline(format!(
6240 "resident domain proof does not support {other:?} joins"
6241 ))),
6242 }
6243 }
6244 RirNode::ChainJoin { fallback, .. } | RirNode::MultiWayJoin { fallback, .. } => {
6245 resident_node_domains(fallback, relations)
6246 }
6247 RirNode::Union { inputs } => {
6248 let mut merged: Option<ResidentColumnDomains> = None;
6249 for input in inputs {
6250 let contribution = resident_node_domains(input, relations)?;
6251 if let Some(domains) = merged.as_mut() {
6252 if domains.len() != contribution.len() {
6253 return Err(resident_workspace_decline(
6254 "resident union domain arity mismatch",
6255 ));
6256 }
6257 for (domain, addition) in domains.iter_mut().zip(contribution) {
6258 for (source, bound) in addition {
6259 domain
6260 .entry(source)
6261 .and_modify(|current| *current = (*current).max(bound))
6262 .or_insert(bound);
6263 }
6264 }
6265 } else {
6266 merged = Some(contribution);
6267 }
6268 }
6269 merged.ok_or_else(|| resident_workspace_decline("resident union has no inputs"))
6270 }
6271 RirNode::Diff { left, .. } => resident_node_domains(left, relations),
6272 RirNode::GroupBy { .. } | RirNode::Fixpoint { .. } | RirNode::TensorMaskedJoin { .. } => {
6273 Err(resident_workspace_decline(
6274 "resident domain proof encountered an uncertified operator",
6275 ))
6276 }
6277 }
6278}
6279
6280fn resident_refine_filter_domains(
6281 domains: &mut ResidentColumnDomains,
6282 predicate: &Expr,
6283) -> std::result::Result<(), ResidentGraphDeclineReason> {
6284 match predicate {
6285 Expr::And(parts) => {
6286 for part in parts {
6287 resident_refine_filter_domains(domains, part)?;
6288 }
6289 }
6290 Expr::Compare {
6291 left,
6292 op: RirCompareOp::Eq,
6293 right,
6294 } => {
6295 let (column, value) = match (&**left, &**right) {
6296 (Expr::Column(column), Expr::Const(value))
6297 | (Expr::Const(value), Expr::Column(column)) => (*column, value),
6298 _ => return Ok(()),
6299 };
6300 let arity = domains.len();
6301 let domain = domains.get_mut(column).ok_or_else(|| {
6302 resident_workspace_decline(format!(
6303 "resident equality filter column {column} exceeds input arity {}",
6304 arity
6305 ))
6306 })?;
6307 *domain = BTreeMap::from([(format!("constant:{value:?}"), 1)]);
6308 }
6309 _ => {}
6310 }
6311 Ok(())
6312}
6313
6314fn resident_project_domains(
6315 input: &[ResidentDomain],
6316 columns: &[ProjectExpr],
6317) -> std::result::Result<ResidentColumnDomains, ResidentGraphDeclineReason> {
6318 columns
6319 .iter()
6320 .map(|column| match column {
6321 ProjectExpr::Column(index) => input.get(*index).cloned().ok_or_else(|| {
6322 resident_workspace_decline(format!(
6323 "resident projection column {index} exceeds input arity {}",
6324 input.len()
6325 ))
6326 }),
6327 ProjectExpr::Computed(Expr::Const(value), scalar) => Ok(BTreeMap::from([(
6328 format!("constant:{scalar:?}:{value:?}"),
6329 1,
6330 )])),
6331 _ => Err(resident_workspace_decline(
6332 "resident projection domain is not a column or constant",
6333 )),
6334 })
6335 .collect()
6336}
6337
6338fn resident_domain_product(
6339 columns: &[ResidentDomain],
6340 context: &str,
6341) -> std::result::Result<u64, ResidentGraphDeclineReason> {
6342 if columns.is_empty() {
6343 return Ok(1);
6344 }
6345 columns
6346 .iter()
6347 .enumerate()
6348 .try_fold(1u64, |product, (column_index, domain)| {
6349 let values = domain.values().try_fold(0u64, |total, bound| {
6350 total.checked_add(*bound).ok_or_else(|| {
6351 resident_workspace_decline(format!(
6352 "{context} active-domain sum overflow at column {column_index}: partial={total} addition={bound} lineage={domain:?}"
6353 ))
6354 })
6355 })?;
6356 product.checked_mul(values).ok_or_else(|| {
6357 resident_workspace_decline(format!(
6358 "{context} active-domain product overflow at column {column_index}: partial={product} factor={values} lineage={domain:?}"
6359 ))
6360 })
6361 })
6362}
6363
6364fn resident_domain_product_capped(
6365 columns: &[ResidentDomain],
6366 cap: u64,
6367 context: &str,
6368) -> std::result::Result<u64, ResidentGraphDeclineReason> {
6369 if cap == 0 {
6370 return Ok(0);
6371 }
6372 if columns.is_empty() {
6373 return Ok(1);
6374 }
6375 let mut product = 1u64;
6376 for (column_index, domain) in columns.iter().enumerate() {
6377 let mut values = 0u64;
6378 for bound in domain.values() {
6379 let addition = (*bound).min(cap.saturating_sub(values));
6380 values = values.checked_add(addition).ok_or_else(|| {
6381 resident_workspace_decline(format!(
6382 "{context} capped active-domain sum overflow at column {column_index}: partial={values} addition={addition}"
6383 ))
6384 })?;
6385 if values == cap {
6386 break;
6387 }
6388 }
6389 if values == 0 {
6390 return Ok(0);
6391 }
6392 if product > cap / values {
6393 return Ok(cap);
6394 }
6395 product = product.checked_mul(values).ok_or_else(|| {
6396 resident_workspace_decline(format!(
6397 "{context} capped active-domain product overflow at column {column_index}: partial={product} factor={values}"
6398 ))
6399 })?;
6400 }
6401 Ok(product.min(cap))
6402}
6403
6404fn resident_domain_description(columns: &[ResidentDomain]) -> String {
6405 columns
6406 .iter()
6407 .enumerate()
6408 .map(|(column, domain)| {
6409 let sum = domain.values().copied().fold(0u64, u64::saturating_add);
6410 format!("column[{column}] sum={sum} lineage={domain:?}")
6411 })
6412 .collect::<Vec<_>>()
6413 .join("; ")
6414}
6415
6416#[derive(Clone, Debug, PartialEq, Eq)]
6417enum ResidentCapacityBound {
6418 Finite { rows: u64, proof: String },
6419 AboveResidentLimit { proof: String },
6420}
6421
6422fn resident_capacity_product_bound(proof: &'static str, factors: &[u64]) -> ResidentCapacityBound {
6423 if factors.contains(&0) {
6424 return ResidentCapacityBound::Finite {
6425 rows: 0,
6426 proof: proof.to_owned(),
6427 };
6428 }
6429
6430 let limit = u64::from(MAX_RESIDENT_CAPACITY);
6431 let mut rows = 1u64;
6432 for factor in factors {
6433 if rows > limit / factor {
6434 return ResidentCapacityBound::AboveResidentLimit {
6435 proof: format!("{proof} factors={factors:?}"),
6436 };
6437 }
6438 rows *= factor;
6439 }
6440 ResidentCapacityBound::Finite {
6441 rows,
6442 proof: proof.to_owned(),
6443 }
6444}
6445
6446fn resident_join_capacity_bound(
6447 left_rows: u64,
6448 right_rows: u64,
6449 left_fanout: u64,
6450 right_fanout: u64,
6451 matching_key_values: u64,
6452) -> ResidentCapacityBound {
6453 let candidates = [
6454 resident_capacity_product_bound("left_rows*right_rows", &[left_rows, right_rows]),
6455 resident_capacity_product_bound("left_rows*right_fanout", &[left_rows, right_fanout]),
6456 resident_capacity_product_bound("right_rows*left_fanout", &[right_rows, left_fanout]),
6457 resident_capacity_product_bound(
6458 "matching_keys*left_fanout*right_fanout",
6459 &[matching_key_values, left_fanout, right_fanout],
6460 ),
6461 ];
6462
6463 let mut tightest: Option<(u64, String)> = None;
6464 let mut above = Vec::new();
6465 for candidate in candidates {
6466 match candidate {
6467 ResidentCapacityBound::Finite { rows, proof } => {
6468 if tightest.as_ref().is_none_or(|(current, _)| rows < *current) {
6469 tightest = Some((rows, proof));
6470 }
6471 }
6472 ResidentCapacityBound::AboveResidentLimit { proof } => above.push(proof),
6473 }
6474 }
6475 if let Some((rows, proof)) = tightest {
6476 ResidentCapacityBound::Finite { rows, proof }
6477 } else {
6478 ResidentCapacityBound::AboveResidentLimit {
6479 proof: above.join("; "),
6480 }
6481 }
6482}
6483
6484struct ResidentRowProof {
6485 rows: u64,
6486 peak: u64,
6487 domains: ResidentColumnDomains,
6488 full_row_unique: bool,
6489 peak_detail: String,
6490}
6491
6492fn resident_node_row_bound(
6493 node: &RirNode,
6494 source_rows: &HashMap<RelId, u64>,
6495 relation_set_bounds: &HashMap<RelId, u64>,
6496 relation_domains: &HashMap<RelId, ResidentColumnDomains>,
6497 path: &str,
6498) -> std::result::Result<ResidentRowProof, ResidentGraphDeclineReason> {
6499 match node {
6500 RirNode::Unit => Ok(ResidentRowProof {
6501 rows: 1,
6502 peak: 1,
6503 domains: Vec::new(),
6504 full_row_unique: true,
6505 peak_detail: format!("{path}.unit rows=1"),
6506 }),
6507 RirNode::Scan { rel } => {
6508 let rows = source_rows
6509 .get(rel)
6510 .copied()
6511 .unwrap_or(0)
6512 .max(relation_set_bounds.get(rel).copied().unwrap_or(0));
6513 let domains = relation_domains.get(rel).cloned().ok_or_else(|| {
6514 resident_workspace_decline(format!(
6515 "{path}.scan {rel:?} has no active-domain proof"
6516 ))
6517 })?;
6518 Ok(ResidentRowProof {
6519 rows,
6520 peak: rows,
6521 domains,
6522 full_row_unique: true,
6523 peak_detail: format!("{path}.scan rel={rel:?} rows={rows}"),
6524 })
6525 }
6526 RirNode::Filter { input, predicate } => {
6527 let mut proof = resident_node_row_bound(
6528 input,
6529 source_rows,
6530 relation_set_bounds,
6531 relation_domains,
6532 &format!("{path}.filter.input"),
6533 )?;
6534 resident_refine_filter_domains(&mut proof.domains, predicate)?;
6535 if proof.full_row_unique {
6536 proof.rows = resident_domain_product_capped(
6537 &proof.domains,
6538 proof.rows,
6539 &format!("{path}.filter"),
6540 )?;
6541 }
6542 Ok(proof)
6543 }
6544 RirNode::Project { input, columns } => {
6545 let input = resident_node_row_bound(
6546 input,
6547 source_rows,
6548 relation_set_bounds,
6549 relation_domains,
6550 &format!("{path}.project.input"),
6551 )?;
6552 let domains = resident_project_domains(&input.domains, columns)?;
6553 let full_row_unique = input.full_row_unique
6554 && resident_projection_is_injective(&input.domains, columns, path)?;
6555 Ok(ResidentRowProof {
6556 rows: input.rows,
6557 peak: input.peak.max(input.rows),
6558 domains,
6559 full_row_unique,
6560 peak_detail: input.peak_detail,
6561 })
6562 }
6563 RirNode::Distinct { input, .. } => {
6564 let mut proof = resident_node_row_bound(
6565 input,
6566 source_rows,
6567 relation_set_bounds,
6568 relation_domains,
6569 &format!("{path}.distinct.input"),
6570 )?;
6571 proof.rows = resident_domain_product_capped(
6572 &proof.domains,
6573 proof.rows,
6574 &format!("{path}.distinct"),
6575 )?;
6576 proof.full_row_unique = true;
6577 Ok(proof)
6578 }
6579 RirNode::Join {
6580 left,
6581 right,
6582 left_keys,
6583 right_keys,
6584 join_type,
6585 } => {
6586 let left = resident_node_row_bound(
6587 left,
6588 source_rows,
6589 relation_set_bounds,
6590 relation_domains,
6591 &format!("{path}.join.left"),
6592 )?;
6593 let right = resident_node_row_bound(
6594 right,
6595 source_rows,
6596 relation_set_bounds,
6597 relation_domains,
6598 &format!("{path}.join.right"),
6599 )?;
6600 match join_type {
6601 JoinType::Semi => Ok(ResidentRowProof {
6602 rows: left.rows,
6603 peak: left.peak.max(right.peak).max(left.rows),
6604 domains: left.domains,
6605 full_row_unique: left.full_row_unique,
6606 peak_detail: if left.peak >= right.peak {
6607 left.peak_detail
6608 } else {
6609 right.peak_detail
6610 },
6611 }),
6612 JoinType::Inner => {
6613 if left_keys.len() != 1 || right_keys.len() != 1 {
6614 return Err(resident_workspace_decline(format!(
6615 "{path} resident row proof requires exactly one join key per side"
6616 )));
6617 }
6618 let left_key = left_keys[0];
6619 let right_key = right_keys[0];
6620 let left_fanout = resident_key_fanout_bound(
6621 &left.domains,
6622 left_key,
6623 left.rows,
6624 left.full_row_unique,
6625 &format!("{path}.join.left"),
6626 )?;
6627 let right_fanout = resident_key_fanout_bound(
6628 &right.domains,
6629 right_key,
6630 right.rows,
6631 right.full_row_unique,
6632 &format!("{path}.join.right"),
6633 )?;
6634 let left_key_values = resident_domain_cardinality_capped(
6635 left.domains.get(left_key).ok_or_else(|| {
6636 resident_workspace_decline(format!(
6637 "{path} left join key {left_key} exceeds arity {}",
6638 left.domains.len()
6639 ))
6640 })?,
6641 left.rows,
6642 &format!("{path}.join.left_key"),
6643 )?;
6644 let right_key_values = resident_domain_cardinality_capped(
6645 right.domains.get(right_key).ok_or_else(|| {
6646 resident_workspace_decline(format!(
6647 "{path} right join key {right_key} exceeds arity {}",
6648 right.domains.len()
6649 ))
6650 })?,
6651 right.rows,
6652 &format!("{path}.join.right_key"),
6653 )?;
6654 let matching_key_values = left_key_values.min(right_key_values);
6655 let (rows, bound_proof) = match resident_join_capacity_bound(
6656 left.rows,
6657 right.rows,
6658 left_fanout,
6659 right_fanout,
6660 matching_key_values,
6661 ) {
6662 ResidentCapacityBound::Finite { rows, proof } => (rows, proof),
6663 ResidentCapacityBound::AboveResidentLimit { proof } => {
6664 return Err(resident_workspace_decline(format!(
6665 "{path} inner join exceeds the fixed resident capacity limit: left_rows={} right_rows={} left_fanout={left_fanout} right_fanout={right_fanout} matching_keys={matching_key_values} products={proof}",
6666 left.rows, right.rows
6667 )));
6668 }
6669 };
6670 let join_detail = format!(
6671 "{path}.join left_rows={} right_rows={} left_fanout={left_fanout} right_fanout={right_fanout} matching_keys={matching_key_values} bound={rows} proof={bound_proof}",
6672 left.rows, right.rows
6673 );
6674 let (peak, peak_detail) = if rows >= left.peak && rows >= right.peak {
6675 (rows, join_detail)
6676 } else if left.peak >= right.peak {
6677 (left.peak, left.peak_detail)
6678 } else {
6679 (right.peak, right.peak_detail)
6680 };
6681 let mut domains = left.domains;
6682 domains.extend(right.domains);
6683 Ok(ResidentRowProof {
6684 rows,
6685 peak,
6686 domains,
6687 full_row_unique: left.full_row_unique && right.full_row_unique,
6688 peak_detail,
6689 })
6690 }
6691 other => Err(resident_workspace_decline(format!(
6692 "resident row proof does not support {other:?} joins"
6693 ))),
6694 }
6695 }
6696 RirNode::ChainJoin { fallback, .. } | RirNode::MultiWayJoin { fallback, .. } => {
6697 resident_node_row_bound(
6698 fallback,
6699 source_rows,
6700 relation_set_bounds,
6701 relation_domains,
6702 &format!("{path}.fallback"),
6703 )
6704 }
6705 RirNode::Union { inputs } => {
6706 let mut rows = 0u64;
6707 let mut peak = 0u64;
6708 let mut peak_detail = format!("{path}.union empty");
6709 let mut domains: Option<ResidentColumnDomains> = None;
6710 for (index, input) in inputs.iter().enumerate() {
6711 let proof = resident_node_row_bound(
6712 input,
6713 source_rows,
6714 relation_set_bounds,
6715 relation_domains,
6716 &format!("{path}.union[{index}]"),
6717 )?;
6718 rows = rows.checked_add(proof.rows).ok_or_else(|| {
6719 resident_workspace_decline(format!(
6720 "{path} union addition overflow: partial={rows} addition={}",
6721 proof.rows
6722 ))
6723 })?;
6724 if proof.peak > peak {
6725 peak = proof.peak;
6726 peak_detail = proof.peak_detail;
6727 }
6728 if let Some(merged) = domains.as_mut() {
6729 merge_domain_columns(merged, proof.domains, path)?;
6730 } else {
6731 domains = Some(proof.domains);
6732 }
6733 }
6734 if rows > peak {
6735 peak = rows;
6736 peak_detail = format!("{path}.union raw_rows={rows}");
6737 }
6738 Ok(ResidentRowProof {
6739 rows,
6740 peak,
6741 domains: domains
6742 .ok_or_else(|| resident_workspace_decline("resident union has no inputs"))?,
6743 full_row_unique: false,
6744 peak_detail,
6745 })
6746 }
6747 RirNode::Diff { left, right } => {
6748 let left = resident_node_row_bound(
6749 left,
6750 source_rows,
6751 relation_set_bounds,
6752 relation_domains,
6753 &format!("{path}.diff.left"),
6754 )?;
6755 let right = resident_node_row_bound(
6756 right,
6757 source_rows,
6758 relation_set_bounds,
6759 relation_domains,
6760 &format!("{path}.diff.right"),
6761 )?;
6762 let (peak, peak_detail) = if left.peak >= right.peak {
6763 (left.peak.max(left.rows), left.peak_detail)
6764 } else {
6765 (right.peak.max(left.rows), right.peak_detail)
6766 };
6767 Ok(ResidentRowProof {
6768 rows: left.rows,
6769 peak,
6770 domains: left.domains,
6771 full_row_unique: left.full_row_unique,
6772 peak_detail,
6773 })
6774 }
6775 RirNode::GroupBy { .. } | RirNode::Fixpoint { .. } | RirNode::TensorMaskedJoin { .. } => {
6776 Err(resident_workspace_decline(
6777 "resident row proof encountered an uncertified operator",
6778 ))
6779 }
6780 }
6781}
6782
6783fn resident_projection_is_injective(
6784 input_domains: &[ResidentDomain],
6785 columns: &[ProjectExpr],
6786 path: &str,
6787) -> std::result::Result<bool, ResidentGraphDeclineReason> {
6788 let mut retained = vec![false; input_domains.len()];
6789 for column in columns {
6790 if let ProjectExpr::Column(index) = column {
6791 let slot = retained.get_mut(*index).ok_or_else(|| {
6792 resident_workspace_decline(format!(
6793 "{path} projection column {index} exceeds input arity {}",
6794 input_domains.len()
6795 ))
6796 })?;
6797 *slot = true;
6798 }
6799 }
6800 for (index, (retained, domain)) in retained.iter().zip(input_domains).enumerate() {
6801 if !retained
6802 && resident_domain_cardinality_capped(
6803 domain,
6804 2,
6805 &format!("{path}.project.omitted[{index}]"),
6806 )? > 1
6807 {
6808 return Ok(false);
6809 }
6810 }
6811 Ok(true)
6812}
6813
6814fn resident_domain_cardinality_capped(
6815 domain: &ResidentDomain,
6816 cap: u64,
6817 context: &str,
6818) -> std::result::Result<u64, ResidentGraphDeclineReason> {
6819 let mut total = 0u64;
6820 for bound in domain.values() {
6821 let addition = (*bound).min(cap.saturating_sub(total));
6822 total = total.checked_add(addition).ok_or_else(|| {
6823 resident_workspace_decline(format!(
6824 "{context} domain-cardinality addition overflow: partial={total} addition={addition}"
6825 ))
6826 })?;
6827 if total == cap {
6828 break;
6829 }
6830 }
6831 Ok(total)
6832}
6833
6834fn resident_key_fanout_bound(
6835 domains: &[ResidentDomain],
6836 key: usize,
6837 rows: u64,
6838 full_row_unique: bool,
6839 context: &str,
6840) -> std::result::Result<u64, ResidentGraphDeclineReason> {
6841 if key >= domains.len() {
6842 return Err(resident_workspace_decline(format!(
6843 "{context} join key {key} exceeds input arity {}",
6844 domains.len()
6845 )));
6846 }
6847 if !full_row_unique {
6848 return Ok(rows);
6849 }
6850 let non_key = domains
6851 .iter()
6852 .enumerate()
6853 .filter_map(|(index, domain)| (index != key).then_some(domain.clone()))
6854 .collect::<Vec<_>>();
6855 resident_domain_product_capped(&non_key, rows, &format!("{context}.fanout"))
6856}
6857
6858fn merge_domain_columns(
6859 target: &mut ResidentColumnDomains,
6860 addition: ResidentColumnDomains,
6861 path: &str,
6862) -> std::result::Result<(), ResidentGraphDeclineReason> {
6863 if target.len() != addition.len() {
6864 return Err(resident_workspace_decline(format!(
6865 "{path} union domain arity mismatch: left={} right={}",
6866 target.len(),
6867 addition.len()
6868 )));
6869 }
6870 for (target, addition) in target.iter_mut().zip(addition) {
6871 for (source, bound) in addition {
6872 target
6873 .entry(source)
6874 .and_modify(|current| *current = (*current).max(bound))
6875 .or_insert(bound);
6876 }
6877 }
6878 Ok(())
6879}
6880
6881fn checked_capacity_class(source_capacity: u32) -> Result<u32> {
6882 let capacity = source_capacity
6883 .max(1)
6884 .checked_next_power_of_two()
6885 .ok_or_else(|| XlogError::Execution("resident capacity class overflow".to_string()))?;
6886 if capacity > MAX_RESIDENT_CAPACITY {
6887 return Err(XlogError::Execution(format!(
6888 "resident capacity class {capacity} exceeds the fixed scan envelope {MAX_RESIDENT_CAPACITY}"
6889 )));
6890 }
6891 Ok(capacity)
6892}
6893
6894#[cfg(test)]
6895mod tests {
6896 fn resident_retirement_test_guard() -> std::sync::MutexGuard<'static, ()> {
6899 static TEST_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
6900 TEST_LOCK.lock().unwrap_or_else(|error| error.into_inner())
6901 }
6902
6903 #[test]
6904 fn pending_resident_owners_release_admission_before_error_retirement() {
6905 let _retirement_test = resident_retirement_test_guard();
6906 use std::sync::{
6907 atomic::{AtomicBool, Ordering},
6908 Arc,
6909 };
6910 struct Admission(Arc<AtomicBool>);
6911 impl Drop for Admission {
6912 fn drop(&mut self) {
6913 self.0.store(false, Ordering::SeqCst);
6914 }
6915 }
6916 for unwind in [false, true] {
6917 let excluded = Arc::new(AtomicBool::new(false));
6918 let owner = Arc::new(());
6919 let observer = Arc::downgrade(&owner);
6920 let pending = super::PendingResidentOwners::new(
6921 (owner, Arc::clone(&excluded)),
6922 |(_, excluded)| {
6923 assert!(
6924 !excluded.load(Ordering::SeqCst),
6925 "admission must end before retirement waits"
6926 );
6927 true
6928 },
6929 );
6930 let outcome = std::panic::catch_unwind(move || {
6931 pending.with_admission(
6932 |(_, excluded)| {
6933 excluded.store(true, Ordering::SeqCst);
6934 Ok::<_, ()>(Admission(Arc::clone(excluded)))
6935 },
6936 |_| -> std::result::Result<(), ()> {
6937 if unwind {
6938 panic!("controlled partial operation unwind");
6939 }
6940 Err(())
6941 },
6942 )
6943 });
6944 assert_eq!(outcome.is_err(), unwind);
6945 if let Ok(result) = outcome {
6946 assert!(result.is_err());
6947 }
6948 assert!(!excluded.load(Ordering::SeqCst));
6949 assert!(
6950 observer.upgrade().is_none(),
6951 "completed owners must retire without another lifecycle entry"
6952 );
6953 }
6954 }
6955
6956 #[test]
6957 fn pending_resident_owners_rearm_for_late_receipt_copy() {
6958 let _retirement_test = resident_retirement_test_guard();
6959 let owner = std::sync::Arc::new(());
6960 let observer = std::sync::Arc::downgrade(&owner);
6961 let completed = super::PendingResidentOwners::new(owner, |_| {
6962 panic!("successful completion transfers ownership without another wait")
6963 });
6964 let synchronized_owner = completed.into_inner();
6965 assert!(observer.upgrade().is_some());
6966 let copy = super::PendingResidentOwners::new(synchronized_owner, |_| false);
6969 drop(copy);
6970 assert!(observer.upgrade().is_some());
6971 }
6972
6973 #[test]
6974 fn pending_resident_owners_remain_retained_if_wait_unwinds() {
6975 let _retirement_test = resident_retirement_test_guard();
6976 let owner = std::sync::Arc::new(());
6977 let observer = std::sync::Arc::downgrade(&owner);
6978 let result = std::panic::catch_unwind(move || {
6979 drop(super::PendingResidentOwners::new(owner, |_| {
6980 panic!("controlled completion wait failure")
6981 }));
6982 });
6983 assert!(result.is_err());
6984 assert!(observer.upgrade().is_some());
6985 }
6986
6987 #[test]
6988 fn pending_resident_owners_keep_full_chain_after_failed_completion() {
6989 let _retirement_test = resident_retirement_test_guard();
6990 use std::sync::{
6991 atomic::{AtomicUsize, Ordering},
6992 Arc,
6993 };
6994
6995 struct Owner(Arc<AtomicUsize>);
6996 impl Drop for Owner {
6997 fn drop(&mut self) {
6998 self.0.fetch_add(1, Ordering::SeqCst);
6999 }
7000 }
7001 for completion in [false, true] {
7004 let drops = Arc::new(AtomicUsize::new(0));
7005 let owners: Vec<_> = (0..5)
7006 .map(|_| Arc::new(Owner(Arc::clone(&drops))))
7007 .collect();
7008 let observers: Vec<_> = owners.iter().map(Arc::downgrade).collect();
7009 let pending =
7010 super::PendingResidentOwners::new((owners, completion), |(_, completed)| {
7011 *completed
7012 });
7013 drop(pending);
7014 assert_eq!(drops.load(Ordering::SeqCst), if completion { 5 } else { 0 });
7015 assert!(observers
7016 .iter()
7017 .all(|owner| owner.upgrade().is_some() != completion));
7018 }
7019 }
7020
7021 use super::{
7022 checked_capacity_class, resident_compact_allocation_bytes,
7023 resident_compact_filter_descriptors, resident_compact_preflight_device_bytes,
7024 resident_compact_project_descriptors, resident_compact_regions,
7025 resident_compact_schedule_metadata_bytes, resident_compact_schema_defaults,
7026 resident_compact_tables, resident_compact_topology, resident_join_capacity_bound,
7027 resident_lower_compact_regions, resident_new_phase_unit, resident_output_indices,
7028 resident_phase_merge, resident_plan_relation_ids, resident_record_lifetimes,
7029 resident_record_scan_leaf, resident_record_schema_winner_mark, resident_record_unit_leaf,
7030 resident_register_schema_selection, resident_resolve_output_schema,
7031 resident_semantic_trace_guard, resident_source_binding_route,
7032 resident_source_logical_count, resident_source_slot_map, resident_union_fold_mode,
7033 resident_validate_conditional_body_node_kinds, resident_validate_exact_reservation,
7034 resident_validate_parent_graph_kinds, resident_validate_slot_assignments,
7035 validate_compact_resident_node_envelope, CudaGraphNodeKind, ResidentAllocationManifest,
7036 ResidentBufferRef, ResidentCapacityBound, ResidentCaptureParentKind, ResidentCapturePhase,
7037 ResidentCompactDescriptorTables, ResidentCompactLogicalRegion, ResidentFilterPlan,
7038 ResidentGraphDeclineReason, ResidentHeadSchemaSelection, ResidentLogicalRelation,
7039 ResidentOpDescriptor, ResidentOutputSchemaPlan, ResidentOutputSchemaSelection,
7040 ResidentPhaseMergeStep, ResidentPhysicalSlotPlan, ResidentProjectPlan, ResidentRecordedOp,
7041 ResidentSlotAssignment, ResidentUnionFoldMode, ScalarType, Schema,
7042 RESIDENT_DYNAMIC_SCHEMA_ID,
7043 };
7044 use std::collections::{BTreeMap, HashMap, HashSet};
7045 use xlog_core::RelId;
7046 use xlog_ir::{
7047 CompareOp, CompiledRule, ConstValue, Expr, JoinType, ProjectExpr, RirMeta, RirNode,
7048 };
7049
7050 #[test]
7051 fn resident_plan_relations_exclude_registered_but_unreferenced_relations() {
7052 let source = RelId(1);
7053 let head = RelId(2);
7054 let unrelated = RelId(3);
7055 let rules = [CompiledRule {
7056 head: "reachable".to_owned(),
7057 body: RirNode::Scan { rel: source },
7058 meta: RirMeta::default(),
7059 }];
7060 let names = HashMap::from([
7061 ("source".to_owned(), source),
7062 ("reachable".to_owned(), head),
7063 ("unrelated".to_owned(), unrelated),
7064 ]);
7065
7066 let relations = resident_plan_relation_ids(rules.iter(), &names);
7067
7068 assert_eq!(relations, HashSet::from([source, head]));
7069 }
7070
7071 #[test]
7072 fn capacity_class_is_source_bounded_and_checked() {
7073 assert_eq!(checked_capacity_class(0).unwrap(), 1);
7074 assert_eq!(checked_capacity_class(4_393).unwrap(), 8_192);
7075 assert_eq!(checked_capacity_class(65_536).unwrap(), 65_536);
7076 assert!(checked_capacity_class(65_537).is_err());
7077 }
7078
7079 #[test]
7080 fn schema_lineage_fails_closed_for_cycles_and_missing_sources() {
7081 let schema = Schema::new(vec![("value".to_owned(), ScalarType::U32)]);
7082 let selection = |source: &str| ResidentHeadSchemaSelection {
7083 source_head: source.to_owned(),
7084 output_schemas_by_source_winner: vec![schema.clone()],
7085 };
7086 let choices = BTreeMap::from([
7087 ("a".to_owned(), vec![schema.clone()]),
7088 ("b".to_owned(), vec![schema.clone()]),
7089 ("c".to_owned(), vec![schema.clone()]),
7090 ("empty".to_owned(), Vec::new()),
7091 ]);
7092
7093 let direct_cycle =
7094 resident_register_schema_selection("a", selection("a"), &choices, &mut BTreeMap::new())
7095 .expect_err("direct schema lineage cycle must decline");
7096 assert!(matches!(
7097 direct_cycle,
7098 ResidentGraphDeclineReason::WorkspaceUnbounded { detail }
7099 if detail.contains("contains a cycle")
7100 ));
7101
7102 let mut long_cycle = BTreeMap::new();
7103 resident_register_schema_selection("a", selection("b"), &choices, &mut long_cycle)
7104 .expect("first acyclic lineage edge");
7105 resident_register_schema_selection("b", selection("c"), &choices, &mut long_cycle)
7106 .expect("second acyclic lineage edge");
7107 let long_cycle =
7108 resident_register_schema_selection("c", selection("a"), &choices, &mut long_cycle)
7109 .expect_err("transitive schema lineage cycle must decline");
7110 assert!(matches!(
7111 long_cycle,
7112 ResidentGraphDeclineReason::WorkspaceUnbounded { detail }
7113 if detail.contains("contains a cycle")
7114 ));
7115
7116 for source in ["missing", "empty"] {
7117 let missing = resident_register_schema_selection(
7118 "target",
7119 selection(source),
7120 &choices,
7121 &mut BTreeMap::new(),
7122 )
7123 .expect_err("missing schema source must decline");
7124 assert!(matches!(
7125 missing,
7126 ResidentGraphDeclineReason::WorkspaceUnbounded { detail }
7127 if detail.contains("has no admitted candidate")
7128 ));
7129 }
7130
7131 let dynamic_plan = |source_output| ResidentOutputSchemaPlan {
7132 candidates: vec![schema.clone()],
7133 selection: ResidentOutputSchemaSelection::SourceWinner {
7134 source_output,
7135 schemas: vec![schema.clone()],
7136 },
7137 };
7138 let resolver_cycle = resident_resolve_output_schema(
7139 &[dynamic_plan(1), dynamic_plan(0)],
7140 &[RESIDENT_DYNAMIC_SCHEMA_ID, RESIDENT_DYNAMIC_SCHEMA_ID],
7141 0,
7142 &mut HashSet::new(),
7143 )
7144 .expect_err("receipt schema resolver cycle must fail");
7145 assert!(resolver_cycle.to_string().contains("contains a cycle"));
7146
7147 let missing_plan = resident_resolve_output_schema(
7148 &[dynamic_plan(1)],
7149 &[RESIDENT_DYNAMIC_SCHEMA_ID],
7150 0,
7151 &mut HashSet::new(),
7152 )
7153 .expect_err("missing receipt schema source plan must fail");
7154 assert!(missing_plan.to_string().contains("output 1 is missing"));
7155 }
7156
7157 #[test]
7158 fn join_capacity_uses_a_finite_tight_bound_when_loose_products_overflow() {
7159 let bound = resident_join_capacity_bound(u64::MAX, u64::MAX, 1, 1, 1);
7160 assert_eq!(
7161 bound,
7162 ResidentCapacityBound::Finite {
7163 rows: 1,
7164 proof: "matching_keys*left_fanout*right_fanout".to_owned(),
7165 }
7166 );
7167 }
7168
7169 #[test]
7170 fn true_cartesian_join_remains_above_the_resident_limit() {
7171 let bound = resident_join_capacity_bound(257, 257, 257, 257, 1);
7172 assert!(matches!(
7173 bound,
7174 ResidentCapacityBound::AboveResidentLimit { .. }
7175 ));
7176 }
7177
7178 #[test]
7179 fn lifetime_scan_rejects_an_out_of_range_logical_input() {
7180 let relations = vec![ResidentLogicalRelation {
7181 schema: Schema::new(vec![("value".to_string(), ScalarType::U32)]),
7182 initial_count: 0,
7183 permanent: false,
7184 }];
7185 let mut definitions = vec![None];
7186 let mut last_uses = vec![None];
7187 let mut ordinal = 0;
7188 let error = resident_record_lifetimes(
7189 &[ResidentRecordedOp::Filter {
7190 input: ResidentBufferRef::Private(1),
7191 output: 0,
7192 workspace: 0,
7193 op_id: 0,
7194 }],
7195 &relations,
7196 &mut definitions,
7197 &mut last_uses,
7198 &mut ordinal,
7199 )
7200 .expect_err("an invalid logical input must fail before allocation");
7201 assert!(error
7202 .to_string()
7203 .contains("logical input relation 1 is missing"));
7204 }
7205
7206 #[test]
7207 fn stale_scratch_generation_is_rejected_before_materialization() {
7208 let schema = Schema::new(vec![("value".to_string(), ScalarType::U32)]);
7209 let relations = vec![
7210 ResidentLogicalRelation {
7211 schema: schema.clone(),
7212 initial_count: 0,
7213 permanent: false,
7214 },
7215 ResidentLogicalRelation {
7216 schema: schema.clone(),
7217 initial_count: 0,
7218 permanent: false,
7219 },
7220 ];
7221 let slots = vec![ResidentPhysicalSlotPlan {
7222 schema,
7223 initial_count: 0,
7224 permanent: false,
7225 }];
7226 let assignments = vec![
7227 ResidentSlotAssignment {
7228 slot: 0,
7229 generation: 0,
7230 },
7231 ResidentSlotAssignment {
7232 slot: 0,
7233 generation: 0,
7234 },
7235 ];
7236 let error = resident_validate_slot_assignments(
7237 &relations,
7238 &[Some(0), Some(2)],
7239 &[Some(0), Some(2)],
7240 &slots,
7241 &assignments,
7242 )
7243 .expect_err("a stale scratch generation must fail before allocation");
7244 assert!(error.to_string().contains("generation 0 but expected 1"));
7245 }
7246
7247 #[test]
7248 fn authored_scan_and_unit_are_explicit_lifetime_operations() {
7249 let relations = vec![ResidentLogicalRelation {
7250 schema: Schema::new(Vec::<(String, ScalarType)>::new()),
7251 initial_count: 0,
7252 permanent: false,
7253 }];
7254 let mut definitions = vec![None];
7255 let mut last_uses = vec![None];
7256 let mut ordinal = 0;
7257 let operations = vec![
7258 ResidentRecordedOp::Unit {
7259 output: 0,
7260 op_id: 17,
7261 },
7262 ResidentRecordedOp::Scan {
7263 relation: ResidentBufferRef::Private(0),
7264 op_id: 18,
7265 },
7266 ResidentRecordedOp::TraceDelta {
7267 scan_delta: 1,
7268 filter_delta: 0,
7269 semantic_guard: None,
7270 },
7271 ];
7272
7273 resident_record_lifetimes(
7274 &operations,
7275 &relations,
7276 &mut definitions,
7277 &mut last_uses,
7278 &mut ordinal,
7279 )
7280 .expect("an emitted Unit defines the relation consumed by the emitted Scan");
7281
7282 assert_eq!(definitions, vec![Some(0)]);
7283 assert_eq!(last_uses, vec![Some(1)]);
7284 assert_eq!(ordinal, operations.len());
7285 }
7286
7287 #[test]
7288 fn authored_leaf_emitters_preserve_scan_identity_and_trace_order() {
7289 let mut operations = Vec::new();
7290 let push = |ops: &mut Vec<_>, op, _op_id| ops.push(op);
7291
7292 let unit = resident_record_unit_leaf(3, 17, &mut operations, push);
7293 let first = resident_record_scan_leaf(
7294 ResidentBufferRef::Private(4),
7295 18,
7296 None,
7297 &mut operations,
7298 push,
7299 );
7300 let second = resident_record_scan_leaf(
7301 ResidentBufferRef::Private(5),
7302 19,
7303 None,
7304 &mut operations,
7305 push,
7306 );
7307
7308 assert!(matches!(unit, ResidentBufferRef::Private(3)));
7309 assert!(matches!(first, ResidentBufferRef::Private(4)));
7310 assert!(matches!(second, ResidentBufferRef::Private(5)));
7311 assert!(matches!(
7312 operations.as_slice(),
7313 [
7314 ResidentRecordedOp::Unit {
7315 output: 3,
7316 op_id: 17
7317 },
7318 ResidentRecordedOp::Scan {
7319 relation: ResidentBufferRef::Private(4),
7320 op_id: 18
7321 },
7322 ResidentRecordedOp::TraceDelta {
7323 scan_delta: 1,
7324 filter_delta: 0,
7325 semantic_guard: None,
7326 },
7327 ResidentRecordedOp::Scan {
7328 relation: ResidentBufferRef::Private(5),
7329 op_id: 19
7330 },
7331 ResidentRecordedOp::TraceDelta {
7332 scan_delta: 1,
7333 filter_delta: 0,
7334 semantic_guard: None,
7335 },
7336 ]
7337 ));
7338 }
7339
7340 #[test]
7341 fn recursive_trace_semantics_use_the_selected_delta_and_leave_seed_unguarded() {
7342 let selected_delta = ResidentBufferRef::Private(9);
7343 assert!(resident_semantic_trace_guard(None).is_none());
7344 assert!(matches!(
7345 resident_semantic_trace_guard(Some((RelId(4), 2, 9))),
7346 Some(ResidentBufferRef::Private(9))
7347 ));
7348
7349 let mut operations = Vec::new();
7350 let push = |ops: &mut Vec<_>, op, _op_id| ops.push(op);
7351 resident_record_scan_leaf(
7352 ResidentBufferRef::Private(4),
7353 18,
7354 None,
7355 &mut operations,
7356 push,
7357 );
7358 resident_record_scan_leaf(
7359 ResidentBufferRef::Private(5),
7360 19,
7361 Some(selected_delta.clone()),
7362 &mut operations,
7363 push,
7364 );
7365
7366 assert!(matches!(
7367 operations.as_slice(),
7368 [
7369 ResidentRecordedOp::Scan { op_id: 18, .. },
7370 ResidentRecordedOp::TraceDelta {
7371 semantic_guard: None,
7372 ..
7373 },
7374 ResidentRecordedOp::Scan { op_id: 19, .. },
7375 ResidentRecordedOp::TraceDelta {
7376 semantic_guard: Some(ResidentBufferRef::Private(9)),
7377 ..
7378 },
7379 ]
7380 ));
7381 }
7382
7383 #[test]
7384 fn phase_unit_allocator_creates_fresh_scratch_values() {
7385 let mut relations = Vec::new();
7386
7387 let (first, first_op) = resident_new_phase_unit(&mut relations, 17).unwrap();
7388 let (second, second_op) = resident_new_phase_unit(&mut relations, 18).unwrap();
7389
7390 assert!(matches!(first, ResidentBufferRef::Private(0)));
7391 assert!(matches!(second, ResidentBufferRef::Private(1)));
7392 assert!(matches!(
7393 first_op,
7394 ResidentRecordedOp::Unit {
7395 output: 0,
7396 op_id: 17
7397 }
7398 ));
7399 assert!(matches!(
7400 second_op,
7401 ResidentRecordedOp::Unit {
7402 output: 1,
7403 op_id: 18
7404 }
7405 ));
7406 assert!(relations
7407 .iter()
7408 .all(|relation| !relation.permanent && relation.initial_count == 0));
7409 }
7410
7411 #[test]
7412 fn phase_local_merge_deduplicates_each_contribution_before_ordered_union() {
7413 let mut steps = Vec::new();
7414 let first = resident_phase_merge(None, 2_u32, |step| match step {
7415 ResidentPhaseMergeStep::Deduplicate(value) => {
7416 steps.push(("dedup", value, 0));
7417 Ok::<_, &'static str>(value * 10)
7418 }
7419 ResidentPhaseMergeStep::Union(left, right) => {
7420 steps.push(("union", left, right));
7421 Ok(left + right)
7422 }
7423 })
7424 .unwrap();
7425 let second = resident_phase_merge(Some(first), 3_u32, |step| match step {
7426 ResidentPhaseMergeStep::Deduplicate(value) => {
7427 steps.push(("dedup", value, 0));
7428 Ok::<_, &'static str>(value * 10)
7429 }
7430 ResidentPhaseMergeStep::Union(left, right) => {
7431 steps.push(("union", left, right));
7432 Ok(left + right)
7433 }
7434 })
7435 .unwrap();
7436
7437 assert_eq!(first, 20);
7438 assert_eq!(second, 50);
7439 assert_eq!(steps, [("dedup", 2, 0), ("dedup", 3, 0), ("union", 20, 30)]);
7440 }
7441
7442 #[test]
7443 fn initial_copy_schema_marker_is_ordered_after_its_count_producer() {
7444 let source = ResidentBufferRef::Source("head".to_owned());
7445 let mut operations = vec![ResidentRecordedOp::Project {
7446 input: source.clone(),
7447 output: 0,
7448 workspace: 0,
7449 op_id: 7,
7450 }];
7451
7452 resident_record_schema_winner_mark(&mut operations, ResidentBufferRef::Private(0), 0, 3);
7453
7454 assert!(matches!(
7455 operations.as_slice(),
7456 [
7457 ResidentRecordedOp::Project { op_id: 7, .. },
7458 ResidentRecordedOp::SchemaWinnerMark {
7459 contribution: ResidentBufferRef::Private(0),
7460 head_index: 0,
7461 schema_id: 3,
7462 }
7463 ]
7464 ));
7465 }
7466
7467 #[test]
7468 fn source_slots_are_deduplicated_sorted_and_distinct_from_private_targets() {
7469 let slots =
7470 resident_source_slot_map(3, ["zeta", "head", "zeta", "head"]).expect("source slots");
7471
7472 assert_eq!(slots.get("head"), Some(&3));
7473 assert_eq!(slots.get("zeta"), Some(&4));
7474 assert_eq!(slots.len(), 2);
7475 assert_ne!(slots["head"], 0, "stored source and staged target differ");
7476 }
7477
7478 #[test]
7479 fn empty_untracked_sources_are_normalized_before_slot_binding() {
7480 let logical_count = resident_source_logical_count(Some(0)).unwrap();
7481 assert_eq!(
7482 resident_source_binding_route(logical_count, false, false).unwrap(),
7483 super::ResidentSourceBindingRoute::NormalizeEmpty
7484 );
7485 assert_eq!(
7486 resident_source_binding_route(0, true, true).unwrap(),
7487 super::ResidentSourceBindingRoute::Direct
7488 );
7489 assert!(resident_source_binding_route(1, false, true).is_err());
7490 assert!(resident_source_binding_route(1, true, false).is_err());
7491 assert!(resident_source_logical_count(None).is_err());
7492 }
7493
7494 #[test]
7495 fn compact_regions_preserve_phase_order_and_form_five_parent_nodes() {
7496 let unit = |output, op_id| ResidentRecordedOp::Unit { output, op_id };
7497 let phases = vec![
7498 ResidentCapturePhase::Segment {
7499 ops: vec![unit(10, 10)],
7500 scc_begin: None,
7501 },
7502 ResidentCapturePhase::Segment {
7503 ops: vec![unit(11, 11)],
7504 scc_begin: Some((64, 101)),
7505 },
7506 ResidentCapturePhase::ConditionalWhile {
7507 ops: vec![unit(12, 12)],
7508 iteration_limit: 64,
7509 convergence_op_id: 102,
7510 },
7511 ResidentCapturePhase::Segment {
7512 ops: vec![unit(20, 20)],
7513 scc_begin: None,
7514 },
7515 ResidentCapturePhase::Segment {
7516 ops: vec![ResidentRecordedOp::Scan {
7517 relation: ResidentBufferRef::Private(20),
7518 op_id: 21,
7519 }],
7520 scc_begin: Some((32, 201)),
7521 },
7522 ResidentCapturePhase::ConditionalWhile {
7523 ops: vec![unit(22, 22)],
7524 iteration_limit: 32,
7525 convergence_op_id: 202,
7526 },
7527 ResidentCapturePhase::Segment {
7528 ops: vec![unit(30, 30)],
7529 scc_begin: None,
7530 },
7531 ];
7532
7533 let regions = resident_compact_regions(vec![unit(0, 0)], phases, 999).expect("regions");
7534
7535 assert_eq!(regions.len(), 5);
7536 assert_eq!(
7537 regions.iter().filter(|region| region.recursive()).count(),
7538 2
7539 );
7540 assert_eq!(regions.len() + 2, 7, "hierarchical node inventory");
7541 let topology = resident_compact_topology(®ions).unwrap();
7542 assert_eq!(
7543 topology.parent_kinds,
7544 vec![
7545 ResidentCaptureParentKind::Kernel,
7546 ResidentCaptureParentKind::Conditional,
7547 ResidentCaptureParentKind::Kernel,
7548 ResidentCaptureParentKind::Conditional,
7549 ResidentCaptureParentKind::Kernel,
7550 ]
7551 );
7552 assert_eq!(topology.conditional_body_kernel_counts, vec![1, 1]);
7553 assert_eq!(topology.hierarchical_node_count, 7);
7554 assert!(regions[0].initializes());
7555 assert!(regions[0].begins_scc());
7556 assert_eq!(regions[0].op_id, regions[1].op_id);
7557 assert_eq!(regions[0].iteration_limit, regions[1].iteration_limit);
7558 assert_eq!(regions[2].op_id, regions[3].op_id);
7559 assert_eq!(regions[2].iteration_limit, regions[3].iteration_limit);
7560 assert!(regions[4].finalizes());
7561 assert_eq!(regions[4].op_id, 999);
7562 assert!(matches!(
7563 regions[2].ops.as_slice(),
7564 [
7565 ResidentRecordedOp::Unit { output: 20, .. },
7566 ResidentRecordedOp::Scan {
7567 relation: ResidentBufferRef::Private(20),
7568 ..
7569 }
7570 ]
7571 ));
7572 }
7573
7574 #[test]
7575 fn conditional_body_inventory_requires_one_actual_kernel_per_body() {
7576 let exact = vec![
7577 vec![CudaGraphNodeKind::Kernel],
7578 vec![CudaGraphNodeKind::Kernel],
7579 ];
7580 assert_eq!(
7581 resident_validate_conditional_body_node_kinds(&exact, 2).unwrap(),
7582 vec![1, 1]
7583 );
7584 assert!(resident_validate_conditional_body_node_kinds(&exact[..1], 2).is_err());
7585 assert!(resident_validate_conditional_body_node_kinds(&[Vec::new()], 1).is_err());
7586 assert!(resident_validate_conditional_body_node_kinds(
7587 &[vec![CudaGraphNodeKind::Kernel, CudaGraphNodeKind::Kernel]],
7588 1,
7589 )
7590 .is_err());
7591 assert!(resident_validate_conditional_body_node_kinds(
7592 &[vec![CudaGraphNodeKind::Memcpy]],
7593 1,
7594 )
7595 .is_err());
7596 }
7597
7598 #[test]
7599 fn compact_parent_graph_kind_validation_is_exact() {
7600 let expected = vec![
7601 ResidentCaptureParentKind::Kernel,
7602 ResidentCaptureParentKind::Conditional,
7603 ResidentCaptureParentKind::Kernel,
7604 ];
7605 assert!(resident_validate_parent_graph_kinds(
7606 &[
7607 CudaGraphNodeKind::Kernel,
7608 CudaGraphNodeKind::Conditional,
7609 CudaGraphNodeKind::Kernel
7610 ],
7611 &expected,
7612 )
7613 .is_ok());
7614 assert!(resident_validate_parent_graph_kinds(
7615 &[
7616 CudaGraphNodeKind::Kernel,
7617 CudaGraphNodeKind::Kernel,
7618 CudaGraphNodeKind::Conditional
7619 ],
7620 &expected,
7621 )
7622 .is_err());
7623 assert!(resident_validate_parent_graph_kinds(
7624 &[CudaGraphNodeKind::Kernel, CudaGraphNodeKind::Conditional],
7625 &expected,
7626 )
7627 .is_err());
7628 }
7629
7630 #[test]
7631 fn compact_descriptors_preserve_physical_generations_and_source_slots() {
7632 let regions = vec![ResidentCompactLogicalRegion {
7633 ops: vec![
7634 ResidentRecordedOp::Unit {
7635 output: 0,
7636 op_id: 7,
7637 },
7638 ResidentRecordedOp::SchemaWinnerMark {
7639 contribution: ResidentBufferRef::Private(0),
7640 head_index: 0,
7641 schema_id: 3,
7642 },
7643 ResidentRecordedOp::TraceDelta {
7644 scan_delta: 0,
7645 filter_delta: 1,
7646 semantic_guard: None,
7647 },
7648 ResidentRecordedOp::Scan {
7649 relation: ResidentBufferRef::Source("source".to_owned()),
7650 op_id: 8,
7651 },
7652 ResidentRecordedOp::TraceDelta {
7653 scan_delta: 1,
7654 filter_delta: 0,
7655 semantic_guard: None,
7656 },
7657 ],
7658 iteration_limit: 1,
7659 op_id: 0,
7660 flags: super::RESIDENT_SCHEDULE_REGION_INITIALIZE
7661 | super::RESIDENT_SCHEDULE_REGION_FINALIZE,
7662 }];
7663 let slots = vec![
7664 ResidentPhysicalSlotPlan {
7665 schema: Schema::new(Vec::new()),
7666 initial_count: 0,
7667 permanent: true,
7668 },
7669 ResidentPhysicalSlotPlan {
7670 schema: Schema::new(Vec::new()),
7671 initial_count: 0,
7672 permanent: false,
7673 },
7674 ];
7675 let assignments = vec![ResidentSlotAssignment {
7676 slot: 1,
7677 generation: 4,
7678 }];
7679
7680 let plan = resident_lower_compact_regions(
7681 regions,
7682 &slots,
7683 &assignments,
7684 ["source"],
7685 Default::default(),
7686 )
7687 .expect("compact descriptors");
7688
7689 assert_eq!(plan.source_slots["source"], 2);
7690 assert_eq!(plan.ops.len(), 4);
7691 assert_eq!(plan.ops[0].kind, super::ResidentScheduleOpKind::Unit);
7692 assert_eq!(plan.ops[0].out, 1);
7693 assert_eq!(plan.ops[0].out_generation, 4);
7694 assert_eq!(plan.ops[0].schema_winner_head, 0);
7695 assert_eq!(plan.ops[0].schema_winner_id, 3);
7696 assert_eq!(plan.ops[2].kind, super::ResidentScheduleOpKind::Scan);
7697 assert_eq!(plan.ops[2].out, 2);
7698 assert_eq!(plan.ops[2].in0_generation, 0);
7699 assert_eq!(plan.waves.len(), 1);
7700 assert_eq!(plan.waves[0].first_op, 0);
7701 assert_eq!(plan.waves[0].op_count, 4);
7702 assert_eq!(plan.regions.len(), 1);
7703 assert_eq!(plan.generation_bases, vec![0, 4, 0]);
7704 }
7705
7706 #[test]
7707 fn compact_novelty_marker_observes_the_completed_delta_copy() {
7708 let regions = vec![ResidentCompactLogicalRegion {
7709 ops: vec![
7710 ResidentRecordedOp::ChangedReset,
7711 ResidentRecordedOp::Diff {
7712 left: ResidentBufferRef::Private(0),
7713 right: ResidentBufferRef::Private(1),
7714 output: 2,
7715 op_id: 10,
7716 },
7717 ResidentRecordedOp::Project {
7718 input: ResidentBufferRef::Private(2),
7719 output: 3,
7720 workspace: 0,
7721 op_id: 11,
7722 },
7723 ResidentRecordedOp::ChangedMark { relation: 3 },
7724 ],
7725 iteration_limit: 3,
7726 op_id: 12,
7727 flags: super::RESIDENT_SCHEDULE_REGION_RECURSIVE,
7728 }];
7729 let slots = (0..4)
7730 .map(|slot| ResidentPhysicalSlotPlan {
7731 schema: Schema::new(Vec::new()),
7732 initial_count: 0,
7733 permanent: slot != 2,
7734 })
7735 .collect::<Vec<_>>();
7736 let assignments = (0..4)
7737 .map(|slot| ResidentSlotAssignment {
7738 slot,
7739 generation: 0,
7740 })
7741 .collect::<Vec<_>>();
7742 let tables = ResidentCompactDescriptorTables {
7743 project_expressions: vec![super::ResidentProjectExpressionDescriptor::column(0, 4)],
7744 project_ranges: vec![(0, 1)],
7745 ..Default::default()
7746 };
7747
7748 let plan = resident_lower_compact_regions(
7749 regions,
7750 &slots,
7751 &assignments,
7752 std::iter::empty(),
7753 tables,
7754 )
7755 .expect("the final delta copy must carry the novelty marker");
7756
7757 assert_eq!(plan.ops.len(), 2);
7758 assert_eq!(plan.ops[0].kind, super::ResidentScheduleOpKind::Diff);
7759 assert_eq!(plan.ops[0].flags, 0);
7760 assert_eq!(plan.ops[1].kind, super::ResidentScheduleOpKind::Project);
7761 assert_eq!(plan.ops[1].flags, super::RESIDENT_SCHEDULE_OP_MARK_NOVELTY);
7762 assert_eq!(plan.ops[1].out, 3);
7763 }
7764
7765 #[test]
7766 fn compact_schema_marker_must_name_the_producer_output() {
7767 let regions = vec![ResidentCompactLogicalRegion {
7768 ops: vec![
7769 ResidentRecordedOp::Project {
7770 input: ResidentBufferRef::Source("source".to_owned()),
7771 output: 0,
7772 workspace: 0,
7773 op_id: 7,
7774 },
7775 ResidentRecordedOp::SchemaWinnerMark {
7776 contribution: ResidentBufferRef::Source("source".to_owned()),
7777 head_index: 0,
7778 schema_id: 0,
7779 },
7780 ],
7781 iteration_limit: 1,
7782 op_id: 9,
7783 flags: super::RESIDENT_SCHEDULE_REGION_INITIALIZE
7784 | super::RESIDENT_SCHEDULE_REGION_FINALIZE,
7785 }];
7786 let slots = vec![ResidentPhysicalSlotPlan {
7787 schema: Schema::new(Vec::new()),
7788 initial_count: 0,
7789 permanent: true,
7790 }];
7791 let assignments = vec![ResidentSlotAssignment {
7792 slot: 0,
7793 generation: 0,
7794 }];
7795 let tables = ResidentCompactDescriptorTables {
7796 project_ranges: vec![(0, 0)],
7797 ..Default::default()
7798 };
7799
7800 assert!(resident_lower_compact_regions(
7801 regions,
7802 &slots,
7803 &assignments,
7804 ["source"].into_iter(),
7805 tables,
7806 )
7807 .is_err());
7808 }
7809
7810 #[test]
7811 fn compact_schema_defaults_follow_first_marker_per_head() {
7812 let ops = vec![
7813 ResidentOpDescriptor::unit(1, 0, 0).with_schema_winner(1, u32::MAX),
7814 ResidentOpDescriptor::unit(2, 1, 0).with_schema_winner(0, 7),
7815 ResidentOpDescriptor::unit(3, 2, 0).with_schema_winner(1, 9),
7816 ];
7817 assert_eq!(
7818 resident_compact_schema_defaults(&ops, 2).unwrap(),
7819 [7, u32::MAX]
7820 );
7821 assert!(resident_compact_schema_defaults(&ops[..1], 2).is_err());
7822 }
7823
7824 #[test]
7825 fn compact_changed_reset_is_only_absorbed_at_recursive_region_entry() {
7826 let slot = ResidentPhysicalSlotPlan {
7827 schema: Schema::new(Vec::new()),
7828 initial_count: 0,
7829 permanent: false,
7830 };
7831 let assignment = ResidentSlotAssignment {
7832 slot: 0,
7833 generation: 0,
7834 };
7835 let region = |ops, flags| ResidentCompactLogicalRegion {
7836 ops,
7837 iteration_limit: 4,
7838 op_id: 9,
7839 flags,
7840 };
7841
7842 assert!(resident_lower_compact_regions(
7843 vec![region(vec![ResidentRecordedOp::ChangedReset], 0)],
7844 std::slice::from_ref(&slot),
7845 std::slice::from_ref(&assignment),
7846 std::iter::empty(),
7847 Default::default(),
7848 )
7849 .is_err());
7850 assert!(resident_lower_compact_regions(
7851 vec![region(
7852 vec![
7853 ResidentRecordedOp::Unit {
7854 output: 0,
7855 op_id: 1,
7856 },
7857 ResidentRecordedOp::ChangedReset,
7858 ],
7859 super::RESIDENT_SCHEDULE_REGION_RECURSIVE,
7860 )],
7861 std::slice::from_ref(&slot),
7862 std::slice::from_ref(&assignment),
7863 std::iter::empty(),
7864 Default::default(),
7865 )
7866 .is_err());
7867 assert!(resident_lower_compact_regions(
7868 vec![region(
7869 vec![
7870 ResidentRecordedOp::ChangedReset,
7871 ResidentRecordedOp::Unit {
7872 output: 0,
7873 op_id: 1,
7874 },
7875 ],
7876 super::RESIDENT_SCHEDULE_REGION_RECURSIVE,
7877 )],
7878 &[slot],
7879 &[assignment],
7880 std::iter::empty(),
7881 Default::default(),
7882 )
7883 .is_ok());
7884 }
7885
7886 #[test]
7887 fn compact_filter_project_tables_preserve_types_widths_and_order() {
7888 let input = Schema::new(vec![
7889 ("symbol".to_owned(), ScalarType::Symbol),
7890 ("number".to_owned(), ScalarType::U64),
7891 ]);
7892 let predicate = Expr::And(vec![
7893 Expr::Compare {
7894 left: Box::new(Expr::Column(1)),
7895 op: CompareOp::Ge,
7896 right: Box::new(Expr::Const(ConstValue::U64(9))),
7897 },
7898 Expr::Compare {
7899 left: Box::new(Expr::Column(0)),
7900 op: CompareOp::Eq,
7901 right: Box::new(Expr::Const(ConstValue::Symbol("x".to_owned()))),
7902 },
7903 ]);
7904 let comparisons =
7905 resident_compact_filter_descriptors(&predicate, &input).expect("filter table");
7906 assert_eq!(comparisons.len(), 2);
7907 assert_eq!(comparisons[0].left_column, 1);
7908 assert_eq!(comparisons[0].width, 8);
7909 assert_eq!(comparisons[0].right_constant, 9);
7910 assert_eq!(comparisons[1].left_column, 0);
7911 assert_eq!(comparisons[1].width, 4);
7912
7913 let output = Schema::new(vec![
7914 ("number".to_owned(), ScalarType::U64),
7915 ("symbol".to_owned(), ScalarType::Symbol),
7916 ]);
7917 let expressions = resident_compact_project_descriptors(
7918 &[
7919 ProjectExpr::Column(1),
7920 ProjectExpr::Computed(
7921 Expr::Const(ConstValue::Symbol("y".to_owned())),
7922 ScalarType::Symbol,
7923 ),
7924 ],
7925 &input,
7926 &output,
7927 )
7928 .expect("project table");
7929 assert_eq!(expressions.len(), output.arity());
7930 assert_eq!(expressions[0].column, 1);
7931 assert_eq!(expressions[0].width, 8);
7932 assert_eq!(expressions[1].kind, 1);
7933 assert_eq!(expressions[1].width, 4);
7934
7935 let mismatch = Expr::Compare {
7936 left: Box::new(Expr::Column(1)),
7937 op: CompareOp::Eq,
7938 right: Box::new(Expr::Const(ConstValue::Symbol("wrong".to_owned()))),
7939 };
7940 assert!(resident_compact_filter_descriptors(&mismatch, &input).is_err());
7941 }
7942
7943 #[test]
7944 fn compact_tables_cover_multiple_empty_occurrences_without_duplicate_payloads() {
7945 let filters = vec![
7946 ResidentFilterPlan {
7947 compact_comparisons: Vec::new(),
7948 },
7949 ResidentFilterPlan {
7950 compact_comparisons: Vec::new(),
7951 },
7952 ];
7953 let projects = vec![ResidentProjectPlan {
7954 compact_expressions: Vec::new(),
7955 }];
7956
7957 let tables = resident_compact_tables(&filters, &projects).expect("compact tables");
7958
7959 assert!(tables.filter_comparisons.is_empty());
7960 assert_eq!(tables.filter_ranges, [(0, 0), (0, 0)]);
7961 assert!(tables.project_expressions.is_empty());
7962 assert_eq!(tables.project_ranges, [(0, 0)]);
7963 }
7964
7965 #[test]
7966 fn compact_descriptor_ranges_follow_operation_order_exactly() {
7967 let regions = vec![ResidentCompactLogicalRegion {
7968 ops: vec![
7969 ResidentRecordedOp::Project {
7970 input: ResidentBufferRef::Source("source".to_owned()),
7971 output: 0,
7972 workspace: 1,
7973 op_id: 1,
7974 },
7975 ResidentRecordedOp::Project {
7976 input: ResidentBufferRef::Source("source".to_owned()),
7977 output: 1,
7978 workspace: 0,
7979 op_id: 2,
7980 },
7981 ],
7982 iteration_limit: 1,
7983 op_id: 3,
7984 flags: super::RESIDENT_SCHEDULE_REGION_INITIALIZE
7985 | super::RESIDENT_SCHEDULE_REGION_FINALIZE,
7986 }];
7987 let slots = vec![
7988 ResidentPhysicalSlotPlan {
7989 schema: Schema::new(Vec::new()),
7990 initial_count: 0,
7991 permanent: false,
7992 },
7993 ResidentPhysicalSlotPlan {
7994 schema: Schema::new(Vec::new()),
7995 initial_count: 0,
7996 permanent: false,
7997 },
7998 ];
7999 let assignments = vec![
8000 ResidentSlotAssignment {
8001 slot: 0,
8002 generation: 0,
8003 },
8004 ResidentSlotAssignment {
8005 slot: 1,
8006 generation: 0,
8007 },
8008 ];
8009 let tables = ResidentCompactDescriptorTables {
8010 project_expressions: vec![Default::default(), Default::default()],
8011 project_ranges: vec![(0, 1), (1, 1)],
8012 ..Default::default()
8013 };
8014
8015 assert!(resident_lower_compact_regions(
8016 regions,
8017 &slots,
8018 &assignments,
8019 ["source"].into_iter(),
8020 tables,
8021 )
8022 .is_err());
8023 }
8024
8025 #[test]
8026 fn compact_metadata_reservation_counts_generation_bases_and_schema_defaults() {
8027 let actual = resident_compact_schedule_metadata_bytes(3, 5, 5, 2, 6, 4, 0, 0)
8028 .expect("metadata bytes");
8029 let expected = super::resident_schedule_metadata_device_bytes(3, 5, 5, 2, 10, 0, 0)
8030 .expect("expected metadata bytes");
8031 let missing_defaults = super::resident_schedule_metadata_device_bytes(3, 5, 5, 2, 6, 0, 0)
8032 .expect("generation-only bytes");
8033
8034 assert_eq!(actual, expected);
8035 assert_ne!(actual, missing_defaults);
8036 }
8037
8038 #[test]
8039 fn compact_manifest_replaces_per_occurrence_descriptor_allocations() {
8040 let plan = super::ResidentCompactSchedulePlan {
8041 source_slots: [("source".to_owned(), 2)].into_iter().collect(),
8042 ops: Vec::new(),
8043 waves: Vec::new(),
8044 regions: Vec::new(),
8045 generation_bases: Vec::new(),
8046 filter_comparisons: Vec::new(),
8047 project_expressions: Vec::new(),
8048 };
8049 let (required, metadata) =
8050 resident_compact_allocation_bytes(1_000, 2_000, 3_000, 2, 2, &plan).unwrap();
8051 let expected_metadata =
8052 resident_compact_schedule_metadata_bytes(3, 0, 0, 0, 0, 2, 0, 0).unwrap();
8053 assert_eq!(metadata, expected_metadata);
8054 assert_eq!(required, 1_000 + 2_000 + 3_000 + expected_metadata);
8055 assert_ne!(
8056 required,
8057 1_000 + 48 + 2_000 + 24 + 3_000 + expected_metadata
8058 );
8059 }
8060
8061 #[test]
8062 fn compact_preflight_bytes_report_flattened_tables_without_double_counting() {
8063 let assert_components = |filter_count: usize, project_count: usize| {
8064 let plan = super::ResidentCompactSchedulePlan {
8065 source_slots: [("source".to_owned(), 2)].into_iter().collect(),
8066 ops: Vec::new(),
8067 waves: Vec::new(),
8068 regions: Vec::new(),
8069 generation_bases: Vec::new(),
8070 filter_comparisons: vec![
8071 super::ResidentFilterComparisonDescriptor::default();
8072 filter_count
8073 ],
8074 project_expressions: vec![
8075 super::ResidentProjectExpressionDescriptor::default();
8076 project_count
8077 ],
8078 };
8079 let mut manifest = ResidentAllocationManifest {
8080 slots: Vec::new(),
8081 logical_to_slot: Vec::new(),
8082 required_bytes: 0,
8083 relation_bytes: 1_000,
8084 filter_scratch_bytes: 2_000,
8085 schedule_metadata_bytes: 0,
8086 fixed_workspace_bytes: 3_000,
8087 logical_relation_values: 0,
8088 permanent_slots: 0,
8089 scratch_slots: 0,
8090 filter_scratch_allocations: 1,
8091 max_row_bytes: 0,
8092 };
8093 manifest.finalize_compact_schedule(&plan, 2).unwrap();
8094
8095 let (filter_bytes, project_bytes, fixed_bytes) =
8096 resident_compact_preflight_device_bytes(&manifest, &plan).unwrap();
8097 assert_eq!(
8098 filter_bytes,
8099 48 * u64::try_from(filter_count.max(1)).unwrap()
8100 );
8101 assert_eq!(
8102 project_bytes,
8103 24 * u64::try_from(project_count.max(1)).unwrap()
8104 );
8105 assert_eq!(
8106 manifest.relation_bytes
8107 + manifest.filter_scratch_bytes
8108 + filter_bytes
8109 + project_bytes
8110 + fixed_bytes,
8111 manifest.required_bytes
8112 );
8113 };
8114
8115 assert_components(0, 0);
8116 assert_components(3, 2);
8117 }
8118
8119 #[test]
8120 fn compact_manifest_is_exact_and_preserves_permanent_head_mapping() {
8121 let mut manifest = ResidentAllocationManifest {
8122 slots: vec![
8123 ResidentPhysicalSlotPlan {
8124 schema: Schema::new(Vec::new()),
8125 initial_count: 0,
8126 permanent: true,
8127 },
8128 ResidentPhysicalSlotPlan {
8129 schema: Schema::new(Vec::new()),
8130 initial_count: 0,
8131 permanent: true,
8132 },
8133 ],
8134 logical_to_slot: vec![
8135 ResidentSlotAssignment {
8136 slot: 0,
8137 generation: 0,
8138 },
8139 ResidentSlotAssignment {
8140 slot: 1,
8141 generation: 0,
8142 },
8143 ],
8144 required_bytes: 0,
8145 relation_bytes: 1_000,
8146 filter_scratch_bytes: 2_000,
8147 schedule_metadata_bytes: 0,
8148 fixed_workspace_bytes: 3_000,
8149 logical_relation_values: 2,
8150 permanent_slots: 2,
8151 scratch_slots: 0,
8152 filter_scratch_allocations: 1,
8153 max_row_bytes: 1,
8154 };
8155 let plan = super::ResidentCompactSchedulePlan {
8156 source_slots: [("external".to_owned(), 2)].into_iter().collect(),
8157 ops: Vec::new(),
8158 waves: Vec::new(),
8159 regions: Vec::new(),
8160 generation_bases: Vec::new(),
8161 filter_comparisons: Vec::new(),
8162 project_expressions: Vec::new(),
8163 };
8164 manifest.finalize_compact_schedule(&plan, 1).unwrap();
8165 let without_source =
8166 resident_compact_schedule_metadata_bytes(2, 0, 0, 0, 0, 1, 0, 0).unwrap();
8167 assert_eq!(manifest.schedule_metadata_bytes - without_source, 240);
8168 assert_eq!(
8169 manifest.required_bytes,
8170 manifest.relation_bytes
8171 + manifest.filter_scratch_bytes
8172 + manifest.fixed_workspace_bytes
8173 + manifest.schedule_metadata_bytes
8174 );
8175 resident_validate_exact_reservation(manifest.required_bytes, manifest.required_bytes, 0)
8176 .unwrap();
8177 assert!(resident_validate_exact_reservation(manifest.required_bytes, 5_999, 1).is_err());
8178 let heads = [("head".to_owned(), 0_usize)].into_iter().collect();
8179 assert_eq!(
8180 resident_output_indices(&heads, &manifest.logical_to_slot, &manifest.slots).unwrap(),
8181 vec![("head".to_owned(), 0)]
8182 );
8183 let mut scratch_slots = manifest.slots.clone();
8184 scratch_slots[0].permanent = false;
8185 assert!(
8186 resident_output_indices(&heads, &manifest.logical_to_slot, &scratch_slots,).is_err()
8187 );
8188 }
8189
8190 #[test]
8191 fn compact_distinct_declines_partial_keys_before_planning() {
8192 let node = RirNode::Distinct {
8193 input: Box::new(RirNode::Scan { rel: RelId(1) }),
8194 key_cols: vec![0],
8195 };
8196 let schema = Schema::new(vec![
8197 ("key".to_owned(), ScalarType::U32),
8198 ("value".to_owned(), ScalarType::U32),
8199 ]);
8200
8201 let error = validate_compact_resident_node_envelope(&node, &|_| Ok(schema.clone()))
8202 .expect_err("a partial-key distinct must decline before allocation");
8203
8204 let crate::resident_graph::ResidentGraphDeclineReason::WorkspaceUnbounded { detail } =
8205 error
8206 else {
8207 panic!("expected a workspace-envelope decline");
8208 };
8209 assert!(detail.contains("canonical full-row key columns"));
8210 }
8211
8212 #[test]
8213 fn compact_distinct_accepts_empty_keys_for_nullary_rows() {
8214 let node = RirNode::Distinct {
8215 input: Box::new(RirNode::Unit),
8216 key_cols: vec![],
8217 };
8218
8219 validate_compact_resident_node_envelope(&node, &|_| Ok(Schema::new(Vec::new())))
8220 .expect("the canonical full-row key for an arity-zero relation is empty");
8221 }
8222
8223 #[test]
8224 fn compact_join_declines_same_width_different_key_types() {
8225 let node = RirNode::Join {
8226 left: Box::new(RirNode::Scan { rel: RelId(1) }),
8227 right: Box::new(RirNode::Scan { rel: RelId(2) }),
8228 left_keys: vec![0],
8229 right_keys: vec![0],
8230 join_type: JoinType::Inner,
8231 };
8232
8233 let error = validate_compact_resident_node_envelope(&node, &|node| match node {
8234 RirNode::Scan { rel: RelId(1) } => {
8235 Ok(Schema::new(vec![("key".to_owned(), ScalarType::U32)]))
8236 }
8237 RirNode::Scan { rel: RelId(2) } => {
8238 Ok(Schema::new(vec![("key".to_owned(), ScalarType::Symbol)]))
8239 }
8240 _ => unreachable!("the envelope asks only for operand schemas"),
8241 })
8242 .expect_err("same-width but different key types must decline");
8243
8244 let crate::resident_graph::ResidentGraphDeclineReason::WorkspaceUnbounded { detail } =
8245 error
8246 else {
8247 panic!("expected a workspace-envelope decline");
8248 };
8249 assert!(detail.contains("matching U32, U64, or Symbol key types"));
8250 }
8251
8252 #[test]
8253 fn compact_union_shape_requires_unary_self_union() {
8254 assert!(resident_union_fold_mode(0).is_err());
8255 assert_eq!(
8256 resident_union_fold_mode(1).unwrap(),
8257 ResidentUnionFoldMode::SelfUnion
8258 );
8259 assert_eq!(
8260 resident_union_fold_mode(4).unwrap(),
8261 ResidentUnionFoldMode::LeftAssociated
8262 );
8263 }
8264}