fix(replication): abandon purges to targets the bucket no longer names

A permanent version delete whose replication keeps failing stays in
xl.meta as a PENDING purge, hidden from listings, until every target
confirms it. Once the operator removes the replication configuration or
the rule naming that target nothing ever confirms it: the heal path
derived its delete decision from the configuration (the decision string
is not persisted) and skipped the version forever, so DeleteBucket
answered BucketNotEmpty for a residue the client could neither list nor
remove (rustfs/backlog#2340).

Owe a version purge to the targets its purge state names, let the heal
path through without a configuration, and have the delete worker settle
a target the configuration no longer names as abandoned: the purge is
reported complete locally through the normal writeback, the replica on
the former target is left alone, and the event
replication_purge_abandoned plus a counter are the record.
This commit is contained in:
唐小鸭
2026-09-07 16:35:58 +08:00
parent ee73203791
commit 29f47fda75
6 changed files with 325 additions and 11 deletions
@@ -1036,12 +1036,19 @@ impl ScannerItem {
return;
}
let Some(replication) = self.replication.clone() else {
return;
let replication = match self.replication.clone() {
Some(replication) => (*replication).clone(),
// No active rules or targets, but a purge the bucket still owes
// must reach the heal path: the delete worker settles it against
// the current configuration (abandoned when the target is gone,
// rustfs/backlog#2340) so the hidden version stops blocking
// DeleteBucket.
None if !oi.version_purge_status.is_empty() => ReplicationConfig::new(None, None),
None => return,
};
let done_replication = Metrics::time(Metric::CheckReplication);
let replication_result = queue_replication_heal(&oi.bucket, oi.clone(), (*replication).clone(), 0).await;
let replication_result = queue_replication_heal(&oi.bucket, oi.clone(), replication, 0).await;
done_replication();
let roi = replication_result.object_info;
record_scanner_replication_admission(global_metrics(), &roi, replication_result.admission);