diff --git a/crates/ecstore/src/set_disk/ops/heal.rs b/crates/ecstore/src/set_disk/ops/heal.rs index 1735bd595..67413a219 100644 --- a/crates/ecstore/src/set_disk/ops/heal.rs +++ b/crates/ecstore/src/set_disk/ops/heal.rs @@ -72,7 +72,9 @@ fn should_fail_heal_rename(bucket: &str, object: &str, disk_index: usize) -> boo .expect("heal rename failure registry should not poison"); if let Some(position) = failures .iter() - .position(|entry| entry == &(bucket.to_string(), object.to_string(), disk_index)) + .position(|(registered_bucket, registered_object, registered_index)| { + registered_bucket == bucket && registered_object == object && *registered_index == disk_index + }) { failures.swap_remove(position); true diff --git a/crates/heal/src/heal/manager.rs b/crates/heal/src/heal/manager.rs index e725acf1b..024dd1d52 100644 --- a/crates/heal/src/heal/manager.rs +++ b/crates/heal/src/heal/manager.rs @@ -3767,6 +3767,23 @@ mod tests { assert!(retry_error.contains("Lock acquisition timeout")); } + #[test] + fn test_retry_request_for_incomplete_heal_rename() { + let storage: Arc = Arc::new(MockStorage); + let task = HealTask::from_request(HealRequest::object("bucket".to_string(), "object".to_string(), None), storage); + let result = Err(Error::TaskExecutionFailed { + message: "Failed to heal object bucket/object: heal rename incomplete: 1 of 2 targets committed".to_string(), + }); + + let (retry_request, retry_delay, retry_error) = + retry_request_for_result(&task, &result).expect("incomplete target rename should be retryable"); + + assert_eq!(retry_request.id, task.id); + assert_eq!(retry_request.retry_attempts, 1); + assert!(retry_delay > Duration::ZERO); + assert!(retry_error.contains("heal rename incomplete")); + } + #[test] fn test_retry_request_for_typed_read_quorum_error() { let storage: Arc = Arc::new(MockStorage);