Skip to content

Sentinel: re-resolve the primary when it is demoted on a healthy connection - #3281

Open
rebaseandpanic wants to merge 1 commit into
StackExchange:mainfrom
rebaseandpanic:pr/sentinel-demotion-requery
Open

rebaseandpanic wants to merge 1 commit into
StackExchange:mainfrom
rebaseandpanic:pr/sentinel-demotion-requery

Conversation

@rebaseandpanic

Copy link
Copy Markdown
Contributor

Scenario

The client uses Sentinel mode (ServiceName) and is connected to primary A.

  1. Sentinel fails over to node C, often at an address the client has never seen (e.g. a recreated pod on Kubernetes).
  2. A is demoted to a replica, but the client's connections to A stay up.
  3. The +switch-master message never reaches the client.

Today the client sees the demotion (INFO replication reports role:slave) but only re-elects among the nodes it knows; all of them are replicas, and sentinel is not asked again. Writes fail until restart:

No connection (requires writable - not eligible for replica) is active/available to service this operation

#3130 fixed the case where the new primary is already known; this is the unknown-address case it left untouched. We hit this class of failure in production (Valkey + Sentinel on Kubernetes).

Fix

On the sentinel connection's heartbeat (every second), using local state only: if the primary sentinel last reported is connected but reports itself as a replica, run one recovery attempt in the background:

  • ask sentinel who the primary is; no answer: try again next heartbeat;
  • sentinel still names the demoted node: re-read its role with one INFO (no full reconfigure);
  • otherwise switch, as +switch-master would have.

Skipped while a reconfigure is running, after dispose, or when disconnected (the existing reconnect timer handles that). No public API change, no periodic sentinel polling. This goes slightly beyond the sentinel client guidelines ("Handling reconnections"), which rely on sentinel disconnecting the demoted node's clients.

Tests

In-process sentinel (InProcessSentinelServer) plus SetReplicaOf on the toy server:

  • demotion to an unknown address: fails without the fix, passes with it;
  • sentinel lagging: sentinel is still polled, but no reconfigure on every heartbeat.

Also checked against real Valkey 8.1 + Sentinel in docker: the client switched ~1 s after noticing the demotion.

Refs #1891, #2487, #1670

Checklist

  • I fully and freely contribute this code in accordance with the project license (and am legally able to do so)
  • I take responsibility for this contribution's quality and correctness, including any portions produced with AI assistance (see CONTRIBUTING.md).

… connection

A sentinel-managed connection notices an in-place demotion of its primary
(INFO replication reports role:slave) but only re-elects among the nodes it
already knows. When sentinel promoted a node at an address the client has
never seen and the +switch-master announcement was missed, there is no
primary among them and nothing asks sentinel again: writes fail with
"No connection (requires writable - not eligible for replica)" until restart.

The sentinel connection's heartbeat now checks, from local state only,
whether each managed connection's sentinel-reported primary is connected
and reporting itself as a replica (or was never added). If so, a recovery
attempt runs off the heartbeat thread, at most one per managed connection:
- it asks sentinel first; no answer: retry on a later heartbeat;
- sentinel names a node held as a connected replica (sentinel has not
  failed over yet, or our view of that node's role is stale): re-read that
  node's role with one INFO replication, rather than reconfiguring every
  node once per heartbeat for as long as sentinel lags (where INFO cannot
  be sent, fall through to the switch rather than ask sentinel forever);
- otherwise switch, as +switch-master would have.
An attempt is skipped while the managed connection is already reconfiguring,
or once either multiplexer is disposed; disconnected primaries are left to
the existing reconnect timer. The heartbeat scan also forgets disposed
children, as the +switch-master handler does.

Tests: an in-process sentinel (InProcessSentinelServer) and a standalone
SetReplicaOf on the toy server. The demotion-to-unknown-address test fails
without the fix (no primary at all) and passes with it; a second test holds
sentinel on the demoted node and checks that sentinel is still polled but
the connection is not reconfigured on every heartbeat, then follows sentinel
once it catches up.

Refs StackExchange#1891, StackExchange#2487, StackExchange#1670

This branch has not been deployed

No deployments
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.

1 participant