162 lines
4.2 KiB
Rust
162 lines
4.2 KiB
Rust
//! Transaction support for graph database operations
|
|
|
|
use std::collections::HashMap;
|
|
use uuid::Uuid;
|
|
|
|
/// Transaction state
|
|
#[derive(Debug, Clone)]
|
|
pub enum TransactionState {
|
|
Active,
|
|
Committed,
|
|
RolledBack,
|
|
}
|
|
|
|
/// Transaction metadata
|
|
#[derive(Debug, Clone)]
|
|
pub struct Transaction {
|
|
pub id: String,
|
|
pub state: TransactionState,
|
|
pub operations: Vec<String>,
|
|
}
|
|
|
|
/// Transaction manager
|
|
pub struct TransactionManager {
|
|
transactions: HashMap<String, Transaction>,
|
|
}
|
|
|
|
impl TransactionManager {
|
|
/// Create a new transaction manager
|
|
pub fn new() -> Self {
|
|
Self {
|
|
transactions: HashMap::new(),
|
|
}
|
|
}
|
|
|
|
/// Begin a new transaction
|
|
pub fn begin(&mut self) -> String {
|
|
let tx_id = Uuid::new_v4().to_string();
|
|
let tx = Transaction {
|
|
id: tx_id.clone(),
|
|
state: TransactionState::Active,
|
|
operations: Vec::new(),
|
|
};
|
|
self.transactions.insert(tx_id.clone(), tx);
|
|
tx_id
|
|
}
|
|
|
|
/// Commit a transaction
|
|
pub fn commit(&mut self, tx_id: &str) -> Result<(), String> {
|
|
let tx = self
|
|
.transactions
|
|
.get_mut(tx_id)
|
|
.ok_or_else(|| format!("Transaction not found: {}", tx_id))?;
|
|
|
|
match tx.state {
|
|
TransactionState::Active => {
|
|
tx.state = TransactionState::Committed;
|
|
Ok(())
|
|
}
|
|
TransactionState::Committed => Err("Transaction already committed".to_string()),
|
|
TransactionState::RolledBack => Err("Transaction already rolled back".to_string()),
|
|
}
|
|
}
|
|
|
|
/// Rollback a transaction
|
|
pub fn rollback(&mut self, tx_id: &str) -> Result<(), String> {
|
|
let tx = self
|
|
.transactions
|
|
.get_mut(tx_id)
|
|
.ok_or_else(|| format!("Transaction not found: {}", tx_id))?;
|
|
|
|
match tx.state {
|
|
TransactionState::Active => {
|
|
tx.state = TransactionState::RolledBack;
|
|
Ok(())
|
|
}
|
|
TransactionState::Committed => Err("Cannot rollback committed transaction".to_string()),
|
|
TransactionState::RolledBack => Err("Transaction already rolled back".to_string()),
|
|
}
|
|
}
|
|
|
|
/// Add an operation to a transaction
|
|
pub fn add_operation(&mut self, tx_id: &str, operation: String) -> Result<(), String> {
|
|
let tx = self
|
|
.transactions
|
|
.get_mut(tx_id)
|
|
.ok_or_else(|| format!("Transaction not found: {}", tx_id))?;
|
|
|
|
match tx.state {
|
|
TransactionState::Active => {
|
|
tx.operations.push(operation);
|
|
Ok(())
|
|
}
|
|
_ => Err("Transaction is not active".to_string()),
|
|
}
|
|
}
|
|
|
|
/// Get transaction state
|
|
pub fn get_state(&self, tx_id: &str) -> Option<TransactionState> {
|
|
self.transactions.get(tx_id).map(|tx| tx.state.clone())
|
|
}
|
|
|
|
/// Clean up old transactions
|
|
pub fn cleanup(&mut self) {
|
|
self.transactions
|
|
.retain(|_, tx| matches!(tx.state, TransactionState::Active));
|
|
}
|
|
}
|
|
|
|
impl Default for TransactionManager {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_transaction_lifecycle() {
|
|
let mut tm = TransactionManager::new();
|
|
|
|
// Begin transaction
|
|
let tx_id = tm.begin();
|
|
assert!(matches!(
|
|
tm.get_state(&tx_id),
|
|
Some(TransactionState::Active)
|
|
));
|
|
|
|
// Add operation
|
|
tm.add_operation(&tx_id, "CREATE NODE".to_string()).unwrap();
|
|
|
|
// Commit
|
|
tm.commit(&tx_id).unwrap();
|
|
assert!(matches!(
|
|
tm.get_state(&tx_id),
|
|
Some(TransactionState::Committed)
|
|
));
|
|
|
|
// Cannot commit again
|
|
assert!(tm.commit(&tx_id).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn test_transaction_rollback() {
|
|
let mut tm = TransactionManager::new();
|
|
|
|
let tx_id = tm.begin();
|
|
tm.add_operation(&tx_id, "CREATE NODE".to_string()).unwrap();
|
|
|
|
// Rollback
|
|
tm.rollback(&tx_id).unwrap();
|
|
assert!(matches!(
|
|
tm.get_state(&tx_id),
|
|
Some(TransactionState::RolledBack)
|
|
));
|
|
|
|
// Cannot rollback again
|
|
assert!(tm.rollback(&tx_id).is_err());
|
|
}
|
|
}
|