mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-22 12:26:37 +00:00
fix(scanner): account versioned delete markers in usage (#3904)
Co-authored-by: Henry Guo <marshawcoco@users.noreply.github.com>
This commit is contained in:
@@ -172,6 +172,7 @@ impl SizeSummary {
|
||||
pub fn actions_accounting(&mut self, oi: &ObjectInfo, size: i64, actual_size: i64) {
|
||||
if oi.delete_marker {
|
||||
self.delete_markers += 1;
|
||||
return;
|
||||
}
|
||||
|
||||
if oi.version_id.is_some_and(|v| !v.is_nil()) && size == actual_size {
|
||||
@@ -180,7 +181,7 @@ impl SizeSummary {
|
||||
|
||||
self.total_size += if size > 0 { size as usize } else { 0 };
|
||||
|
||||
if oi.delete_marker || oi.transitioned_object.free_version {
|
||||
if oi.transitioned_object.free_version {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1065,6 +1066,22 @@ mod tests {
|
||||
assert_eq!(summary1.versions, 15);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn size_summary_counts_delete_markers_separately_from_versions() {
|
||||
let mut summary = SizeSummary::new();
|
||||
let marker = ObjectInfo {
|
||||
delete_marker: true,
|
||||
version_id: Some(uuid::Uuid::new_v4()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
summary.actions_accounting(&marker, 0, 0);
|
||||
|
||||
assert_eq!(summary.delete_markers, 1);
|
||||
assert_eq!(summary.versions, 0);
|
||||
assert_eq!(summary.total_size, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_data_usage_entry_merge_sums_failed_objects() {
|
||||
let mut left = DataUsageEntry {
|
||||
|
||||
@@ -697,10 +697,10 @@ impl ScannerItem {
|
||||
component = LOG_COMPONENT_SCANNER,
|
||||
subsystem = LOG_SUBSYSTEM_LIFECYCLE,
|
||||
bucket = %self.bucket,
|
||||
state = "versioning_lookup_failed",
|
||||
"Scanner lifecycle action skipped"
|
||||
state = "versioning_lookup_failed_defaulting",
|
||||
"Scanner lifecycle action falling back to default bucket versioning"
|
||||
);
|
||||
return;
|
||||
Default::default()
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1543,8 +1543,10 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::scanner_folder::ScannerItem;
|
||||
use crate::{DiskOption, Endpoint, new_disk, path2_bucket_object_with_base_path};
|
||||
use rustfs_filemeta::FileInfo;
|
||||
use serial_test::serial;
|
||||
use temp_env::with_var;
|
||||
use time::OffsetDateTime;
|
||||
use uuid::Uuid;
|
||||
|
||||
fn bucket_info(name: &str) -> BucketInfo {
|
||||
@@ -1770,6 +1772,76 @@ mod tests {
|
||||
let _ = tokio::fs::remove_dir_all(&temp_dir).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_size_counts_delete_markers_separately_from_versions() {
|
||||
let temp_dir = std::env::temp_dir().join(format!("rustfs-scanner-versioned-usage-{}", Uuid::new_v4()));
|
||||
let bucket = "bucket";
|
||||
let object = "object";
|
||||
let object_dir = temp_dir.join(bucket).join(object);
|
||||
let metadata_path = object_dir.join(STORAGE_FORMAT_FILE);
|
||||
|
||||
tokio::fs::create_dir_all(&object_dir)
|
||||
.await
|
||||
.expect("failed to create object directory");
|
||||
|
||||
let mut meta = FileMeta::new();
|
||||
for (size, timestamp) in [(10, 10), (20, 20)] {
|
||||
let mut fi = FileInfo::new(object, 1, 1);
|
||||
fi.version_id = Some(Uuid::new_v4());
|
||||
fi.mod_time = Some(OffsetDateTime::from_unix_timestamp(timestamp).expect("timestamp should be valid"));
|
||||
fi.size = size;
|
||||
meta.add_version(fi).expect("object version should be added");
|
||||
}
|
||||
|
||||
let mut delete_marker = FileInfo::new(object, 1, 1);
|
||||
delete_marker.version_id = Some(Uuid::new_v4());
|
||||
delete_marker.mod_time = Some(OffsetDateTime::from_unix_timestamp(30).expect("timestamp should be valid"));
|
||||
delete_marker.deleted = true;
|
||||
meta.add_version(delete_marker).expect("delete marker should be added");
|
||||
|
||||
tokio::fs::write(&metadata_path, meta.marshal_msg().expect("metadata should marshal"))
|
||||
.await
|
||||
.expect("failed to write metadata");
|
||||
|
||||
let endpoint = Endpoint::try_from(temp_dir.to_string_lossy().as_ref()).expect("failed to create endpoint");
|
||||
let disk = new_disk(
|
||||
&endpoint,
|
||||
&DiskOption {
|
||||
cleanup: false,
|
||||
health_check: false,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.expect("failed to open local disk");
|
||||
|
||||
let relative_path = metadata_path.to_string_lossy().to_string();
|
||||
let (_, scanner_path) = path2_bucket_object_with_base_path(temp_dir.to_string_lossy().as_ref(), relative_path.as_str());
|
||||
let file_type = tokio::fs::metadata(&metadata_path)
|
||||
.await
|
||||
.expect("failed to stat metadata")
|
||||
.file_type();
|
||||
let item = ScannerItem {
|
||||
path: scanner_path,
|
||||
bucket: bucket.to_string(),
|
||||
prefix: object.to_string(),
|
||||
object_name: STORAGE_FORMAT_FILE.to_string(),
|
||||
file_type,
|
||||
lifecycle: None,
|
||||
replication: None,
|
||||
heal_enabled: false,
|
||||
heal_bitrot: false,
|
||||
debug: false,
|
||||
};
|
||||
|
||||
let summary = disk.get_size(item).await.expect("scanner should read versioned metadata");
|
||||
|
||||
assert_eq!(summary.versions, 2);
|
||||
assert_eq!(summary.delete_markers, 1);
|
||||
assert_eq!(summary.total_size, 30);
|
||||
|
||||
let _ = tokio::fs::remove_dir_all(&temp_dir).await;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cache_root_entry_info_flattens_bucket_children() {
|
||||
let mut cache = DataUsageCache {
|
||||
|
||||
Reference in New Issue
Block a user