actionqueue_storage/wal/
repair.rs1use std::fs::OpenOptions;
7use std::io;
8
9pub fn truncate_to_last_valid(path: &std::path::Path, valid_end_offset: u64) -> io::Result<()> {
19 let file = OpenOptions::new().write(true).open(path)?;
20 file.set_len(valid_end_offset)?;
21 file.sync_all()?;
22 Ok(())
23}
24
25#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
27pub enum RepairPolicy {
28 #[default]
30 Strict,
31 TruncatePartial,
35}
36
37#[cfg(test)]
38mod tests {
39 use std::fs;
40 use std::io::Write;
41 use std::sync::atomic::{AtomicUsize, Ordering};
42
43 use super::*;
44
45 static TEST_COUNTER: AtomicUsize = AtomicUsize::new(0);
46
47 fn temp_path() -> std::path::PathBuf {
48 let dir = std::env::temp_dir();
49 let count = TEST_COUNTER.fetch_add(1, Ordering::SeqCst);
50 let path =
51 dir.join(format!("actionqueue_wal_repair_test_{}_{}.tmp", std::process::id(), count));
52 let _ = fs::remove_file(&path);
53 path
54 }
55
56 #[test]
57 fn truncate_removes_trailing_bytes() {
58 let path = temp_path();
59 {
60 let mut file = fs::File::create(&path).unwrap();
61 file.write_all(b"valid_record_data_here_extra_junk").unwrap();
62 }
63
64 truncate_to_last_valid(&path, 22).unwrap();
65
66 let contents = fs::read(&path).unwrap();
67 assert_eq!(&contents, b"valid_record_data_here");
68
69 let _ = fs::remove_file(&path);
70 }
71
72 #[test]
73 fn truncate_to_zero_empties_file() {
74 let path = temp_path();
75 {
76 let mut file = fs::File::create(&path).unwrap();
77 file.write_all(b"some data").unwrap();
78 }
79
80 truncate_to_last_valid(&path, 0).unwrap();
81
82 let contents = fs::read(&path).unwrap();
83 assert!(contents.is_empty());
84
85 let _ = fs::remove_file(&path);
86 }
87}