mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-28 16:07:05 +00:00
feat(heal,scanner): best-effort repaired notices from the MRF consumer (#6283)
The scanner's pending-heal ledger and the MRF journal tracked the same damaged objects with no cross-talk: once the consumer landed an intent with the heal manager, the ledger's retry entry for that target kept re-submitting a heal the manager already owned (backlog#1894 axis B). Fan the acceptance out: both dispatch sites in the MRF queue (the live consumer and the startup replay) record a compact MrfRepairedEvent (bucket, object, version bytes) in a bounded process-wide ring owned by rustfs-common. The scanner drains its own bucket's notices at the top of retry_pending_scanner_heals and clears the matching Object-kind ledger entries in one batched retain + sync (a mass-recovery first sweep must not turn into thousands of full-table ledger clones on the scan task), with nil notice UUIDs mapping to None per the repo-wide defensive-UUID invariant so unversioned entries match unversioned notices only. Notices are best-effort by design — a lost or capped-out notice leaves the entry to expire through its own attempts/age limits, because the ledger is a retry oracle, not a source of truth; other buckets' notices stay queued for their own scanners. Neither persistent format changes; old nodes that keep double-booking remain harmless. Co-authored-by: heihutu <heihutu@gmail.com>
This commit is contained in:
@@ -127,6 +127,20 @@ async fn decode_failure_intent_maps_to_urgent_mrf_heal_request() {
|
||||
"MRF intent must reach the manager queue as an Urgent request (snapshot: {:?})",
|
||||
manager.operations_snapshot().await
|
||||
);
|
||||
|
||||
// Axis B (backlog#1894): the accepted dispatch must also fan out a
|
||||
// repaired notice for the intent's bucket so the scanner ledger can drop
|
||||
// its retry entry for the same target. Polled: the queue observation
|
||||
// above can land between the manager push and the consumer's notice.
|
||||
let noticed = wait_until(Duration::from_secs(10), || async {
|
||||
!mrf_channel::take_mrf_repaired_events_for("mrf-bucket").is_empty()
|
||||
})
|
||||
.await;
|
||||
assert!(noticed, "accepted intent must fan out a repaired notice");
|
||||
assert!(
|
||||
mrf_channel::take_mrf_repaired_events_for("mrf-bucket").is_empty(),
|
||||
"notice take is destructive"
|
||||
);
|
||||
}
|
||||
|
||||
/// A journal left behind by a previous process must be replayed into the
|
||||
|
||||
Reference in New Issue
Block a user