fix(heal): detect stale delete-marker metadata (#8102)

This commit is contained in:
cxymds
2026-09-24 11:16:02 +08:00
committed by GitHub
parent df64242f64
commit 0913686b12
+34 -1
View File
@@ -6821,7 +6821,12 @@ pub fn should_heal_object_on_disk(
return (false, false, err.clone());
}
if !meta.equals(latest_meta) {
// `FileInfo::equals` intentionally compares the erasure payload shape and
// modification time, but it does not compare the selected version or the
// delete-marker bit. Heal uses this decision for versioned metadata, so a
// stale historical version that is still marked latest must be treated as
// outdated even when those storage-level fields happen to match.
if !meta.equals(latest_meta) || !heal_metadata_identity_matches(meta, latest_meta) {
debug!(
event = EVENT_SET_DISK_HEAL,
component = LOG_COMPONENT_ECSTORE,
@@ -6844,6 +6849,11 @@ pub fn should_heal_object_on_disk(
(false, false, None)
}
fn heal_metadata_identity_matches(meta: &FileInfo, latest_meta: &FileInfo) -> bool {
meta.deleted == latest_meta.deleted
&& meta.version_id.filter(|version| !version.is_nil()) == latest_meta.version_id.filter(|version| !version.is_nil())
}
/// Probe every drive of the set at once. Each live probe is bounded by the
/// drive `disk_info` timeout, and the admin peer probe budget only covers one
/// such timeout; a sequential walk over several stalled drives after a power
@@ -11288,6 +11298,29 @@ mod tests {
assert_eq!(reason, Some(DiskError::FileCorrupt));
}
#[test]
fn stale_delete_marker_identity_requires_metadata_heal() {
let historical_version = Uuid::new_v4();
let marker_version = Uuid::new_v4();
let mut stale = FileInfo {
version_id: Some(historical_version),
..FileInfo::default()
};
let mut latest = stale.clone();
stale.deleted = false;
latest.deleted = true;
latest.version_id = Some(marker_version);
// The generic equality helper intentionally considers these records
// equal when their erasure shape and mod-time match. Heal must still
// repair the version identity and delete-marker state.
assert!(stale.equals(&latest));
let (should_heal, metadata, reason) = should_heal_object_on_disk(&None, &[], &stale, &latest);
assert!(should_heal);
assert!(metadata);
assert_eq!(reason, Some(DiskError::OutdatedXLMeta));
}
#[test]
fn metadata_io_failures_never_authorize_heal_overwrite() {
let meta = FileInfo::default();