Skip to content

Send primary-only commands to the primary when the caller prefers a replica - #3291

Merged
mgravell merged 2 commits into
StackExchange:mainfrom
AsadIqbal-Dev:fix/prefer-replica-primary-only
Oct 23, 2026
Merged

mgravell merged 2 commits into
StackExchange:mainfrom
AsadIqbal-Dev:fix/prefer-replica-primary-only

Conversation

@AsadIqbal-Dev

Copy link
Copy Markdown
Contributor

Fixes #2821

A few methods pick a server up front, through RedisBase.GetFeatures, to decide which command to send:

  • KeyDelete (DEL vs UNLINK)
  • KeyExpire with sub-second precision (PEXPIRE)
  • LockExtend (SET IFEQ)
  • LockRelease (DELEX)

GetFeatures passed the caller's flags straight to SelectServer, so with PreferReplica it picked a replica. The message itself is upgraded to DemandMaster by Message.SetPrimaryOnly, but because the replica was passed in explicitly, the send failed with Command cannot be issued to a replica. Commands that pick their server when the message is sent, like StringSet, already went to the primary, which is the difference the issue describes.

GetFeatures now applies the same rule as Message.SetPrimaryOnly for primary-only commands: PreferMaster/PreferReplica become DemandMaster before selecting. DemandReplica is left alone, so it still fails the way it does for every other primary-only command. RedisServer overrides GetFeatures and isn't affected.

Test: KeyTests.PrimaryOnlyCommandsWithPreferReplicaRunOnThePrimary runs against the primary/replica pair and calls each method, sync and async, with PreferReplica. I ran CI on my fork. Without the fix, the test fails on RESP2 and RESP3 with Command cannot be issued to a replica: PEXPIRE .... With the fix, the Ubuntu job passes.

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).

…eplica

KeyDelete, KeyExpire (milliseconds), LockExtend and LockRelease pick a server up front to check its features. With PreferReplica that picked a replica, and the primary-only message was then rejected with 'Command cannot be issued to a replica'. GetFeatures now upgrades the flags the same way Message does.

Fixes StackExchange#2821
@mgravell

mgravell commented Oct 22, 2026 •

Copy link
Copy Markdown
Collaborator

Makes perfect sense at a glance. I'll look at it in more detail tomorrow, thanks!

@mgravell

Copy link
Copy Markdown
Collaborator

Looks good, thanks. I pushed one small commit onto your branch (fd37aec) rather than round-tripping two nits:

  • GetFeatures now tests != DemandReplica instead of is PreferMaster or PreferReplica, so it is literally the same rule as Message.SetPrimaryOnly (which also upgrades None via its default arm); the comment now points at that method.
  • A comment in the test explaining that the fractional-second expiry is what forces the PEXPIRE path.

No behaviour change from your version. Will merge once CI is green.

@mgravell
mgravell merged commit 6cbaa37 into StackExchange:main Oct 23, 2026
4 checks passed
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.

Different behavior on the 2 write(del and set) commands with the PreferReplica flags.

2 participants