Skip to main content

reddb_server/storage/wal/
mod.rs

1//! Write-Ahead Log (WAL) for RedDB durability.
2//!
3//! Handles crash recovery by logging all changes before they are applied to the database file.
4//!
5//! The persisted WAL header, record envelope, segment sizing, compression tags,
6//! and checksums are `reddb-file` contracts. This module owns runtime
7//! orchestration: append buffering, fsync policy, checkpointing, and recovery.
8
9pub mod append_coordinator;
10pub mod archiver;
11pub mod checkpoint;
12pub mod checkpointer_task;
13pub mod group_commit;
14pub mod reader;
15pub mod record;
16pub mod recovery;
17pub mod rmgr;
18pub mod transaction;
19pub mod writer;
20
21pub use append_coordinator::WalAppendCoordinator;
22pub use archiver::{
23    archive_change_records, archive_snapshot, load_archived_change_records, load_backup_head,
24    load_snapshot_manifest, load_unified_manifest, load_wal_segment_manifest, publish_backup_head,
25    publish_snapshot_manifest, publish_unified_manifest, publish_unified_manifest_for_prefix,
26    publish_wal_segment_manifest, WalArchiver,
27};
28pub use checkpoint::{CheckpointError, CheckpointMode, CheckpointResult, Checkpointer};
29pub use group_commit::GroupCommit;
30pub use reader::WalReader;
31pub use record::{RecordType, WalRecord};
32pub use recovery::{PointInTimeRecovery, RecoveryResult, RestorePoint};
33pub use transaction::{Transaction, TransactionManager, TxError, TxState};
34pub use writer::WalWriter;
35
36#[cfg(test)]
37mod tests {
38    use super::*;
39    use std::fs;
40    use std::time::{SystemTime, UNIX_EPOCH};
41
42    #[test]
43    fn test_wal_write_read() {
44        let timestamp = SystemTime::now()
45            .duration_since(UNIX_EPOCH)
46            .unwrap()
47            .as_nanos();
48        let dir = std::env::temp_dir().join(format!("reddb_wal_test_{}", timestamp));
49        let _ = fs::create_dir_all(&dir);
50        let path = reddb_file::layout::wal_component_temp_path(
51            &dir,
52            "mod",
53            "write-read",
54            std::process::id(),
55        );
56        if path.exists() {
57            fs::remove_file(&path).unwrap();
58        }
59
60        // Write
61        {
62            let mut writer = WalWriter::open(&path).unwrap();
63
64            let rec1 = WalRecord::Begin { tx_id: 100 };
65            writer.append(&rec1).unwrap();
66
67            let rec2 = WalRecord::PageWrite {
68                tx_id: 100,
69                page_id: 5,
70                data: vec![1, 2, 3, 4],
71            };
72            writer.append(&rec2).unwrap();
73
74            let rec3 = WalRecord::Commit { tx_id: 100 };
75            writer.append(&rec3).unwrap();
76        }
77
78        // Read
79        {
80            let reader = WalReader::open(&path).unwrap();
81            let records: Vec<_> = reader.iter().map(|r| r.unwrap().1).collect();
82
83            assert_eq!(records.len(), 3);
84
85            match &records[0] {
86                WalRecord::Begin { tx_id } => assert_eq!(*tx_id, 100),
87                _ => panic!("Wrong type"),
88            }
89
90            match &records[1] {
91                WalRecord::PageWrite {
92                    tx_id,
93                    page_id,
94                    data,
95                } => {
96                    assert_eq!(*tx_id, 100);
97                    assert_eq!(*page_id, 5);
98                    assert_eq!(*data, vec![1, 2, 3, 4]);
99                }
100                _ => panic!("Wrong type"),
101            }
102
103            match &records[2] {
104                WalRecord::Commit { tx_id } => assert_eq!(*tx_id, 100),
105                _ => panic!("Wrong type"),
106            }
107        }
108
109        // Cleanup
110        if path.exists() {
111            fs::remove_file(path).unwrap();
112        }
113        if dir.exists() {
114            fs::remove_dir(dir).unwrap();
115        }
116    }
117}