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 pub preparation_page_decodes: Option<[[u64; 3]; 2]>,
412 }
413
414 pub fn run_qualification_probe(seed: u64, mapping: bool) -> QualificationReport {
416 crate::lifecycle::native_graph::tests::storage_faults::run_qualification_probe(
417 seed, mapping,
418 )
419 }
420
421 #[derive(Clone, Debug, Eq, PartialEq)]
423 pub struct StorageFaultState {
424 pub artifact_refusals: Vec<String>,
426 pub surviving_nodes: Vec<ObservedKeyedNode>,
428 pub split_level: u16,
431 pub retained_split_level: u16,
434 pub pre_split_keys: u32,
437 pub committed_keys: Vec<ObservedKeyedNode>,
439 pub old_root_keys: Vec<ObservedKeyedNode>,
441 pub split_generations: (u64, u64),
444 pub retained_root_digests: (u64, u64),
447 pub unsplit_generations: (u64, u64),
451 pub unsplit_reopen_level: u16,
454 pub split_refusals: Vec<String>,
456 pub out_rows: Vec<ObservedRelationship>,
458 pub in_rows: Vec<ObservedRelationship>,
460 pub out_in_refusals: Vec<String>,
462 pub root_refusals: Vec<String>,
464 pub reopened_generation: u64,
466 pub previous_generation: u64,
468 pub qualification: QualificationReport,
470 }
471
472 #[derive(Clone, Debug, Eq, PartialEq)]
474 pub struct StorageFaultProbeReport {
475 pub receipts: Vec<PathReceipt>,
477 pub state: StorageFaultState,
479 }
480
481 pub fn run_actual_probe(seed: u64, schedule: StorageFaultSchedule) -> StorageFaultProbeReport {
484 crate::lifecycle::native_graph::tests::run_storage_fault_probe(seed, schedule)
485 }
486
487 pub fn create_native_graph_with_infrastructure(
492 path: &Path,
493 options: OpenOptions,
494 vfs: Arc<dyn Vfs>,
495 ) -> Result<Store, String> {
496 Store::create_native_graph_with_infrastructure(
497 path,
498 options,
499 None,
500 vfs,
501 Arc::new(crate::lifecycle::SystemMonotonicClock),
502 &mut crate::property_graph::storage::allocation::OsEntropy,
503 )
504 .map_err(|error| error.to_string())
505 }
506
507 pub fn open_native_graph_with_infrastructure(
510 path: &Path,
511 options: OpenOptions,
512 vfs: Arc<dyn Vfs>,
513 ) -> Result<Store, String> {
514 Store::open_native_graph_with_infrastructure(
515 path,
516 options,
517 None,
518 vfs,
519 Arc::new(crate::lifecycle::SystemMonotonicClock),
520 )
521 .map_err(|error| error.to_string())
522 }
523}
524
525#[cfg(all(feature = "graph-cypher", feature = "test-seams"))]
526#[doc(hidden)]
527pub mod graph_recovery_test_support {
528 use crate::graph_read_view_test_support::{ObservedRelationship, PathReceipt};
529
530 pub fn create_with_relationship_rules(
533 path: &std::path::Path,
534 options: crate::lifecycle::OpenOptions,
535 document: Option<crate::epoch::EmbeddingTower>,
536 rules: crate::property_graph::catalog::RelationshipRules<'_>,
537 ) -> Result<crate::lifecycle::Store, String> {
538 crate::lifecycle::Store::create_native_graph_with_relationship_types(
539 path, options, document, rules,
540 )
541 .map_err(|error| error.to_string())
542 }
543
544 #[allow(clippy::result_large_err)]
546 pub fn configure_unified_runner(
547 store: &crate::lifecycle::Store,
548 ) -> Result<(), crate::property_graph::GraphStoreError> {
549 store
550 .set_native_graph_maintenance_policy(crate::property_graph::GraphMaintenancePolicy {
551 automatic: false,
552 ..Default::default()
553 })
554 .map_err(Into::into)
555 }
556
557 #[allow(clippy::result_large_err)]
559 pub fn apply_unified_batch(
560 store: &crate::lifecycle::Store,
561 documents: Option<&crate::ingest::IngestBatch>,
562 requests: &[crate::property_graph::staging::StructuredWrite<'_, '_>],
563 control: &crate::lifecycle::QueryControl,
564 ) -> Result<(u64, bool), crate::property_graph::GraphStoreError> {
565 let result = match documents {
566 Some(documents) => store.apply_native_mixed(documents, requests, control),
567 None => store.apply_native_graph(requests, control),
568 }
569 .map_err(crate::property_graph::GraphStoreError::from)?;
570 let changed = result.changed_generation();
571 Ok((
572 changed.unwrap_or(result.admitted_generation()).get(),
573 changed.is_some(),
574 ))
575 }
576
577 #[allow(clippy::result_large_err)]
579 pub fn unified_graph_enabled(
580 store: &crate::lifecycle::Store,
581 ) -> Result<bool, crate::property_graph::GraphStoreError> {
582 store.native_graph.is_installed().map_err(|error| {
583 crate::property_graph::GraphStoreError::from(
584 crate::lifecycle::native_graph::NativeGraphError::Store(error),
585 )
586 })
587 }
588
589 pub fn apply_mixed_batch(
591 store: &crate::lifecycle::Store,
592 documents: &crate::ingest::IngestBatch,
593 key: &str,
594 ) -> Result<(crate::property_graph::NodeId, u64), String> {
595 use crate::property_graph::staging::{StructuredOperation, StructuredWrite, WriteImage};
596 use crate::property_graph::{
597 ApplicationKey, CanonicalContents, EntityId, EntityKind, GraphRevision,
598 };
599 let image =
600 CanonicalContents::node(&mut [], &mut [], None, None).map_err(|e| e.to_string())?;
601 let requests = [StructuredWrite {
602 key: ApplicationKey::new(EntityKind::Node, "mixed-probe", key)
603 .map_err(|e| e.to_string())?,
604 revision: GraphRevision::new(1).map_err(|e| e.to_string())?,
605 operation: StructuredOperation::Create,
606 image: Some(WriteImage::Node(&image)),
607 }];
608 let result = store
609 .apply_native_mixed(
610 documents,
611 &requests,
612 &crate::lifecycle::QueryControl::Cancel(crate::lifecycle::CancelToken::new()),
613 )
614 .map_err(|e| e.to_string())?;
615 let receipt = result.first().ok_or("mixed probe omitted node receipt")?;
616 let EntityId::Node(node) = receipt.entity else {
617 return Err("mixed probe returned relationship".into());
618 };
619 let generation = result
620 .changed_generation()
621 .ok_or("mixed probe did not commit")?;
622 Ok((node, generation.get()))
623 }
624
625 pub fn mixed_node_present(
627 store: &crate::lifecycle::Store,
628 node: crate::property_graph::NodeId,
629 ) -> bool {
630 crate::lifecycle::native_graph::tests::observe_mixed_node(store, node)
631 }
632
633 #[derive(Clone, Debug, Eq, PartialEq)]
635 pub struct RecoveryState {
636 pub store: u128,
638 pub generation: u64,
640 pub sequence: u64,
642 pub first_node: u128,
644 pub second_node: u128,
646 pub relationship: ObservedRelationship,
648 }
649
650 #[derive(Clone, Debug, Eq, PartialEq)]
652 pub struct RecoveryProbeReport {
653 pub receipts: Vec<PathReceipt>,
655 pub state: RecoveryState,
657 }
658
659 pub fn run_seal_rotation_probe() -> Vec<PathReceipt> {
661 crate::lifecycle::native_graph::tests::seal::run_rotation_probe()
662 }
663
664 pub fn run_seal_cancellation_probe() -> Vec<PathReceipt> {
666 crate::lifecycle::native_graph::tests::seal::run_cancellation_probe()
667 }
668
669 pub fn run_enable_retry_probe() -> Vec<PathReceipt> {
671 crate::lifecycle::native_graph::tests::recovery::run_enable_retry_probe()
672 }
673
674 pub fn run_actual_probe(seed: u64) -> RecoveryProbeReport {
676 crate::lifecycle::native_graph::tests::run_recovery_probe(seed)
677 }
678}
679
680#[cfg(all(feature = "graph-cypher", any(test, feature = "test-seams")))]
682pub mod graph_commit_recovery_test_support {
683 pub use crate::property_graph::commit_recovery_test_support::*;
684}
685
686pub mod diag;
688pub mod epoch;
690pub mod format;
692pub mod fts;
694pub mod fusion;
696pub mod graph;
698pub mod ingest;
700pub mod kernels;
702pub mod lifecycle;
704pub mod manifest;
706pub mod meta;
708pub mod planner;
710#[cfg(feature = "graph-cypher")]
712pub mod property_graph;
713pub mod quant;
715pub mod scan;
717pub mod segment;
719pub mod sys;
721pub mod tier;
723pub mod verify;
725pub mod vfs;
727pub mod wal;
729
730#[cfg(test)]
731mod test_support;
732
733pub const VERSION: &str = env!("CARGO_PKG_VERSION");
735
736#[cfg(test)]
737#[allow(
738 clippy::expect_used,
739 clippy::indexing_slicing,
740 clippy::panic,
741 clippy::unwrap_used
742)]
743mod tests {
744 use rand::RngCore;
745
746 #[test]
747 #[cfg_attr(miri, ignore = "environment-dependent RNG is outside the Miri subset")]
748 fn seeded_rng_is_deterministic_per_name_and_env() {
749 let mut first =
750 crate::test_support::seeded_rng("tests::seeded_rng_is_deterministic_per_name_and_env");
751 let mut second =
752 crate::test_support::seeded_rng("tests::seeded_rng_is_deterministic_per_name_and_env");
753 let mut other_name = crate::test_support::seeded_rng("tests::another_test");
754
755 let first_draw = first.next_u64();
756 let second_draw = second.next_u64();
757 let other_name_draw = other_name.next_u64();
758
759 let environment_seed = std::env::var("ZE_TEST_SEED").unwrap_or_else(|_| String::from("0"));
760 let other_environment_seed = format!("{environment_seed}-different");
761 let mut other_env = crate::test_support::seeded_rng_from(
762 "tests::seeded_rng_is_deterministic_per_name_and_env",
763 &other_environment_seed,
764 );
765 let other_env_draw = other_env.next_u64();
766
767 assert_eq!(first_draw, second_draw);
768 assert_ne!(first_draw, other_name_draw);
769 assert_ne!(first_draw, other_env_draw);
770 }
771
772 #[test]
773 fn version_constant_is_current() {
774 assert_eq!(crate::VERSION, "0.8.0");
775 }
776}