From 5947c420d7f64dec07d1c1cbb08a174c7fd866dd Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Fri, 13 Mar 2026 16:11:50 -0700 Subject: [PATCH] Compact tombstone file when stale entries exceed live set size Fixes #6 Signed-off-by: Cong Wang --- src/branch.rs | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/src/branch.rs b/src/branch.rs index 5ccfb52..fc155d4 100644 --- a/src/branch.rs +++ b/src/branch.rs @@ -30,6 +30,9 @@ pub struct Branch { pub files_dir: PathBuf, pub tombstones_file: PathBuf, tombstones: RwLock>, + /// Number of stale (removed-from-memory-but-still-on-disk) tombstone entries. + /// When this exceeds the live set size, we compact the file. + tombstone_stale: AtomicU64, /// How many children have been committed (merged) into this branch. pub commit_count: AtomicU64, /// Parent's commit_count at the time this branch was forked. @@ -60,6 +63,7 @@ impl Branch { files_dir, tombstones_file, tombstones: RwLock::new(tombstones), + tombstone_stale: AtomicU64::new(0), commit_count: AtomicU64::new(0), parent_version_at_fork, }) @@ -92,7 +96,26 @@ impl Branch { } pub fn remove_tombstone(&self, path: &str) { - self.tombstones.write().remove(path); + let mut tombstones = self.tombstones.write(); + if tombstones.remove(path) { + let stale = self.tombstone_stale.fetch_add(1, Ordering::Relaxed) + 1; + // Compact when stale entries exceed live set size (at least 16 to avoid + // thrashing on tiny sets). + if stale >= tombstones.len().max(16) as u64 + && self.rewrite_tombstones(&tombstones).is_ok() + { + self.tombstone_stale.store(0, Ordering::Relaxed); + } + } + } + + /// Rewrite the tombstones file from the in-memory set (caller holds write lock). + fn rewrite_tombstones(&self, tombstones: &HashSet) -> Result<()> { + let mut file = File::create(&self.tombstones_file)?; + for t in tombstones { + writeln!(file, "{}", t)?; + } + Ok(()) } pub fn get_tombstones(&self) -> HashSet { @@ -103,11 +126,8 @@ impl Branch { pub fn set_tombstones(&self, new_tombstones: HashSet) -> Result<()> { let mut tombstones = self.tombstones.write(); *tombstones = new_tombstones; - // Rewrite the tombstones file - let mut file = File::create(&self.tombstones_file)?; - for t in tombstones.iter() { - writeln!(file, "{}", t)?; - } + self.rewrite_tombstones(&tombstones)?; + self.tombstone_stale.store(0, Ordering::Relaxed); Ok(()) }