-
feat(cluster): join as non-voter, auto-promote/demote by reachability
Release / version (push) Canceled after 0sRelease / frontend (push) Canceled after 0sRelease / linux (amd64) (push) Canceled after 0sRelease / linux (arm64) (push) Canceled after 0sRelease / windows (push) Canceled after 0sRelease / macos (push) Canceled after 0sRelease / release (push) Canceled after 0sBuild and Push Docker Image / build-and-push (push) Canceled after 0sreleased this
2026-06-20 22:57:05 +03:00 | 128 commits to main since this releaseA new node used to be added straight as a Raft voter, so a botched join (e.g.
an advertise address that's unreachable or later goes down) could drag the
cluster below quorum and wedge control-plane writes. Nodes now join as
NON-voters — they replicate the full log/snapshot and serve reads, but don't
count toward quorum — so a bad joiner can never cost the cluster its write
availability.A leader-side membership manager (internal/cluster/raft/membership.go, ~10s)
reconciles suffrage by TCP-probing each peer's advertised Raft addr:- promotes a non-voter to voter once it has stayed reachable >= 20s
- demotes a voter unreachable >= 90s back to non-voter to restore quorum, but
only while a majority is still reachable (the demote config-change must
commit) — so the genuine 2-node 'lost a voter' case is left to the manual
'Recover this node' action instead of churning uncommittable changes.
The decision policy is a pure function (decideMembership) with unit tests; the
manager reads the swappable Service so one loop survives a wipe-state
re-activation. Adds AddNonvoter/DemoteVoter across the Service interface, the
real Node, the disabled/swappable wrappers and the bridge test fake.Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01U3LvJu9qZfspPr21TW9ptvDownloads