// Find all deleted nodes that have no subsequent non-deleted version // (i.e., nodes that are currently dead). // // Also checks: is there a live node under the same key with a different UUID? // If not, the deletion was terminal — the node is gone. use std::collections::HashMap; use std::io::BufReader; use std::fs; use capnp::{message, serialize}; use poc_memory::memory_capnp; use poc_memory::store::Node; fn main() { let path = std::env::args().nth(1) .unwrap_or_else(|| { let dir = poc_memory::store::nodes_path(); dir.to_string_lossy().to_string() }); let file = fs::File::open(&path).unwrap(); let mut reader = BufReader::new(file); // Collect ALL entries, tracking latest version per key let mut latest_by_key: HashMap = HashMap::new(); let mut all_entries = 0u64; while let Ok(msg) = serialize::read_message(&mut reader, message::ReaderOptions::new()) { let log = msg.get_root::().unwrap(); for node_reader in log.get_nodes().unwrap() { all_entries += 1; let node = Node::from_capnp_migrate(node_reader).unwrap(); let dominated = latest_by_key.get(&node.key) .map(|n| node.version >= n.version) .unwrap_or(true); if dominated { latest_by_key.insert(node.key.clone(), node); } } } // Find keys where the latest version is deleted let mut dead: Vec<&Node> = latest_by_key.values() .filter(|n| n.deleted) .collect(); dead.sort_by(|a, b| a.key.cmp(&b.key)); eprintln!("Scanned {} entries, {} unique keys", all_entries, latest_by_key.len()); eprintln!("{} live nodes, {} deleted (terminal tombstones)\n", latest_by_key.len() - dead.len(), dead.len()); for node in &dead { println!("{:<60} v{:<4} {}b prov={}", node.key, node.version, node.content.len(), node.provenance); } }