mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-26 08:18:18 +00:00
fix(ecstore): split rename_data signature from heal-convergence decision (#4926)
CompleteMultipartUpload enqueued a normal-priority heal whenever `rename_data` returned a `Some(versions)` signature. But the per-disk signature is produced for every object with <=10 versions, and a healthy quorum reduces to `Some` as well, so the `Option<Vec<u8>>` return value conflated two distinct facts — "a version signature exists" and "the committed replicas need heal". The result: nearly every healthy MPU completion self-enqueued a heal, while >10-version objects (signature `None`) did not — an algorithmic heal amplification on the healthy path (rustfs/backlog#1321). Replace the overloaded `Option<Vec<u8>>` second element of `SetDisks::rename_data` with an explicit `RenameConvergence` classification computed after the write-quorum gate: - AllSuccessIdentical — every attempted disk committed with an identical, known signature (no heal). - PartialCommit — write quorum met but a disk failed/offline; a committed replica is missing or stale (heal). - SignatureDivergent — all committed but signatures diverge, or mix signed (<=10-version) with unsigned (>10-version) disks (heal). - Unknown — all committed, no signature produced (>10 versions); latent divergence is left to the scanner backstop, not self-enqueued. `RenameConvergence::needs_heal()` is the single decision point. The version signature is now only comparison material; it no longer doubles as a heal flag. The old `select_rename_data_versions` / `reduce_common_versions` / `rename_data_versions_key` machinery that carried the conflation is removed. The heal submission in `complete_multipart_upload` moves off the ACK critical path into a detached task: it runs after the object lock is dropped and after the durable `rename_data` commit, survives cancellation of the completion future, and coalesces through the existing bounded / deduplicated / observable heal-channel admission (one submit per degraded completion, at most). A completion cancelled in the narrow window between the durable commit and reaching the enqueue is scanner-backstopped, as is the Unknown (>10-version) case. The PUT path (`object.rs`) binds the second element as `_` and is unchanged. The change is orthogonal to and composes with the #1312 commit fence on the same `rename_data` path (epoch rejection is a commit-gate failure surfaced through `Result::Err`, convergence is a post-commit signal); documented in docs/architecture/unified-object-generation.md. Tests: `classify_rename_convergence` white-box cases cover the full acceptance matrix (healthy 4/4 and 8/8, 3-same-1-divergent, failed/offline disk, no-common-quorum split, >10-version all-success and with-failure, mixed signed/unsigned) and fail on revert to the old "signature exists => heal" semantics. The decision function is tested directly rather than through the process-global heal channel, whose receiver is owned exclusively by the blackbox serial test (init_heal_channel is once per binary). Refs: https://github.com/rustfs/backlog/issues/1321
This commit is contained in:
@@ -97,6 +97,27 @@ orphan an acceptable residue accounted for by GC metrics — the white-box
|
||||
acceptance "no background disk write after release" must be rewritten
|
||||
accordingly.
|
||||
|
||||
### Post-commit convergence is orthogonal to the fence (#1321)
|
||||
|
||||
The same `SetDisks::rename_data` path already returns a post-commit
|
||||
convergence classification (`RenameConvergence`, rustfs/backlog#1321) that
|
||||
tells the caller whether the *committed* replicas need heal to converge —
|
||||
`AllSuccessIdentical` (no heal), `PartialCommit` (a replica failed/offline),
|
||||
`SignatureDivergent` (committed replicas' version signatures differ), or
|
||||
`Unknown` (no signature was produced, e.g. >10 versions — scanner-backstopped).
|
||||
This replaced an earlier `Option<Vec<u8>>` heuristic under which any
|
||||
version signature looked like "needs heal", so every healthy multipart
|
||||
completion self-enqueued.
|
||||
|
||||
Convergence is a *post-commit* signal (the write landed; do the replicas need
|
||||
reconciliation), whereas the #1312 fence is a *commit* gate (a stale epoch is
|
||||
rejected before the write lands, surfaced through the existing `Result::Err`
|
||||
channel). They compose on the one `rename_data` path rather than competing:
|
||||
the fence decides whether a convergence is produced at all, and
|
||||
`RenameConvergence` classifies it once produced. A future fence-aware
|
||||
convergence variant, if ever needed, is an additive change to that enum and
|
||||
does not disturb the epoch comparison at the disk-write points above.
|
||||
|
||||
## Transport and security contract
|
||||
|
||||
Generation and all derived tokens (lease, reservation) cross node boundaries in
|
||||
|
||||
Reference in New Issue
Block a user