Repository navigation
[chart/redis-ha][BUG] Masterless deadlock: sentinel keeps naming a master whose actual role is replica; split-brain container never heals it #412
Description
Describe the bug
After the Redis master pod is lost (spot node reclaim in our case) and its replacement is rescheduled behind the same announce Service ClusterIP, the set can wedge into a state with zero actual masters that nothing in the chart ever heals:
- The replacement pod's
init.shcan't get a usable answer at boot and falls back to replicatingannounce-0. - Sentinel keeps advertising the old master's announce IP as master — the IP is still up (it's a stable ClusterIP, now answering from the replacement pod), so sentinel's down detection never triggers a failover.
- The other replicas keep replicating the sentinel-named "master", which is itself a replica: they loop on
Master is currently unable to PSYNC but should be in the future: -NOMASTERLINK Can't SYNC while not connected with my masteronce per second, indefinitely. - The
split-brain-fixcontainer never intervenes: it only reinitializes when a pod's localmaster_hostdisagrees with sentinel (or when sentinel returns no master →MAX_QUORUM_FAILURESreset). Here every member agrees with sentinel about the stale master, so both conditions pass. The one pod that does get reinit'd (the one owning the stale announce IP, since sentinel names it master while its role is replica) just re-runsinit.sh, whose fallback makes it a replica again — a ~4-minute reinit loop that never converges.
Observed on chart 4.39.0 (Redis 7.4, 5 replicas, quorum: 3, haproxy enabled, no PVC): the set stayed masterless — accepting zero writes — for 7.5 hours until manual intervention (restarting two members and rescheduling the stale-IP pod finally re-seated the topology). Logs from the two ends of the cycle:
server-0 (agreed with sentinel that the dead master's announce IP was master):
16:13:31 * Connection with master lost.
16:13:31 * Reconnecting to MASTER 172.20.242.95:6379
16:13:31 # Error condition on socket for SYNC: Connection refused
...then hours of: * Trying a partial resynchronization (request ...)
server-4 (the replacement pod behind that announce IP, running as replica of announce-0):
16:16:00 * Connecting to MASTER 172.20.156.64:6379
16:16:00 * Master is currently unable to PSYNC but should be in the future: -NOMASTERLINK Can't SYNC while not connected with my master
(repeats once per second for 7.5h)
To Reproduce
- Deploy redis-ha (≥3 replicas, sentinel defaults, no persistence).
- Kill the node hosting the current master pod (uncleanly — e.g. spot interruption) so the pod is rescheduled while its announce ClusterIP stays constant.
- If the replacement boots before sentinel completes a failover (or init.sh hits its
announce-0fallback), the set wedges:INFO replicationshowsrole:slaveon every member, sentinelget-master-addr-by-namestill returns the old master's announce IP, and no failover ever fires.
Timing-dependent — it needs the replacement pod to come back on the same announce IP before sentinel declares odown — but node-loss + reschedule makes it reachable in practice.
Expected behavior
The split-brain container (or sentinel config shipped by the chart) should detect that the sentinel-named master does not actually hold role:master and force a failover, instead of leaving the set masterless forever.
Additional context
PR incoming: adds a stale-master check to the split-brain loop's agreement path — if the sentinel-named master reports a non-master role for MAX_STALE_MASTER_FAILURES consecutive checks (default 3), issue SENTINEL FAILOVER <group> so sentinel promotes one of the known replicas. Unreachable masters are still left to sentinel's own down detection.
Activity
hexilic commentedon Oct 7, 2026
We hit the same thing on chart 4.39.0 with Valkey 9.1.2 (3 replicas,
quorum: 2, HAProxy, auth on, no persistence), and we have a reproduction that doesn't depend on timing: force-delete the master pod.Reproduction (k3d, four nodes):
The replacement starts within a second, while the old container is still answering on the announce ClusterIP.
init.shasks Sentinel, which hasn't noticed the kill yet, and gets that announce IP. It pings it, the dying container answers, and the new pod configures itself as a replica of its own announce address:From there it played out as you describe in #412, plus two details that may help:
Sentinel did reach odown and voted, but the elected leader never acted. Every round was won by the Sentinel in the replacement pod.
init.shhad rewritten that Sentinel's config, and it never ran the failover. With the defaultfailover-timeout: 180000the retries came every 6 minutes, each won by the same Sentinel:A manual
SENTINEL FAILOVERfrom another Sentinel aborted with-failover-abort-no-good-slave: the replicas had been disconnected for longer than Sentinel trusts. Meanwhile the wedged pod's redis container crash-looped on its startup probe (role=slave; repl=connect).We recovered after 18 minutes by gracefully deleting the pod behind the stale announce IP, which also removed the Sentinel that kept winning the vote. The next scheduled retry then elected another Sentinel, which promoted a replica normally. No data was lost.
A milder form, from a graceful rolling restart: during a rollout, a restarted master's replacement asked the shared Sentinel Service, reached a Sentinel that hadn't switched yet, and was again told its own address. That time
split-brain-fixcaught the mismatch ("expected master is , reinitializing") and it healed in about 10 s. It shows the stale answer isn't limited to unclean node loss.And a related one: after a hard kill,
init.shforcedSENTINEL FAILOVERusing a master address it had resolved about 15 s earlier. By then Sentinel had already failed over correctly, so the forced failover moved the healthy new master a second time. Re-queryingget-master-addr-by-nameright before forcing a failover would avoid that.What we've done meanwhile:
failover-timeout: 60000, an alert when no pod reportsrole:master, and never force-deleting a master in drills. #413's approach (fail over when the Sentinel-named master reports a non-master role for N checks) would have healed both our cases. We'd be glad to see it merged and can test a build of it against this reproduction.