Skip to content

Do not replace the master until a quorum of replicas is ready (#23) - #24

Merged
usiegj00 merged 3 commits into
mainfrom
23-operator-managed-failover-master-deleted-before-replacement-replica-syncs-causing-a-short-full-outage-on-rolling-updates
Sep 28, 2026
Merged

usiegj00 merged 3 commits into
mainfrom
23-operator-managed-failover-master-deleted-before-replacement-replica-syncs-causing-a-short-full-outage-on-rolling-updates

Conversation

@usiegj00

@usiegj00 usiegj00 commented Sep 28, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #23.

In operator-managed failover mode a rolling update replaced the master before the
replacement replica had synced, leaving the cluster with no master for tens of
seconds on any routine pod-spec change.

UpdateRedisesPods gated the replacement on a loop over GetRedisesIPs, which
only reports pods whose phase is already Running. While the replacement replica
is terminating or being recreated it is absent from that list, so the loop had
nothing to iterate and "no replica reported unready" was trivially true. The gate
passed on an empty set and the master was deleted straight after.

Sentinel deployments never saw this: CheckSentinelSlavesNumberQuorumInMemory
gates the master delete on sentinel's own view of the replicas, which lags pod
creation and does block. Operator-managed mode skips that gate, so the replica
count has to be checked here instead.

The fix

Count ready replicas and require a quorum before replacing any pod. A replica
that is missing now blocks the replacement instead of silently satisfying it.
Quorum rather than the full count mirrors the sentinel gate, so one permanently
unavailable replica cannot block a rollout forever.

Scoped to OperatorManagedFailover(), so sentinel behaviour is unchanged.

Measured before and after

The rollout test previously only checked the end state, which converges either
way. It now samples master availability every 500ms for the duration of the
rollout and records the longest run with no master at all.

The first commit adds only that measurement, so CI recorded the broken behaviour
before the fix landed:

k8s before (b90165eb) after (fd323578)
1.35.8 0 masterless — pass 554 samples, 0 masterless (0s)
1.36.4 0 masterless — pass 544 samples, 0 masterless (0s)
1.37.0 164 samples, 118 masterless (59s) — fail 525 samples, 0 masterless (0s)

It reproduced on 1 of 3 versions, which is expected for a race between the master
delete and the replacement replica appearing in GetRedisesIPs. The 59s measured
here matches a 28-83s window observed on a production cluster.

No-op for sentinel

The unit test covers both modes from an identical fixture: with replicas missing
entirely, operator-managed mode now keeps the master, while sentinel mode is
unchanged and still reaches its own quorum gate.

An earlier attempt applied the gate unconditionally and broke ~20 tests, every one
of them a sentinel test (generateRF enables sentinel). After scoping, all of
them pass untouched.

Notes for reviewers

  • Rollouts are slower now, by design: replicas are replaced one at a time, each
    syncing before the next pod is touched, as described in
    upgrade redisfailover cluster spotahome/redis-operator#637. The rollout test's wait went from 5 to 10 minutes
    to accommodate it, and its timeout error now reports how many pods were
    recreated so a slow rollout is distinguishable from a wedged one.
  • Three operator-managed unit fixtures reported only the master IP and expected it
    to be replaced regardless. They described a cluster with nothing left to
    promote, so they now model the master plus its ready replicas.

The rollout test only checked the end state, which converges whether or
not the master was replaced too early. Sample the cluster every 500ms for
the duration of the rollout instead and record the longest run with no
master at all, so a mid-rollout outage is visible rather than averaged
away by the final assertions.

Refs #23
@codecov-commenter

codecov-commenter commented Sep 28, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 84.61538% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
operator/redisfailover/checker.go 84.61% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

UpdateRedisesPods gated replacing pods on a loop over GetRedisesIPs, which
only reports pods whose phase is already Running. While the replacement
replica is terminating or being recreated it is absent from that list, so
the loop had nothing to iterate and "no replica reported unready" was
trivially true. The gate passed on an empty set and the master was deleted
straight after, leaving no master and no synced replica to promote until a
later reconcile elected one.

Count the ready replicas and require a quorum, so a replica that is missing
blocks the replacement instead of silently satisfying it. Quorum rather
than the full count mirrors CheckSentinelSlavesNumberQuorumInMemory, so one
permanently unavailable replica cannot block the rollout forever.

Scoped to OperatorManagedFailover. Sentinel deployments already reach their
own quorum gate, which reads sentinel's view of the replicas and does block,
so their behaviour is unchanged.

Three operator-managed fixtures reported only the master IP and expected it
to be replaced regardless; they described a cluster with nothing left to
promote, so they now model the master plus its ready replicas.

Fixes #23
Replacing pods one at a time means three restarts and three initial syncs
end to end, which ran to about five minutes on a loaded runner and tripped
the old limit. Raise the wait to ten minutes and report how many pods were
recreated on timeout, so a slow rollout is distinguishable from a wedged one.

Also loosen the masterless threshold from 1 to 10 samples. Promotion is not
atomic, so a sample can land between demoting the old master and promoting
the chosen replica. The regression measured 118 consecutive masterless
samples; the restored ordering measures 0 to 2.

Refs #23
@usiegj00
usiegj00 marked this pull request as ready for review September 28, 2026 06:51
@usiegj00
usiegj00 merged commit 9d11a9e into main Sep 28, 2026
12 checks passed
@usiegj00
usiegj00 deleted the 23-operator-managed-failover-master-deleted-before-replacement-replica-syncs-causing-a-short-full-outage-on-rolling-updates branch September 28, 2026 06:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Operator-managed failover: master deleted before replacement replica syncs, causing a short full outage on rolling updates

2 participants