From 76f2c804bd89ca8d01485ebeaa186971d10d57d9 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 16 Mar 2026 17:43:28 -0700 Subject: [PATCH] Add walk_ecx_stats for accurate EC volume file counts and size --- .../src/storage/erasure_coding/ec_volume.rs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/seaweed-volume/src/storage/erasure_coding/ec_volume.rs b/seaweed-volume/src/storage/erasure_coding/ec_volume.rs index 82e98a06b..1a752eb21 100644 --- a/seaweed-volume/src/storage/erasure_coding/ec_volume.rs +++ b/seaweed-volume/src/storage/erasure_coding/ec_volume.rs @@ -299,6 +299,40 @@ impl EcVolume { 0 } + /// Walk the .ecx index and return (file_count, file_deleted_count, total_size). + /// total_size sums size.Raw() for all entries (including deleted), matching Go's WalkIndex. + pub fn walk_ecx_stats(&self) -> io::Result<(u64, u64, u64)> { + let ecx_file = match self.ecx_file.as_ref() { + Some(f) => f, + None => return Ok((0, 0, 0)), + }; + + let entry_count = self.ecx_file_size as usize / NEEDLE_MAP_ENTRY_SIZE; + let mut files: u64 = 0; + let mut files_deleted: u64 = 0; + let mut total_size: u64 = 0; + let mut entry_buf = [0u8; NEEDLE_MAP_ENTRY_SIZE]; + + for i in 0..entry_count { + let file_offset = (i * NEEDLE_MAP_ENTRY_SIZE) as u64; + #[cfg(unix)] + { + use std::os::unix::fs::FileExt; + ecx_file.read_exact_at(&mut entry_buf, file_offset)?; + } + let (_key, _offset, size) = idx_entry_from_bytes(&entry_buf); + // Raw size includes the sign bit; match Go's size.Raw() which is uint32 + total_size += size.0.unsigned_abs() as u64; + if size.is_deleted() { + files_deleted += 1; + } else { + files += 1; + } + } + + Ok((files, files_deleted, total_size)) + } + // ---- Deletion journal ---- /// Append a deleted needle ID to the .ecj journal.