[redis-ha] Self-heal a masterless set instead of waiting for sentinel - #413
Open
ShmuelOps wants to merge 1 commit into
Open
[redis-ha] Self-heal a masterless set instead of waiting for sentinel#413ShmuelOps wants to merge 1 commit into
ShmuelOps wants to merge 1 commit into
Conversation
ShmuelOps
marked this pull request as draft
August 2, 2026 11:17
ShmuelOps
force-pushed
the
fix/redis-ha-stale-master-failover
branch
from
August 2, 2026 11:33
10b1981 to
233a97d
Compare
ShmuelOps
force-pushed
the
fix/redis-ha-stale-master-failover
branch
2 times, most recently
from
August 2, 2026 11:54
1fe46ff to
76e1680
Compare
ShmuelOps
marked this pull request as ready for review
August 2, 2026 12:43
ShmuelOps
force-pushed
the
fix/redis-ha-stale-master-failover
branch
2 times, most recently
from
August 2, 2026 21:34
9b4ef02 to
55aee49
Compare
When the master is lost and its replacement rejoins as a replica behind the same announce address, sentinel keeps advertising that address as master and the set ends up with no master at all; sentinel aborts every failover with -NOGOODSLAVE because all candidates look stale. The split-brain container cannot help: it only fixes disagreement with sentinel, and it resolves sentinel through the headless service, which loses all endpoints once every pod is unready. Poll the announce-N services (publishNotReadyAddresses) for whether any member holds role:master. After MASTERLESS_CONFIRMATIONS consecutive observations of none, promote the reachable member with the highest replication offset, repoint the others, and reset the sentinels. The branch where sentinel names this pod as master but it runs as a replica routes into the same healer instead of reinit: reinit re-derives slaveof and shuts down, restarting the pod straight back into a replica while sentinel keeps naming it (DandyDeveloper#383). The misnamed pod is the fastest observer of the masterless state, and promotion stays offset-aware so a freshly replaced empty pod is never promoted merely because a stale sentinel record names it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: ShmuelOps <shmuel@tennasys.com>
ShmuelOps
force-pushed
the
fix/redis-ha-stale-master-failover
branch
from
August 3, 2026 13:04
bf20adf to
0a3bcb0
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this PR does / why we need it:
Fixes the masterless deadlock in #412.
When the master pod is lost and its replacement rejoins as a replica behind the same announce address, sentinel keeps advertising that address as master (it answers pings), every member's local view agrees with sentinel, and the set ends up with no master at all. Replicas loop on
-NOMASTERLINK Can't SYNC while not connected with my master; automatic failovers abort with-failover-abort-no-good-slave/-failover-abort-not-elected, and an explicitSENTINEL FAILOVERreturns-NOGOODSLAVE, because sentinel considers every remaining candidate stale. We hit this in production and it persisted for 7.5h until manual intervention.The
split-brain-fixcontainer cannot help as written, for two reasons:identify_mastercannot resolve the name.MASTERcomes back empty and the existing quorum-reset is equally a no-op.The change. The
announce-Nservices setpublishNotReadyAddresses, so they keep resolving throughout the outage. Poll those for the one fact that matters — does any member holdrole:master. AfterMASTERLESS_CONFIRMATIONSconsecutive observations of none (default 5,MASTERLESS_CONFIRM_INTERVALseconds apart, default 5), promote the reachable member with the highest replication offset viaREPLICAOF NO ONE, repoint the others, and reset the sentinels so they rediscover the topology.The
reinitcrashloop branch. The branch where sentinel names this pod as master while it runs as a replica used to callreinit, which re-derivesslaveofand shuts the pod down — it restarts straight back into a replica while sentinel keeps naming it, an endless shutdown loop (#383). That branch now routes into the same masterless healer: the misnamed pod is the fastest observer of this state, so it heals on its first detection cycle instead of waiting for its peers.Safety properties:
role:masteraborts the sequence, so concurrent containers never double-promote, and ordinary sentinel failovers are never interfered with.PROMOTE_SCAN_ATTEMPTStimes, so a member that happens to be mid-restart doesn't postpone recovery.MAX_QUORUM_FAILURESpattern — no new chart values.Relation to #410
#410 addresses the same family of failure (sentinel naming an address whose real role is
slave) by having the named pod promote itself withreplicaof no one. This PR deliberately does not do that, for three reasons:MASTER == ANNOUNCE_IP+role:slaveis exactly what a freshly replaced pod looks like behind a stale sentinel record — sentinel never re-elected it, it just kept the address. Promoting that pod without comparing replication offsets can promote an empty node, and the other replicas (which already point at it) then full-resync from the empty dataset. Here the healer always promotes the highest-offset reachable member — which is the named pod whenever it genuinely is the best candidate.announce-Nservices, which publish not-ready addresses.SENTINEL FAILOVERfirst and promotes directly only after N consecutive masterless confirmations, aborting the moment any master appears.#410's valid core idea — stop the
reinitshutdown loop in that branch (#383) — is folded in here, with the promotion decision kept offset-aware.Which issue this PR fixes
Validation
Verified on an isolated 3-replica release. Fault: point every member at an unreachable address, then
SENTINEL RESET— this reproduces the signature above exactly.The named pod detects the state on its next detection cycle and heals directly (previously 1m57s via the peers' stale-master counters):
Peers, confirming only one member acts:
No false positives: deleting the master pod produces an ordinary sentinel failover — new master within seconds, the deleted pod rejoins as a replica, and no healer on any pod promotes or enters the confirmation loop. It acts only in the state sentinel cannot leave on its own.
helm lintclean; the rendered script passessh -nwith defaults and withauth+sentinel.authenabled.Checklist
[stable/mychartname])