aletheiadb 0.1.0

A high-performance bi-temporal graph database for LLM integration
Documentation
//! Transaction types and metadata
//!
//! This module defines the core types that describe the lifecycle and identity
//! of a transaction in AletheiaDB.
//!
//! # Transaction State Lifecycle
//!
//! 1.  `Active`: The transaction is currently open and accepting operations.
//! 2.  `Preparing`: The transaction is validating operations before commit.
//! 3.  `Committed` or `Aborted`: Terminal states indicating success or failure.

use crate::core::temporal::Timestamp;
// Re-export TxId from core to break dependency cycles
pub use crate::core::id::TxId;

/// Transaction state
///
/// Represents the current phase of a transaction's lifecycle.
///
/// ## Examples
///
/// ```rust
/// use aletheiadb::api::transaction::TxState;
///
/// let state = TxState::Active;
/// assert_eq!(format!("{}", state), "Active");
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TxState {
    /// Transaction is active and can perform operations
    Active,
    /// Transaction is preparing to commit (validating)
    Preparing,
    /// Transaction has committed successfully
    Committed,
    /// Transaction has been rolled back
    Aborted,
}

impl std::fmt::Display for TxState {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            TxState::Active => write!(f, "Active"),
            TxState::Preparing => write!(f, "Preparing"),
            TxState::Committed => write!(f, "Committed"),
            TxState::Aborted => write!(f, "Aborted"),
        }
    }
}

/// Transaction metadata
///
/// Contains information about a transaction's identity, timestamps, and current state.
/// This is typically retrieved via the `metadata()` method on a transaction handle.
///
/// ## Examples
///
/// ```rust
/// use aletheiadb::api::transaction::{TxId, TxState, TxMetadata};
/// use aletheiadb::core::temporal::Timestamp;
///
/// let metadata = TxMetadata {
///     tx_id: TxId::new(42),
///     start_timestamp: Timestamp::from(100),
///     commit_timestamp: None,
///     state: TxState::Active,
///     is_read_only: false,
/// };
///
/// assert_eq!(metadata.state, TxState::Active);
/// assert!(!metadata.is_read_only);
/// ```
#[derive(Debug, Clone)]
pub struct TxMetadata {
    /// Transaction ID
    pub tx_id: TxId,
    /// Timestamp when transaction started
    pub start_timestamp: Timestamp,
    /// Timestamp when transaction committed (None if not yet committed)
    pub commit_timestamp: Option<Timestamp>,
    /// Current transaction state
    pub state: TxState,
    /// Whether this is a read-only transaction
    pub is_read_only: bool,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_tx_id_creation() {
        let tx_id = TxId::new(42);
        assert_eq!(tx_id.as_u64(), 42u64);
    }

    #[test]
    fn test_tx_id_ordering() {
        let tx1 = TxId::new(1);
        let tx2 = TxId::new(2);
        assert!(tx1 < tx2);
        assert_eq!(tx1, tx1);
    }

    #[test]
    fn test_tx_id_display() {
        let tx_id = TxId::new(123);
        assert_eq!(format!("{}", tx_id), "TxId(123)");
    }

    #[test]
    fn test_tx_state_display() {
        assert_eq!(format!("{}", TxState::Active), "Active");
        assert_eq!(format!("{}", TxState::Preparing), "Preparing");
        assert_eq!(format!("{}", TxState::Committed), "Committed");
        assert_eq!(format!("{}", TxState::Aborted), "Aborted");
    }

    #[test]
    fn test_tx_id_generator() {
        use crate::core::id::TxIdGenerator;
        let generator = TxIdGenerator::new();
        let tx1 = generator.next();
        let tx2 = generator.next();
        let tx3 = generator.next();

        assert_eq!(tx1.as_u64(), 1u64);
        assert_eq!(tx2.as_u64(), 2u64);
        assert_eq!(tx3.as_u64(), 3u64);
        assert_eq!(generator.current().as_u64(), 3u64);
    }

    #[test]
    fn test_tx_id_generator_concurrent() {
        use crate::core::id::TxIdGenerator;
        use std::sync::Arc;
        use std::thread;

        let generator = Arc::new(TxIdGenerator::new());
        let mut handles = vec![];

        // Spawn 10 threads, each generating 100 IDs
        for _ in 0..10 {
            let generator_clone = Arc::clone(&generator);
            let handle = thread::spawn(move || {
                let mut ids = vec![];
                for _ in 0..100 {
                    ids.push(generator_clone.next());
                }
                ids
            });
            handles.push(handle);
        }

        // Collect all generated IDs
        let mut all_ids: Vec<TxId> = vec![];
        for handle in handles {
            all_ids.extend(handle.join().unwrap());
        }

        // All IDs should be unique
        all_ids.sort();
        let unique_count = all_ids
            .iter()
            .collect::<std::collections::HashSet<_>>()
            .len();
        assert_eq!(unique_count, 1000);

        // Final current should be 1000
        assert_eq!(generator.current().as_u64(), 1000u64);
    }

    #[test]
    fn test_tx_metadata() {
        let metadata = TxMetadata {
            tx_id: TxId::new(1),
            start_timestamp: 100.into(),
            commit_timestamp: None,
            state: TxState::Active,
            is_read_only: false,
        };

        assert_eq!(metadata.tx_id, TxId::new(1));
        assert_eq!(metadata.start_timestamp, 100.into());
        assert_eq!(metadata.commit_timestamp, None);
        assert_eq!(metadata.state, TxState::Active);
        assert!(!metadata.is_read_only);
    }
}