Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions packages/client/lib/client/commands-queue.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import assert from 'node:assert';
import RedisCommandsQueue from './commands-queue';

describe('RedisCommandsQueue', () => {
function createQueue() {
return new RedisCommandsQueue(3, null, () => {}, 'test-client');
}

describe('extractAllCommands', () => {
it('returns and removes every queued command, not just the first one', () => {
const queue = createQueue();
for (let i = 0; i < 5; i++) {
queue.addCommand([`CMD${i}`]).catch(() => {});
}

const extracted = queue.extractAllCommands();

assert.strictEqual(extracted.length, 5);
assert.deepStrictEqual(
extracted.map(command => command.args?.[0]),
['CMD0', 'CMD1', 'CMD2', 'CMD3', 'CMD4'],
);
assert.strictEqual(queue.extractAllCommands().length, 0);
});
});
});
5 changes: 3 additions & 2 deletions packages/client/lib/client/commands-queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -665,15 +665,16 @@ export default class RedisCommandsQueue {
}

/**
* Gets all commands from the write queue without removing them.
* Extracts all commands from the write queue.
*/
extractAllCommands(): CommandToWrite[] {
const result: CommandToWrite[] = [];
let current = this.#toWrite.head;
while (current) {
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.

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.

}
return result;
}
Expand Down