Skip to content

raft §3.10: complete leadership transfer on a leader-authored message from the target, not on any higher-term echo #1243

Description

@keddie

🤖 This issue was generated by Claude on behalf of @keddie.

Found by the review of PR #1240 (#1231 fix). That PR narrowed the false-success bug (transfer now completes only when the higher-term step-down's sender from == target), a strict improvement — but the residual remains because "sender of the first higher-term message" identifies neither the winner nor even a campaigner.

Severity: 🟠 contract (false success/failure — only reachable on degraded networks; healthy clusters are protected by §4.2.3 stickiness).

Residual false-SUCCESS (echo route). Transfer A→B in flight; unrelated D wins term T+1 on a partition majority; B adopts T+1 from D's heartbeat; B's next AppendEntriesResponse(term=T+1) to A → stepDown(from=B)from == target → SUCCESS reported while the real leader is D. Also: success can complete at the target's own RequestVote receipt (the isTransferCandidate step-down path) before the vote is even granted — a not-caught-up target that campaigns mid-transfer reports success then loses on LogNotUpToDate.

Residual false-FAILURE (non-target-first). Target B genuinely wins with {B,C} (A never saw B's RequestVote, slow A↔B link); C's AppendEntriesResponse(T+1) outraces B's first leader message → A steps down from=C → transfer reports FAILURE while B is leader.

Root cause. A higher-term message's sender is not the win signal. The §3.10-faithful signal is a leader-authored message from the target — an AppendEntries/InstallSnapshot with leaderId == target at the new term (proof the target is actually leader).

Fix. Keep the transfer pending across the old leader's step-down; complete SUCCESS only when _leader resolves to the target at the higher term (a leader-authored message with leaderId == target), else fail on the auto-timeout. This is the deferred-completion design PR #1240 explicitly deferred as "invasive."

Interim (done in #1240 via KDoc softening): the transferLeadership / onLeadershipRelinquished KDocs are softened to "best-effort confirmation" rather than a guarantee. This issue tracks the real gating fix.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions