Skip to main content

grafeo_engine/transaction/
mod.rs

1//! Transaction management with MVCC and configurable isolation levels.
2//!
3//! # Isolation Levels
4//!
5//! Grafeo supports multiple isolation levels to balance consistency vs. performance:
6//!
7//! | Level | Anomalies Prevented | Use Case |
8//! |-------|---------------------|----------|
9//! | [`ReadCommitted`](IsolationLevel::ReadCommitted) | Dirty reads | High throughput, relaxed consistency |
10//! | [`SnapshotIsolation`](IsolationLevel::SnapshotIsolation) | + Non-repeatable reads, phantom reads | Default - good balance |
11//! | [`Serializable`](IsolationLevel::Serializable) | + Write skew | Full ACID, financial/critical workloads |
12//!
13//! The default is **Snapshot Isolation (SI)**, which offers strong consistency
14//! guarantees while maintaining high concurrency. Each transaction sees a consistent
15//! snapshot of the database as of its start time.
16//!
17//! ## Guarantees
18//!
19//! - **Read Consistency**: A transaction always reads the same values for the same
20//!   entities throughout its lifetime (repeatable reads).
21//! - **Write-Write Conflict Detection**: If two concurrent transactions write to the
22//!   same entity, the second to commit will be aborted.
23//! - **No Dirty Reads**: A transaction never sees uncommitted changes from other
24//!   transactions.
25//! - **No Lost Updates**: Write-write conflicts prevent the lost update anomaly.
26//!
27//! ## Limitations (Write Skew Anomaly)
28//!
29//! Snapshot Isolation does **not** provide full Serializable isolation. The "write skew"
30//! anomaly is possible when two transactions read overlapping data but write to
31//! disjoint sets:
32//!
33//! ```text
34//! Initial state: A = 50, B = 50, constraint: A + B >= 0
35//!
36//! T1: Read A, B → both 50
37//! T2: Read A, B → both 50
38//! T1: Write A = A - 100 = -50 (valid: -50 + 50 = 0)
39//! T2: Write B = B - 100 = -50 (valid: 50 + -50 = 0)
40//! T1: Commit ✓
41//! T2: Commit ✓ (no write-write conflict since T1 wrote A, T2 wrote B)
42//!
43//! Final state: A = -50, B = -50, constraint violated: A + B = -100 < 0
44//! ```
45//!
46//! ## Workarounds for Write Skew
47//!
48//! If your application has invariants that span multiple entities, consider:
49//!
50//! 1. **Promote reads to writes**: Add a dummy write to entities you read if you
51//!    need them to remain unchanged.
52//! 2. **Application-level locking**: Use external locks for critical sections.
53//! 3. **Constraint checking**: Validate invariants at commit time and retry if
54//!    violated.
55//!
56//! ## Epoch-Based Versioning
57//!
58//! Grafeo uses epoch-based MVCC where:
59//! - Each commit advances the global epoch
60//! - Transactions read data visible at their start epoch
61//! - Version chains store multiple versions for concurrent access
62//! - Garbage collection removes versions no longer needed by active transactions
63//!
64//! # Example
65//!
66//! ```no_run
67//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
68//! use grafeo_engine::GrafeoDB;
69//!
70//! let db = GrafeoDB::new_in_memory();
71//! let mut session = db.session();
72//!
73//! session.begin_transaction()?;
74//!
75//! // All reads see a consistent snapshot
76//! let result = session.execute("MATCH (n:Person) RETURN n")?;
77//!
78//! // Writes are isolated until commit
79//! session.execute("INSERT (:Person {name: 'Alix'})")?;
80//!
81//! // Commit may fail if write-write conflict detected
82//! session.commit()?;
83//! # Ok(())
84//! # }
85//! ```
86
87mod manager;
88mod mvcc;
89#[cfg(feature = "parallel")]
90pub mod parallel;
91mod prepared;
92
93pub use manager::{
94    EntityId, IsolationLevel, TransactionInfo, TransactionManager, TransactionState,
95};
96pub use mvcc::{VersionChain, VersionInfo};
97pub use prepared::{CommitInfo, PreparedCommit};
98pub use write_tracker::TransactionWriteTracker;
99
100mod write_tracker;
101
102#[cfg(feature = "parallel")]
103pub use parallel::{BatchRequest, BatchResult, ExecutionStatus, ParallelExecutor};