1
0
mirror of https://github.com/redis/node-redis.git synced 2025-08-07 13:22:56 +03:00

fix client.reset

This commit is contained in:
Leibale
2024-01-31 15:07:01 -05:00
parent baef3486ad
commit 0cd6915698
3 changed files with 70 additions and 57 deletions

View File

@@ -1,7 +1,7 @@
import { SinglyLinkedList, DoublyLinkedNode, DoublyLinkedList } from './linked-list'; import { SinglyLinkedList, DoublyLinkedNode, DoublyLinkedList } from './linked-list';
import encodeCommand from '../RESP/encoder'; import encodeCommand from '../RESP/encoder';
import { Decoder, PUSH_TYPE_MAPPING, RESP_TYPES } from '../RESP/decoder'; import { Decoder, PUSH_TYPE_MAPPING, RESP_TYPES } from '../RESP/decoder';
import { CommandArguments, TypeMapping, ReplyUnion, RespVersions, SimpleStringReply, ReplyWithTypeMapping } from '../RESP/types'; import { CommandArguments, TypeMapping, ReplyUnion, RespVersions } from '../RESP/types';
import { ChannelListeners, PubSub, PubSubCommand, PubSubListener, PubSubType, PubSubTypeListeners } from './pub-sub'; import { ChannelListeners, PubSub, PubSubCommand, PubSubListener, PubSubType, PubSubTypeListeners } from './pub-sub';
import { AbortError, ErrorReply } from '../errors'; import { AbortError, ErrorReply } from '../errors';
import { MonitorCallback } from '.'; import { MonitorCallback } from '.';
@@ -154,11 +154,11 @@ export default class RedisCommandsQueue {
}); });
} }
#addPubSubCommand(command: PubSubCommand, asap = false) { #addPubSubCommand(command: PubSubCommand, asap = false, chainId?: symbol) {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
this.#toWrite.add({ this.#toWrite.add({
args: command.args, args: command.args,
chainId: undefined, chainId,
abort: undefined, abort: undefined,
resolve() { resolve() {
command.resolve(); command.resolve();
@@ -237,13 +237,13 @@ export default class RedisCommandsQueue {
return this.#addPubSubCommand(command); return this.#addPubSubCommand(command);
} }
resubscribe() { resubscribe(chainId?: symbol) {
const commands = this.#pubSub.resubscribe(); const commands = this.#pubSub.resubscribe();
if (!commands.length) return; if (!commands.length) return;
this.#setupPubSubHandler(); this.#setupPubSubHandler();
return Promise.all( return Promise.all(
commands.map(command => this.#addPubSubCommand(command, true)) commands.map(command => this.#addPubSubCommand(command, true, chainId))
); );
} }
@@ -271,11 +271,12 @@ export default class RedisCommandsQueue {
return this.#pubSub.getTypeListeners(type); return this.#pubSub.getTypeListeners(type);
} }
monitor(callback: MonitorCallback, typeMapping: TypeMapping = {}, asap = false) { monitor(callback: MonitorCallback, options?: CommandOptions) {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
const typeMapping = options?.typeMapping ?? {};
this.#toWrite.add({ this.#toWrite.add({
args: ['MONITOR'], args: ['MONITOR'],
chainId: undefined, chainId: options?.chainId,
abort: undefined, abort: undefined,
// using `resolve` instead of using `.then`/`await` to make sure it'll be called before processing the next reply // using `resolve` instead of using `.then`/`await` to make sure it'll be called before processing the next reply
resolve: () => { resolve: () => {
@@ -295,7 +296,7 @@ export default class RedisCommandsQueue {
reject, reject,
channelsCounter: undefined, channelsCounter: undefined,
typeMapping typeMapping
}, asap); }, options?.asap);
}); });
} }
@@ -306,7 +307,7 @@ export default class RedisCommandsQueue {
#resetFallbackOnReply?: Decoder['onReply']; #resetFallbackOnReply?: Decoder['onReply'];
async reset<T extends TypeMapping>(typeMapping?: T) { async reset<T extends TypeMapping>(chainId: symbol, typeMapping?: T) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// overriding onReply to handle `RESET` while in `MONITOR` or PubSub mode // overriding onReply to handle `RESET` while in `MONITOR` or PubSub mode
this.#resetFallbackOnReply = this.decoder.onReply; this.#resetFallbackOnReply = this.decoder.onReply;
@@ -328,7 +329,7 @@ export default class RedisCommandsQueue {
this.#toWrite.push({ this.#toWrite.push({
args: ['RESET'], args: ['RESET'],
chainId: undefined, chainId,
abort: undefined, abort: undefined,
resolve, resolve,
reject, reject,

View File

@@ -732,6 +732,9 @@ describe('Client', () => {
skipMe: true skipMe: true
}) })
]); ]);
await once(duplicate, 'ready');
await Promise.all([ await Promise.all([
waitTillBeenCalled(listener), waitTillBeenCalled(listener),
client.ping() client.ping()

View File

@@ -332,24 +332,8 @@ export default class RedisClient<
); );
} }
#handshake(asap = false, promises: Array<Promise<unknown>> = []) { #handshake(selectedDB: number) {
if (this.#selectedDB !== 0) { const commands = [];
promises.push(
this.#queue.addCommand(
['SELECT', this.#selectedDB.toString()],
{ asap }
)
);
}
if (this.#options?.readonly) {
promises.push(
this.#queue.addCommand(
COMMANDS.READONLY.transformArguments(),
{ asap }
)
);
}
if (this.#options?.RESP) { if (this.#options?.RESP) {
const hello: HelloOptions = {}; const hello: HelloOptions = {};
@@ -365,43 +349,45 @@ export default class RedisClient<
hello.SETNAME = this.#options.name; hello.SETNAME = this.#options.name;
} }
promises.push( commands.push(
this.#queue.addCommand( HELLO.transformArguments(this.#options.RESP, hello)
HELLO.transformArguments(this.#options.RESP, hello),
{ asap }
)
); );
} else { } else {
if (this.#options?.name) {
promises.push(
this.#queue.addCommand(
COMMANDS.CLIENT_SETNAME.transformArguments(this.#options.name),
{ asap }
)
);
}
if (this.#options?.username || this.#options?.password) { if (this.#options?.username || this.#options?.password) {
promises.push( commands.push(
this.#queue.addCommand(
COMMANDS.AUTH.transformArguments({ COMMANDS.AUTH.transformArguments({
username: this.#options.username, username: this.#options.username,
password: this.#options.password ?? '' password: this.#options.password ?? ''
}), })
{ asap } );
) }
if (this.#options?.name) {
commands.push(
COMMANDS.CLIENT_SETNAME.transformArguments(this.#options.name)
); );
} }
} }
return promises; if (selectedDB !== 0) {
commands.push(['SELECT', this.#selectedDB.toString()]);
}
if (this.#options?.readonly) {
commands.push(
COMMANDS.READONLY.transformArguments()
);
}
return commands;
} }
#initiateSocket(): RedisSocket { #initiateSocket(): RedisSocket {
const socketInitiator = () => { const socketInitiator = () => {
const promises: Array<Promise<unknown>> = []; const promises = [],
chainId = Symbol('Socket Initiator');
const resubscribePromise = this.#queue.resubscribe(); const resubscribePromise = this.#queue.resubscribe(chainId);
if (resubscribePromise) { if (resubscribePromise) {
promises.push(resubscribePromise); promises.push(resubscribePromise);
} }
@@ -410,13 +396,24 @@ export default class RedisClient<
promises.push( promises.push(
this.#queue.monitor( this.#queue.monitor(
this.#monitorCallback, this.#monitorCallback,
this._commandOptions?.typeMapping, {
true typeMapping: this._commandOptions?.typeMapping,
chainId,
asap: true
}
) )
); );
} }
this.#handshake(true, promises); const commands = this.#handshake(this.#selectedDB);
for (let i = commands.length - 1; i >= 0; --i) {
promises.push(
this.#queue.addCommand(commands[i], {
chainId,
asap: true
})
);
}
if (promises.length) { if (promises.length) {
this.#write(); this.#write();
@@ -885,7 +882,9 @@ export default class RedisClient<
} }
async MONITOR(callback: MonitorCallback<TYPE_MAPPING>) { async MONITOR(callback: MonitorCallback<TYPE_MAPPING>) {
const promise = this._self.#queue.monitor(callback, this._commandOptions?.typeMapping); const promise = this._self.#queue.monitor(callback, {
typeMapping: this._commandOptions?.typeMapping
});
this._self.#scheduleWrite(); this._self.#scheduleWrite();
await promise; await promise;
this._self.#monitorCallback = callback; this._self.#monitorCallback = callback;
@@ -897,10 +896,20 @@ export default class RedisClient<
* Reset the client to its default state (i.e. stop PubSub, stop monitoring, select default DB, etc.) * Reset the client to its default state (i.e. stop PubSub, stop monitoring, select default DB, etc.)
*/ */
async reset() { async reset() {
const promises = [this._self.#queue.reset()]; const chainId = Symbol('Reset Chain'),
this._self.#handshake(false, promises); promises = [this._self.#queue.reset(chainId)],
selectedDB = this._self.#options?.database ?? 0;
for (const command of this._self.#handshake(selectedDB)) {
promises.push(
this._self.#queue.addCommand(command, {
chainId
})
);
}
this._self.#scheduleWrite(); this._self.#scheduleWrite();
await Promise.all(promises); await Promise.all(promises);
this._self.#selectedDB = selectedDB;
this._self.#monitorCallback = undefined;
} }
/** /**