Skip to content

fix(cluster): make extractAllCommands drain the write queue - #3364

Open
GiHoon1123 wants to merge 1 commit into
redis:masterfrom
GiHoon1123:fix-3363-extract-all-commands
Open

fix(cluster): make extractAllCommands drain the write queue#3364
GiHoon1123 wants to merge 1 commit into
redis:masterfrom
GiHoon1123:fix-3363-extract-all-commands

Conversation

@GiHoon1123

@GiHoon1123 GiHoon1123 commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Fixes #3363.

extractAllCommands() removed the current linked-list node before reading
current.next. DoublyLinkedList.remove() clears the removed node's links, so
the loop stopped after the first command and left the rest in the write queue.

This stores the next node before removing the current one, matching the pattern
used by extractCommandsForSlots().

Also corrected the method comment, since this method does remove the commands it
returns.

Test added for extracting multiple queued commands and leaving the queue empty.


Note

Medium Risk
Cluster slot migration relies on extractAllCommands to relocate slotless commands; the prior bug could leave commands stranded on a node with no slots.

Overview
Fixes a bug in extractAllCommands() where only the first pending write-queue command was returned. The loop called remove() before advancing current, and DoublyLinkedList.remove() clears the removed node’s links, so current.next was lost and the rest of the queue stayed behind.

The implementation now saves the next node, then removes the current one—the same pattern as extractCommandsForSlots()—so the full write queue is extracted and drained. The method comment is updated to state that commands are removed, not merely read.

A unit test queues five commands and asserts they are all returned in order and that a second extractAllCommands() yields an empty queue.

Reviewed by Cursor Bugbot for commit cf393bd. Bugbot is set up for automated code reviews on this repo. Configure here.

remove() clears a node's own next/previous pointers, so reading
current.next after removing current always came back undefined and
ended the loop early.

Same fix as extractCommandsForSlots: grab next before removing.

Fixes redis#3363

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cf393bdaf0

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

this.#toWrite.remove(current);
const toRemove = current;
current = current.next;
this.#toWrite.remove(toRemove);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Rebind cancellation listeners when transferring commands

When a remaining command has an abort signal or timeout and a full-shard migration calls this method, its listener still closes over the original source queue and linked-list node created in addCommand. The cluster caller then passes only the command value to prependCommandsToWrite, which creates a different destination node; if cancellation fires before the destination writes it, the promise is rejected but removal from the now-empty source queue is a no-op, so the destination retains and can execute the canceled command. Rebind these listeners to the destination node as part of transferring the command.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Real, but pre-existing — goes back to the V5 rewrite, not something this PR touches.
The old bug just hid it by dropping most commands instead of moving them. Filing a
separate issue for this.

result.push(current.value);
this.#toWrite.remove(current);
const toRemove = current;
current = current.next;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve batch routing when draining the source queue

When a source is split across multiple destinations while a transaction or pipeline is still queued, _executeMulti and _executePipeline add their commands without slotNumber, so extractCommandsForSlots ignores the entire batch. This newly complete drain then lets cluster-slots.ts prepend all of those commands to lastDestNode, which is merely the final destination in notification order and may not own the batch's key slot; the commands consequently receive MOVED errors or an aborted transaction instead of surviving the handoff. Preserve the selected batch slot on these queue entries or otherwise route the extracted chain to the destination owning that slot.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also pre-existing — _executeMulti/_executePipeline not setting slotNumber goes
back to the V5 rewrite too, not something this PR touches. Fixing it properly means
threading slot info through batch commands, bigger than this PR. Filing a separate
issue.

@nkaradzhov nkaradzhov left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GiHoon1123, thanks for the fix and the focused regression test. Confirmed against master: remove() clears the node links before next is read, so only the head command was ever relocated during a full-shard handoff.

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.

extractAllCommands() returns only the first queued command

2 participants