fix(swim): fast-restart rejoin — persist incarnation, echo refutations, ping liveness - #243
fix(swim): fast-restart rejoin — persist incarnation, echo refutations, ping liveness#243EnRaiha wants to merge 2 commits into
Conversation
…s, ping liveness Fixes the SWIM fast-restart rejoin stick (epic NodeDB-Lab#165): a node that crashes while peers hold Dead(A, N) restarts at incarnation 0, announces Alive(0) exactly once, and can diverge from the cluster forever when the probabilistic refutation round-trip fails. - F4 (primary): persist the local incarnation in the catalog (KEY_SWIM_INCARNATION, u64 LE, same pattern as cluster_epoch). Bootstrap resumes at persisted + 1, so the first announcement already dominates any lingering Dead(A, N) rumour. Every self-refutation bump is persisted (fire-and-forget, never stalls the probe loop). New IncarnationStore trait: catalog-backed in production, in-memory in tests. Wired through SwimSubsystemConfig, spawn_with_subscribers, and register_default_subsystems (load + bump on start). - F1: ingest_piggyback returns refutations — whenever an update is refuted, the stored (newer) view is echoed deterministically on the reply piggyback (Ack + forwarded PingReq), instead of relying on probabilistic gossip fanout. - F2: a ping IS liveness evidence (SWIM paper §3) — the sender's Alive claim is applied before acking, clearing Dead/Suspect views; self-advertisement on the ack path is rate-limited to 500ms so a heavily-pinged node cannot flood its dissemination queue. - F3: a fresh Alive apply cancels the pending suspicion timer, so a stale expiry can no longer promote a just-seen-live node to Dead. Tests: 6 new regression tests pinning each fix + the combined deterministic recovery loop (A holds Dead(B,5) → echo → self-refute → converge Alive), 129/129 swim, 1015 cluster lib, clippy -D 0, maya-gate L1 clean. Refactor note (deferred): IncarnationTracker consolidation (P2-GLM53-REVIEW-RESOLUTION.md Improvement 1) — incarnation state is still split between detector/local_incarnation and the store; a single-owner tracker with atomic persist would remove the fire-and-forget save. MemberState::Left has no production sender yet; when graceful-leave lands, restart must resume above TerminalLeft or clean-shutdown restarts will stick forever.
Drill/fix-plan/GLM-resolution docs (commit dd0c316) written pre-fix are now stale. Add resolution banners pointing to the implemented fixes: SWIM 8886846 (PR NodeDB-Lab#243), wire window e60a853 (PR NodeDB-Lab#244), epoch fence 4f92959 (PR NodeDB-Lab#245), lease GC 16d9916 (PR NodeDB-Lab#246). Full verification + refactor code in P2-REPORT.md.
load_swim_incarnation errors on corrupted metadata (len != 8) instead of silent Ok(None); register_default_subsystems propagates read errors instead of unwrap_or(None) — bootstrap no longer hides catalog IO/corruption problems at startup.
/NodeDB-Lab#246 - catalog: load_swim_incarnation returns an ERROR on corrupted metadata (len != 8) instead of silently Ok(None); register_default_subsystems propagates the read error instead of unwrap_or(None) — bootstrap no longer hides catalog IO/corruption problems (PR NodeDB-Lab#243 critical items). - server: handle_stream drops StalePeerEpoch frames explicitly with a warn! log instead of ending the stream via generic ? — fenced frames are not transport failures; connection stays open for exempt traffic (PR NodeDB-Lab#245 critical item). - lease_gc: proposal failures logged at warn (operators can notice GC not happening); drain_propose doc comment translated to English (PR NodeDB-Lab#246 review items).
|
Addressed the two critical review items in 1876085:
Thanks for the review! |
|
The diagnosis was right, and the fix is actually stronger than the description claims. F1 and F2 together are the part I would highlight: A pings with its stale Alive, the peer refutes and echoes its stored Dead record on the reply, A self-refutes above it, converged. That is a deterministic recovery loop and it works without persistence at all — F4 just makes the first announcement win outright. I checked whether F2 lets a stale claim bypass incarnation ordering and it does not: the sender's claim goes through Landed in 47f890d, with you as co-author. Four corrections went in on top, as 40ea609 and one hunk in the first commit.
Items 3 and 4 both surface immediately in the second test stage ( |
Closes the SWIM fast-restart rejoin stick (epic #165, Medium #9): a node that crashes while peers hold Dead(A, N) restarts at incarnation 0, announces Alive(0) exactly once, and can diverge forever when the probabilistic refutation round-trip fails.
6 regression tests incl. deterministic recovery loop (A holds Dead(B,5) → echo → self-refute → converge Alive). Verification: swim 129/129, cluster lib 1023, clippy -D warnings 0, maya-gate L1 clean.
Closes #165 item: Medium — SWIM fast-restart rejoin.