diff --git a/crates/ecstore/src/bucket/replication/replication_resyncer.rs b/crates/ecstore/src/bucket/replication/replication_resyncer.rs index 37b9ba469..91308688f 100644 --- a/crates/ecstore/src/bucket/replication/replication_resyncer.rs +++ b/crates/ecstore/src/bucket/replication/replication_resyncer.rs @@ -929,7 +929,7 @@ impl ReplicationResyncer { } fn heal_should_use_check_replicate_delete(oi: &ObjectInfo) -> bool { - oi.delete_marker || (!oi.replication_status.is_empty() && oi.replication_status != ReplicationStatusType::Failed) + oi.delete_marker || !oi.version_purge_status.is_empty() } pub async fn get_heal_replicate_object_info(oi: &ObjectInfo, rcfg: &ReplicationConfig) -> ReplicateObjectInfo { @@ -943,10 +943,6 @@ pub async fn get_heal_replicate_object_info(oi: &ObjectInfo, rcfg: &ReplicationC oi.replication_status_internal = Some(format!("{}={};", rc.role, oi.replication_status.as_str())); } - if !oi.replication_status.is_empty() { - oi.replication_status_internal = Some(format!("{}={};", rc.role, oi.replication_status.as_str())); - } - let keys_to_update: Vec<_> = user_defined .iter() .filter(|(k, _)| has_internal_suffix(k, SUFFIX_REPLICATION_RESET)) @@ -3742,7 +3738,7 @@ mod tests { } #[test] - fn test_heal_should_use_check_replicate_delete_pending_uses_delete_path() { + fn test_heal_should_use_check_replicate_delete_pending_non_delete_marker_uses_must_replicate_path() { let oi = ObjectInfo { bucket: "b".to_string(), name: "obj".to_string(), @@ -3751,8 +3747,8 @@ mod tests { ..Default::default() }; assert!( - heal_should_use_check_replicate_delete(&oi), - "Pending (non-Failed) status with non-empty replication uses check_replicate_delete path" + !heal_should_use_check_replicate_delete(&oi), + "Pending non-delete-marker object must use must_replicate path to evaluate current target set" ); } @@ -3771,6 +3767,36 @@ mod tests { ); } + #[test] + fn test_heal_should_use_check_replicate_delete_version_purge_status_uses_delete_path() { + let oi = ObjectInfo { + bucket: "b".to_string(), + name: "obj".to_string(), + delete_marker: false, + version_purge_status: VersionPurgeStatusType::Pending, + ..Default::default() + }; + assert!( + heal_should_use_check_replicate_delete(&oi), + "Version purge entries must use check_replicate_delete path" + ); + } + + #[test] + fn test_heal_should_use_check_replicate_delete_completed_non_delete_marker_uses_must_replicate_path() { + let oi = ObjectInfo { + bucket: "b".to_string(), + name: "obj".to_string(), + delete_marker: false, + replication_status: ReplicationStatusType::Completed, + ..Default::default() + }; + assert!( + !heal_should_use_check_replicate_delete(&oi), + "Completed non-delete-marker object must use must_replicate path so new targets can be evaluated" + ); + } + #[test] fn test_delete_replication_object_opts_marks_replica_deletes() { let dobj = ObjectToDelete {