1#![deny(
20 clippy::expect_used,
21 clippy::indexing_slicing,
22 clippy::panic,
23 clippy::todo,
24 clippy::unimplemented,
25 clippy::unwrap_used,
26 unsafe_op_in_unsafe_fn
27)]
28#![warn(missing_docs)]
29
30#[cfg(all(
31 feature = "graph-cypher",
32 not(any(
33 target_os = "macos",
34 all(target_os = "windows", target_arch = "x86_64")
35 ))
36))]
37compile_error!("graph-cypher supports macOS (arm64, x86_64) and Windows x64 only");
38
39#[cfg(feature = "allocation-audit")]
40mod allocation_audit;
41
42#[cfg(any(test, feature = "test-seams"))]
43#[doc(hidden)]
44pub mod adversarial_test_support;
45
46#[cfg(all(feature = "graph-cypher", feature = "test-seams"))]
48#[doc(hidden)]
49pub mod graph_structured_write_test_support {
50 use crate::lifecycle::{CancelToken, QueryControl, Store};
51 use crate::property_graph::staging::{StructuredOperation, StructuredWrite, WriteImage};
52 use crate::property_graph::{
53 ApplicationKey, CanonicalContents, EntityId, EntityKind, GraphGeneration, GraphName,
54 GraphProperty, GraphRevision, NodeId, PropertyData, PropertyValue,
55 };
56
57 pub fn create_keyed_node(store: &Store) -> Result<(NodeId, GraphGeneration), String> {
59 let mut labels = [GraphName::new("A").map_err(|e| format!("label: {e:?}"))?];
60 let mut properties = [GraphProperty::new(
61 GraphName::new("v").map_err(|e| format!("property name: {e:?}"))?,
62 PropertyValue::new(PropertyData::I64(1))
63 .map_err(|e| format!("property value: {e:?}"))?,
64 )];
65 let image = CanonicalContents::node(&mut labels, &mut properties, None, None)
66 .map_err(|e| format!("node image: {e:?}"))?;
67 let receipts = store
68 .apply_native_graph(
69 &[StructuredWrite {
70 key: ApplicationKey::new(EntityKind::Node, "app", "ze214")
71 .map_err(|e| format!("application key: {e:?}"))?,
72 revision: GraphRevision::new(1).map_err(|e| format!("revision: {e:?}"))?,
73 operation: StructuredOperation::Create,
74 image: Some(WriteImage::Node(&image)),
75 }],
76 &QueryControl::Cancel(CancelToken::new()),
77 )
78 .map_err(|e| format!("structured create: {e:?}"))?;
79 if receipts.len() != 1 {
80 return Err("structured create did not return exactly one receipt".to_owned());
81 }
82 let receipt = receipts
83 .first()
84 .ok_or_else(|| "structured create returned no receipt".to_owned())?;
85 match receipt.entity {
86 EntityId::Node(node) => Ok((node, receipt.generation)),
87 EntityId::Relationship(_) => {
88 Err("structured create returned a relationship".to_owned())
89 }
90 }
91 }
92}
93
94#[cfg(all(feature = "graph-cypher", feature = "test-seams"))]
95#[doc(hidden)]
96pub mod graph_read_view_test_support {
97 #[derive(Clone, Debug, Eq, PartialEq)]
100 pub struct PathReceipt {
101 pub key: &'static str,
103 pub fires: u64,
105 pub clean_controls: u64,
107 }
108
109 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
112 pub struct ObservedRelationship {
113 pub rel: u128,
114 pub source: u128,
115 pub target: u128,
116 pub relationship_type: u64,
117 }
118
119 #[derive(Clone, Debug, Eq, PartialEq)]
121 pub struct ActualProbeReport {
122 pub receipts: Vec<PathReceipt>,
123 pub relationships: Vec<ObservedRelationship>,
124 }
125 pub fn controlled_boundary_is_available() -> bool {
129 std::mem::size_of::<crate::lifecycle::native_graph::NativeReadLease>() > 0
130 && std::mem::size_of::<crate::property_graph::storage::GraphReadView<'_, '_, '_, '_>>()
131 > 0
132 }
133
134 pub fn run_actual_probe(seed: u64) -> ActualProbeReport {
138 crate::lifecycle::native_graph::tests::run_adversarial_probe(seed)
139 }
140}
141
142#[cfg(all(feature = "graph-cypher", feature = "test-seams"))]
143#[doc(hidden)]
144pub mod graph_publication_test_support {
145 pub fn run_actual_probe(seed: u64) -> crate::graph_read_view_test_support::ActualProbeReport {
147 crate::lifecycle::native_graph::tests::publication::run_actual_probe(seed)
148 }
149}
150
151#[cfg(all(feature = "graph-cypher", feature = "test-seams"))]
152#[doc(hidden)]
153pub mod graph_identity_test_support {
154 use crate::graph_read_view_test_support::PathReceipt;
155
156 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
158 pub struct ObservedAdjacency {
159 pub bound_node: u128,
161 pub relationship_type: u64,
163 pub rel: u128,
165 pub neighbor: u128,
167 }
168
169 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
171 pub struct ObservedOutcome {
172 pub key: u32,
174 pub rejection: Option<&'static str>,
176 pub entity: u128,
178 pub revision: u64,
180 pub generation: u64,
182 pub replayed: bool,
184 }
185
186 #[derive(Clone, Debug, Eq, PartialEq)]
188 pub struct ObservedWatch {
189 pub batch: usize,
191 pub generation: u64,
193 pub key: u32,
195 pub outgoing: bool,
197 pub rows: Vec<ObservedAdjacency>,
199 }
200
201 #[derive(Clone, Debug, Eq, PartialEq)]
203 pub struct IdentityState {
204 pub outcomes: Vec<ObservedOutcome>,
206 pub watched: Vec<ObservedWatch>,
208 pub retained_generation: u64,
210 pub retained_rows: Vec<ObservedAdjacency>,
213 pub fresh_generation: u64,
215 pub fresh_rows: Vec<ObservedAdjacency>,
217 }
218
219 #[derive(Clone, Debug, Eq, PartialEq)]
221 pub struct IdentityProbeReport {
222 pub receipts: Vec<PathReceipt>,
224 pub state: IdentityState,
226 }
227
228 pub fn run_actual_probe(seed: u64) -> IdentityProbeReport {
230 crate::lifecycle::native_graph::tests::run_identity_probe(seed)
231 }
232}
233
234#[cfg(all(feature = "graph-cypher", feature = "test-seams"))]
235#[doc(hidden)]
236pub mod graph_reclaim_test_support {
237 use crate::graph_read_view_test_support::{ObservedRelationship, PathReceipt};
238
239 #[derive(Clone, Debug, Eq, PartialEq)]
242 pub struct ReclaimState {
243 pub first_node: u128,
245 pub second_node: u128,
247 pub relationships: Vec<ObservedRelationship>,
249 pub replay_generation: u64,
251 pub replayed: bool,
253 pub removed_bytes: u64,
255 pub unlinked_file_bytes: u64,
257 }
258
259 #[derive(Clone, Debug, Eq, PartialEq)]
261 pub struct ReclaimProbeReport {
262 pub receipts: Vec<PathReceipt>,
264 pub state: ReclaimState,
266 pub detach_sweep: (bool, u64),
268 pub host_budget: (u64, u64, u64, bool),
270 }
271
272 #[derive(Clone, Debug, Eq, PartialEq)]
274 pub struct RaceProbeReport {
275 pub observation: (u64, Vec<u8>, bool, bool),
277 pub control: (u64, Vec<u8>, bool, bool),
279 }
280
281 pub fn run_ze176_race_probe(seed: u64) -> RaceProbeReport {
283 crate::lifecycle::native_graph::tests::run_ze176_race_probe(seed)
284 }
285
286 pub fn run_actual_probe(seed: u64) -> ReclaimProbeReport {
289 crate::lifecycle::native_graph::tests::run_reclaim_probe(seed)
290 }
291}
292
293#[cfg(all(feature = "graph-cypher", feature = "test-seams"))]
294#[doc(hidden)]
295pub mod graph_storage_fault_test_support {
296 use crate::graph_read_view_test_support::{ObservedRelationship, PathReceipt};
297 use crate::lifecycle::{OpenOptions, Store};
298 use crate::vfs::Vfs;
299 use std::path::Path;
300 use std::sync::Arc;
301
302 #[derive(Clone, Debug, Eq, PartialEq)]
305 pub struct ObservedKeyedNode {
306 pub key: Vec<u8>,
308 pub node: u128,
310 pub revision: u64,
312 }
313
314 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
317 pub struct StorageFaultSchedule {
318 pub artifact_ref_offset: u64,
320 pub split_keys: u32,
323 pub split_probe: u32,
325 pub split_skip: u32,
327 pub out_in_append: u32,
329 pub root_variant: u8,
331 pub qualification_seed: u64,
333 }
334
335 #[derive(Clone, Debug, Default, Eq, PartialEq)]
337 pub struct QualificationCell {
338 pub name: String,
340 pub fires: u64,
342 pub controls: u64,
343 pub refusal: String,
345 pub unauthorized_unlinks: u64,
347 pub changed_live_files: u64,
348 pub intent_preserved: bool,
350 pub resumed: bool,
351 pub removed_bytes: u64,
353 pub unlinked_bytes: u64,
354 pub exposed_blocks: u64,
356 pub filled: usize,
358 pub opens: usize,
360 pub logged_opens: usize,
361 pub scoped_opens: usize,
362 pub runs: usize,
364 pub merges: usize,
365 pub folded: usize,
366 pub count_debt: u64,
368 }
369
370 #[derive(Clone, Debug, Eq, PartialEq)]
372 pub struct QualificationPack {
373 pub serial: u64,
374 pub bytes: u32,
375 pub live: u64,
376 pub pages: u32,
377 pub records: u32,
378 pub graph_live: bool,
379 }
380 #[derive(Clone, Debug, Default, Eq, PartialEq)]
382 pub struct QualificationSelection {
383 pub packs: Vec<QualificationPack>,
384 pub selected_serials: Vec<u64>,
385 pub clean_serials: Vec<u64>,
386 pub byte_limit: u64,
387 pub ranges: Vec<(u8, usize, usize)>,
389 pub selected_ranges: Vec<(u8, usize)>,
390 pub clean_ranges: Vec<(u8, usize)>,
391 pub fixture_pending: Vec<usize>,
392 }
393
394 #[derive(Clone, Debug, Eq, PartialEq)]
396 pub struct QualificationCommit {
397 pub generation: u64,
398 pub opens: usize,
399 pub logged_opens: usize,
400 pub filled: usize,
401 pub scoped_opens: usize,
402 }
403
404 #[derive(Clone, Debug, Default, Eq, PartialEq)]
406 pub struct QualificationReport {
407 pub cells: Vec<QualificationCell>,
408 pub selection: Option<QualificationSelection>,
409 pub commits: Vec<QualificationCommit>,
410 }
411
412 pub fn run_qualification_probe(seed: u64, mapping: bool) -> QualificationReport {
414 crate::lifecycle::native_graph::tests::storage_faults::run_qualification_probe(
415 seed, mapping,
416 )
417 }
418
419 #[derive(Clone, Debug, Eq, PartialEq)]
421 pub struct StorageFaultState {
422 pub artifact_refusals: Vec<String>,
424 pub surviving_nodes: Vec<ObservedKeyedNode>,
426 pub split_level: u16,
429 pub retained_split_level: u16,
432 pub pre_split_keys: u32,
435 pub committed_keys: Vec<ObservedKeyedNode>,
437 pub old_root_keys: Vec<ObservedKeyedNode>,
439 pub split_generations: (u64, u64),
442 pub retained_root_digests: (u64, u64),
445 pub unsplit_generations: (u64, u64),
449 pub unsplit_reopen_level: u16,
452 pub split_refusals: Vec<String>,
454 pub out_rows: Vec<ObservedRelationship>,
456 pub in_rows: Vec<ObservedRelationship>,
458 pub out_in_refusals: Vec<String>,
460 pub root_refusals: Vec<String>,
462 pub reopened_generation: u64,
464 pub previous_generation: u64,
466 pub qualification: QualificationReport,
468 }
469
470 #[derive(Clone, Debug, Eq, PartialEq)]
472 pub struct StorageFaultProbeReport {
473 pub receipts: Vec<PathReceipt>,
475 pub state: StorageFaultState,
477 }
478
479 pub fn run_actual_probe(seed: u64, schedule: StorageFaultSchedule) -> StorageFaultProbeReport {
482 crate::lifecycle::native_graph::tests::run_storage_fault_probe(seed, schedule)
483 }
484
485 pub fn create_native_graph_with_infrastructure(
490 path: &Path,
491 options: OpenOptions,
492 vfs: Arc<dyn Vfs>,
493 ) -> Result<Store, String> {
494 Store::create_native_graph_with_infrastructure(
495 path,
496 options,
497 None,
498 vfs,
499 Arc::new(crate::lifecycle::SystemMonotonicClock),
500 &mut crate::property_graph::storage::allocation::OsEntropy,
501 )
502 .map_err(|error| error.to_string())
503 }
504
505 pub fn open_native_graph_with_infrastructure(
508 path: &Path,
509 options: OpenOptions,
510 vfs: Arc<dyn Vfs>,
511 ) -> Result<Store, String> {
512 Store::open_native_graph_with_infrastructure(
513 path,
514 options,
515 None,
516 vfs,
517 Arc::new(crate::lifecycle::SystemMonotonicClock),
518 )
519 .map_err(|error| error.to_string())
520 }
521}
522
523#[cfg(all(feature = "graph-cypher", feature = "test-seams"))]
524#[doc(hidden)]
525pub mod graph_recovery_test_support {
526 use crate::graph_read_view_test_support::{ObservedRelationship, PathReceipt};
527
528 pub fn create_with_relationship_rules(
531 path: &std::path::Path,
532 options: crate::lifecycle::OpenOptions,
533 document: Option<crate::epoch::EmbeddingTower>,
534 rules: crate::property_graph::catalog::RelationshipRules<'_>,
535 ) -> Result<crate::lifecycle::Store, String> {
536 crate::lifecycle::Store::create_native_graph_with_relationship_types(
537 path, options, document, rules,
538 )
539 .map_err(|error| error.to_string())
540 }
541
542 #[allow(clippy::result_large_err)]
544 pub fn configure_unified_runner(
545 store: &crate::lifecycle::Store,
546 ) -> Result<(), crate::property_graph::GraphStoreError> {
547 store
548 .set_native_graph_maintenance_policy(crate::property_graph::GraphMaintenancePolicy {
549 automatic: false,
550 ..Default::default()
551 })
552 .map_err(Into::into)
553 }
554
555 #[allow(clippy::result_large_err)]
557 pub fn apply_unified_batch(
558 store: &crate::lifecycle::Store,
559 documents: Option<&crate::ingest::IngestBatch>,
560 requests: &[crate::property_graph::staging::StructuredWrite<'_, '_>],
561 control: &crate::lifecycle::QueryControl,
562 ) -> Result<(u64, bool), crate::property_graph::GraphStoreError> {
563 let result = match documents {
564 Some(documents) => store.apply_native_mixed(documents, requests, control),
565 None => store.apply_native_graph(requests, control),
566 }
567 .map_err(crate::property_graph::GraphStoreError::from)?;
568 let changed = result.changed_generation();
569 Ok((
570 changed.unwrap_or(result.admitted_generation()).get(),
571 changed.is_some(),
572 ))
573 }
574
575 #[allow(clippy::result_large_err)]
577 pub fn unified_graph_enabled(
578 store: &crate::lifecycle::Store,
579 ) -> Result<bool, crate::property_graph::GraphStoreError> {
580 store.native_graph.is_installed().map_err(|error| {
581 crate::property_graph::GraphStoreError::from(
582 crate::lifecycle::native_graph::NativeGraphError::Store(error),
583 )
584 })
585 }
586
587 pub fn apply_mixed_batch(
589 store: &crate::lifecycle::Store,
590 documents: &crate::ingest::IngestBatch,
591 key: &str,
592 ) -> Result<(crate::property_graph::NodeId, u64), String> {
593 use crate::property_graph::staging::{StructuredOperation, StructuredWrite, WriteImage};
594 use crate::property_graph::{
595 ApplicationKey, CanonicalContents, EntityId, EntityKind, GraphRevision,
596 };
597 let image =
598 CanonicalContents::node(&mut [], &mut [], None, None).map_err(|e| e.to_string())?;
599 let requests = [StructuredWrite {
600 key: ApplicationKey::new(EntityKind::Node, "mixed-probe", key)
601 .map_err(|e| e.to_string())?,
602 revision: GraphRevision::new(1).map_err(|e| e.to_string())?,
603 operation: StructuredOperation::Create,
604 image: Some(WriteImage::Node(&image)),
605 }];
606 let result = store
607 .apply_native_mixed(
608 documents,
609 &requests,
610 &crate::lifecycle::QueryControl::Cancel(crate::lifecycle::CancelToken::new()),
611 )
612 .map_err(|e| e.to_string())?;
613 let receipt = result.first().ok_or("mixed probe omitted node receipt")?;
614 let EntityId::Node(node) = receipt.entity else {
615 return Err("mixed probe returned relationship".into());
616 };
617 let generation = result
618 .changed_generation()
619 .ok_or("mixed probe did not commit")?;
620 Ok((node, generation.get()))
621 }
622
623 pub fn mixed_node_present(
625 store: &crate::lifecycle::Store,
626 node: crate::property_graph::NodeId,
627 ) -> bool {
628 crate::lifecycle::native_graph::tests::observe_mixed_node(store, node)
629 }
630
631 #[derive(Clone, Debug, Eq, PartialEq)]
633 pub struct RecoveryState {
634 pub store: u128,
636 pub generation: u64,
638 pub sequence: u64,
640 pub first_node: u128,
642 pub second_node: u128,
644 pub relationship: ObservedRelationship,
646 }
647
648 #[derive(Clone, Debug, Eq, PartialEq)]
650 pub struct RecoveryProbeReport {
651 pub receipts: Vec<PathReceipt>,
653 pub state: RecoveryState,
655 }
656
657 pub fn run_seal_rotation_probe() -> Vec<PathReceipt> {
659 crate::lifecycle::native_graph::tests::seal::run_rotation_probe()
660 }
661
662 pub fn run_seal_cancellation_probe() -> Vec<PathReceipt> {
664 crate::lifecycle::native_graph::tests::seal::run_cancellation_probe()
665 }
666
667 pub fn run_enable_retry_probe() -> Vec<PathReceipt> {
669 crate::lifecycle::native_graph::tests::recovery::run_enable_retry_probe()
670 }
671
672 pub fn run_actual_probe(seed: u64) -> RecoveryProbeReport {
674 crate::lifecycle::native_graph::tests::run_recovery_probe(seed)
675 }
676}
677
678#[cfg(all(feature = "graph-cypher", any(test, feature = "test-seams")))]
680pub mod graph_commit_recovery_test_support {
681 pub use crate::property_graph::commit_recovery_test_support::*;
682}
683
684pub mod diag;
686pub mod epoch;
688pub mod format;
690pub mod fts;
692pub mod fusion;
694pub mod graph;
696pub mod ingest;
698pub mod kernels;
700pub mod lifecycle;
702pub mod manifest;
704pub mod meta;
706pub mod planner;
708#[cfg(feature = "graph-cypher")]
710pub mod property_graph;
711pub mod quant;
713pub mod scan;
715pub mod segment;
717pub mod sys;
719pub mod tier;
721pub mod verify;
723pub mod vfs;
725pub mod wal;
727
728#[cfg(test)]
729mod test_support;
730
731pub const VERSION: &str = env!("CARGO_PKG_VERSION");
733
734#[cfg(test)]
735#[allow(
736 clippy::expect_used,
737 clippy::indexing_slicing,
738 clippy::panic,
739 clippy::unwrap_used
740)]
741mod tests {
742 use rand::RngCore;
743
744 #[test]
745 #[cfg_attr(miri, ignore = "environment-dependent RNG is outside the Miri subset")]
746 fn seeded_rng_is_deterministic_per_name_and_env() {
747 let mut first =
748 crate::test_support::seeded_rng("tests::seeded_rng_is_deterministic_per_name_and_env");
749 let mut second =
750 crate::test_support::seeded_rng("tests::seeded_rng_is_deterministic_per_name_and_env");
751 let mut other_name = crate::test_support::seeded_rng("tests::another_test");
752
753 let first_draw = first.next_u64();
754 let second_draw = second.next_u64();
755 let other_name_draw = other_name.next_u64();
756
757 let environment_seed = std::env::var("ZE_TEST_SEED").unwrap_or_else(|_| String::from("0"));
758 let other_environment_seed = format!("{environment_seed}-different");
759 let mut other_env = crate::test_support::seeded_rng_from(
760 "tests::seeded_rng_is_deterministic_per_name_and_env",
761 &other_environment_seed,
762 );
763 let other_env_draw = other_env.next_u64();
764
765 assert_eq!(first_draw, second_draw);
766 assert_ne!(first_draw, other_name_draw);
767 assert_ne!(first_draw, other_env_draw);
768 }
769
770 #[test]
771 fn version_constant_is_current() {
772 assert_eq!(crate::VERSION, "0.7.0");
773 }
774}