Skip to content

Akka.Cluster: a Down node must never be leader, so it can't remove itself and linger outside the cluster (forward-port of #8650) - #8652

Merged
Aaronontheweb merged 2 commits into
akkadotnet:devfrom
Aaronontheweb:fix/down-member-never-leader-dev
Sep 28, 2026
Merged

Aaronontheweb merged 2 commits into
akkadotnet:devfrom
Aaronontheweb:fix/down-member-never-leader-dev

Conversation

@Aaronontheweb

Copy link
Copy Markdown
Member

close #8651

Forward-port of #8650 to dev. Both commits were cherry-picked from the v1.5 branch and applied cleanly.

Problem

MembershipState.LeaderOf is missing parentheses in the branch that runs when some members are unreachable:

.Where(m => m.Status != MemberStatus.Down && reachability.IsReachable(m.UniqueAddress) || m.UniqueAddress == SelfUniqueAddress)

&& binds tighter than ||, so a node that is Down in its own view still counts as a leader candidate. When every member is Down, for example after SBR's down-all-when-unstable, the downed node becomes leader of its own view and removes itself as an unreachable member in LeaderActionsOnConvergence. ShutdownSelfWhenDown only acts on Down, and self now reads as Removed, so the node never shuts down and keeps running outside the cluster. See #8651 for the full walkthrough.

Fix

.Where(m => m.Status != MemberStatus.Down && (reachability.IsReachable(m.UniqueAddress) || m.UniqueAddress == SelfUniqueAddress))

A Down node is never leader, so it stays in Members as Down and the existing ShutdownSelfWhenDown path shuts it down.

Tests

  • New GossipSpec.A_gossip_must_not_have_Down_self_as_leader_when_some_members_are_unreachable. It fails on current dev without the fix: 1 failed, 35 passed in GossipSpec.
  • With the fix, all 429 Akka.Cluster.Tests pass on net10.0.

Notes

  • No public API or wire-format change.
  • Not added to BREAKING_CHANGES_V1.6.md: this restores the intended behavior, since the all-reachable branch already excluded Down members.

…some members are unreachable

MembershipState.LeaderOf lets a Down self count as a leader candidate
whenever the cluster has unreachable members: `&&` binds tighter than
`||`, so the self check skips the Down filter.

Once every member is Down (e.g. SBR down-all-when-unstable), the downed
node becomes leader of its own view, removes itself as an unreachable
member in LeaderActionsOnConvergence, and never shuts down. It then
fails to serialize every outgoing gossip with "Unknown address ... in
cluster message", reporting its own address.

The existing Down-leader test only covers the all-reachable branch.
The new test covers the unreachable branch, and also checks that a
self that is not Down still counts as a leader candidate when other
members see it as unreachable.

(cherry picked from commit 18bea7b)
…e unreachable

Add the missing parentheses in MembershipState.LeaderOf so the Down
filter applies to self too:

    m.Status != Down && (reachable(m) || m == self)

Before, a Down self stayed a leader candidate. After a down-all
decision it could become leader of its own view, remove itself in
LeaderActionsOnConvergence, and then never shut down, because
ShutdownSelfWhenDown only acts on Down and self now reads as Removed.

(cherry picked from commit 378ddb9)
@Aaronontheweb Aaronontheweb added this to the 1.6.0 milestone Sep 28, 2026
@Aaronontheweb
Aaronontheweb enabled auto-merge (squash) September 28, 2026 18:03
@Aaronontheweb
Aaronontheweb merged commit 0c84d4f into akkadotnet:dev Sep 28, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Akka.Cluster: a Down node can become leader, remove itself, and keep running outside the cluster

1 participant