From f8f373a2367185a088143966c66e068f41d61aac Mon Sep 17 00:00:00 2001 From: cxymds Date: Thu, 2 Jul 2026 22:34:47 +0800 Subject: [PATCH] fix: preserve compacted bucket counts (#4183) Co-authored-by: overtrue --- crates/data-usage/src/data_usage.rs | 30 ++++++++++++++++++++++++- crates/scanner/src/data_usage_define.rs | 30 ++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 2 deletions(-) diff --git a/crates/data-usage/src/data_usage.rs b/crates/data-usage/src/data_usage.rs index 1744ddc26..3ea80c9e2 100644 --- a/crates/data-usage/src/data_usage.rs +++ b/crates/data-usage/src/data_usage.rs @@ -828,7 +828,7 @@ impl DataUsageCache { versions_total_count: flat.versions as u64, delete_markers_total_count: flat.delete_markers as u64, objects_total_size: flat.size as u64, - buckets_count: e.children.len() as u64, + buckets_count: u64::try_from(buckets.len()).unwrap_or(u64::MAX), buckets_usage, ..Default::default() } @@ -1373,6 +1373,34 @@ mod tests { assert_eq!(child_entry.objects, 3); } + #[test] + fn test_dui_bucket_count_uses_bucket_list_after_compaction() { + let root_hash = hash_path("root"); + let mut cache = DataUsageCache { + info: DataUsageCacheInfo { + name: "root".to_string(), + ..Default::default() + }, + ..Default::default() + }; + cache.replace_hashed( + &root_hash, + &None, + &DataUsageEntry { + compacted: true, + objects: 3, + ..Default::default() + }, + ); + + let buckets = vec!["bucket-a".to_string(), "bucket-b".to_string()]; + let info = cache.dui("root", &buckets); + + assert_eq!(info.buckets_count, 2); + assert!(info.buckets_usage.is_empty()); + assert_eq!(info.objects_total_count, 3); + } + #[test] fn test_data_usage_entry_merge_preserves_replication_targets() { let mut base = DataUsageEntry { diff --git a/crates/scanner/src/data_usage_define.rs b/crates/scanner/src/data_usage_define.rs index a95e9a620..2e95e83e0 100644 --- a/crates/scanner/src/data_usage_define.rs +++ b/crates/scanner/src/data_usage_define.rs @@ -680,7 +680,7 @@ impl DataUsageCache { versions_total_count: flat.versions as u64, delete_markers_total_count: flat.delete_markers as u64, objects_total_size: flat.size as u64, - buckets_count: e.children.len() as u64, + buckets_count: u64::try_from(buckets.len()).unwrap_or(u64::MAX), buckets_usage, ..Default::default() } @@ -1501,6 +1501,34 @@ mod tests { assert!(cache.cache.contains_key(&missing_hash.key())); } + #[test] + fn test_dui_bucket_count_uses_bucket_list_after_compaction() { + let root_hash = hash_path("root"); + let mut cache = DataUsageCache { + info: DataUsageCacheInfo { + name: "root".to_string(), + ..Default::default() + }, + ..Default::default() + }; + cache.replace_hashed( + &root_hash, + &None, + &DataUsageEntry { + compacted: true, + objects: 3, + ..Default::default() + }, + ); + + let buckets = vec!["bucket-a".to_string(), "bucket-b".to_string()]; + let info = cache.dui("root", &buckets); + + assert_eq!(info.buckets_count, 2); + assert!(info.buckets_usage.is_empty()); + assert_eq!(info.objects_total_count, 3); + } + #[test] fn test_cache_path_type_distinguishes_main_and_backup() { assert_eq!(DataUsageCache::cache_path_type("buckets/.usage-cache.bin"), "main");