heal: reject cancelled object repair receipts

Do not record positive storage repair receipts once an object heal task has been cancelled, even if the receipt still matches the requested owner and object identity.

Co-Authored-By: heihutu <heihutu@gmail.com>

Co-Authored-By: zhi22915 <qiuzgang@gmail.com>
This commit is contained in:
houseme
2026-09-08 14:11:27 +08:00
parent 817ad0a682
commit 8d339da706
2 changed files with 38 additions and 1 deletions
+1 -1
View File
@@ -609,7 +609,7 @@ impl HealTask {
expected: HealObjectIdentity,
receipt: Option<HealObjectReceipt>,
) -> bool {
if self.options.dry_run {
if self.options.dry_run || self.cancel_token.is_cancelled() {
return false;
}
let Some(receipt) = receipt else {
+37
View File
@@ -1328,6 +1328,43 @@ async fn object_heal_records_matching_positive_storage_receipt() {
assert_eq!(object.disposition, HealObjectDisposition::Repaired);
}
#[tokio::test]
async fn cancelled_object_heal_rejects_matching_positive_storage_receipt() {
let incarnation = Uuid::new_v4();
let storage = Arc::new(MockStorage::default());
let task = HealTask::from_request(
HealRequest::object("bucket-a".to_string(), "object-a".to_string(), Some("version-a".to_string())),
storage,
);
task.cancel().await.expect("task cancellation should succeed");
let expected = HealObjectIdentity {
kind: HealObjectKind::Object,
bucket: "bucket-a".to_string(),
object: "object-a".to_string(),
version_id: Some("version-a".to_string()),
bucket_incarnation_id: Some(incarnation),
pool_index: None,
set_index: None,
};
let accepted = task
.record_verified_storage_receipt(
expected,
Some(object_receipt(
"object-a",
Some("version-a"),
HealObjectDisposition::Repaired,
incarnation,
)),
)
.await;
let outcome = task.get_outcome().await;
assert!(!accepted);
assert_eq!(outcome.counters.healed, 0);
assert!(outcome.objects.is_empty());
}
#[tokio::test]
async fn object_heal_latches_expected_incarnation_before_repair() {
let original_incarnation = Uuid::new_v4();