#![warn(missing_docs)]
pub mod checkpoint;
pub mod error;
#[doc(hidden)]
pub mod formats;
pub mod publish;
pub mod recordlog;
pub mod recover;
pub mod storage;
pub mod walog;
pub use error::{PersistenceError, PersistenceResult};
pub use storage::{Directory, FsDirectory, MemoryDirectory};
#[cfg(test)]
mod tests {
use super::*;
use crate::checkpoint::CheckpointFile;
use crate::recover::{CheckpointState, RecoveryManager};
use crate::walog::{WalEntry, WalWriter};
use std::collections::HashSet;
use std::sync::Arc;
#[test]
fn wal_recovery_roundtrip_in_memory() {
let dir: Arc<dyn Directory> = Arc::new(MemoryDirectory::new());
let mut w = WalWriter::<WalEntry>::new(dir.clone());
let id1 = w
.append(&WalEntry::AddSegment {
segment_id: 10,
doc_count: 3,
})
.unwrap();
assert_eq!(id1, 1);
let id2 = w
.append(&WalEntry::DeleteDocuments {
deletes: vec![(10, 2)],
})
.unwrap();
assert_eq!(id2, 2);
w.flush().unwrap();
let mgr = RecoveryManager::new(dir);
let state = mgr.recover(None).unwrap();
assert_eq!(state.last_entry_id, 2);
assert_eq!(state.segments.len(), 1);
assert_eq!(state.segments[0].segment_id, 10);
assert_eq!(state.segments[0].doc_count, 3);
let dels: HashSet<u32> = state.segments[0].deleted_docs.iter().copied().collect();
assert!(dels.contains(&2));
assert!(!dels.contains(&1));
}
#[test]
fn checkpoint_then_wal_suffix_applies() {
let tmp = tempfile::tempdir().unwrap();
let dir: Arc<dyn Directory> = Arc::new(FsDirectory::new(tmp.path()).unwrap());
let mut w = WalWriter::<WalEntry>::new(dir.clone());
w.append(&WalEntry::AddSegment {
segment_id: 1,
doc_count: 5,
})
.unwrap();
w.append(&WalEntry::DeleteDocuments {
deletes: vec![(1, 4)],
})
.unwrap();
w.flush().unwrap();
let mgr = RecoveryManager::new(dir.clone());
let prefix = mgr.recover(None).unwrap();
let ckpt_state: CheckpointState = RecoveryManager::to_checkpoint_state(&prefix);
let ckpt_last = prefix.last_entry_id;
let ckpt_path = "checkpoints/c1.bin";
let ckpt = CheckpointFile::new(dir.clone());
ckpt.write_postcard(ckpt_path, ckpt_last, &ckpt_state)
.unwrap();
w.append(&WalEntry::AddSegment {
segment_id: 2,
doc_count: 7,
})
.unwrap();
w.flush().unwrap();
let after = mgr.recover(Some(ckpt_path)).unwrap();
assert!(after.segments.iter().any(|s| s.segment_id == 1));
assert!(after.segments.iter().any(|s| s.segment_id == 2));
let seg1 = after.segments.iter().find(|s| s.segment_id == 1).unwrap();
assert!(seg1.deleted_docs.contains(&4));
}
}