1
0
mirror of https://github.com/redis/node-redis.git synced 2025-08-06 02:15:48 +03:00

fix(cluster): replace native private with _ (#2971)

Private class fields (e.g., #execute) cause runtime errors when accessed from contexts where `this` is not the exact instance, triggering “Receiver must be an instance of class RedisCluster”.

fixes #2967
This commit is contained in:
Nikolay Karadzhov
2025-05-22 10:35:16 +03:00
committed by GitHub
parent 9ea260f0f9
commit 27537b0ab7

View File

@@ -188,7 +188,7 @@ export default class RedisCluster<
const parser = new BasicCommandParser();
command.parseCommand(parser, ...args);
return this._self.#execute(
return this._self._execute(
parser.firstKey,
command.IS_READ_ONLY,
this._commandOptions,
@@ -204,7 +204,7 @@ export default class RedisCluster<
const parser = new BasicCommandParser();
command.parseCommand(parser, ...args);
return this._self.#execute(
return this._self._execute(
parser.firstKey,
command.IS_READ_ONLY,
this._self._commandOptions,
@@ -222,7 +222,7 @@ export default class RedisCluster<
parser.push(...prefix);
fn.parseCommand(parser, ...args);
return this._self.#execute(
return this._self._execute(
parser.firstKey,
fn.IS_READ_ONLY,
this._self._commandOptions,
@@ -240,7 +240,7 @@ export default class RedisCluster<
parser.push(...prefix);
script.parseCommand(parser, ...args);
return this._self.#execute(
return this._self._execute(
parser.firstKey,
script.IS_READ_ONLY,
this._commandOptions,
@@ -293,9 +293,9 @@ export default class RedisCluster<
return RedisCluster.factory(options)(options);
}
readonly #options: RedisClusterOptions<M, F, S, RESP, TYPE_MAPPING/*, POLICIES*/>;
readonly _options: RedisClusterOptions<M, F, S, RESP, TYPE_MAPPING/*, POLICIES*/>;
readonly #slots: RedisClusterSlots<M, F, S, RESP, TYPE_MAPPING>;
readonly _slots: RedisClusterSlots<M, F, S, RESP, TYPE_MAPPING>;
private _self = this;
private _commandOptions?: ClusterCommandOptions<TYPE_MAPPING/*, POLICIES*/>;
@@ -305,11 +305,11 @@ export default class RedisCluster<
* Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific node (master or replica).
*/
get slots() {
return this._self.#slots.slots;
return this._self._slots.slots;
}
get clientSideCache() {
return this._self.#slots.clientSideCache;
return this._self._slots.clientSideCache;
}
/**
@@ -317,7 +317,7 @@ export default class RedisCluster<
* Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific master node.
*/
get masters() {
return this._self.#slots.masters;
return this._self._slots.masters;
}
/**
@@ -325,7 +325,7 @@ export default class RedisCluster<
* Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific replica node.
*/
get replicas() {
return this._self.#slots.replicas;
return this._self._slots.replicas;
}
/**
@@ -333,25 +333,25 @@ export default class RedisCluster<
* Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific node (master or replica).
*/
get nodeByAddress() {
return this._self.#slots.nodeByAddress;
return this._self._slots.nodeByAddress;
}
/**
* The current pub/sub node.
*/
get pubSubNode() {
return this._self.#slots.pubSubNode;
return this._self._slots.pubSubNode;
}
get isOpen() {
return this._self.#slots.isOpen;
return this._self._slots.isOpen;
}
constructor(options: RedisClusterOptions<M, F, S, RESP, TYPE_MAPPING/*, POLICIES*/>) {
super();
this.#options = options;
this.#slots = new RedisClusterSlots(options, this.emit.bind(this));
this._options = options;
this._slots = new RedisClusterSlots(options, this.emit.bind(this));
if (options?.commandOptions) {
this._commandOptions = options.commandOptions;
@@ -366,14 +366,14 @@ export default class RedisCluster<
_TYPE_MAPPING extends TypeMapping = TYPE_MAPPING
>(overrides?: Partial<RedisClusterOptions<_M, _F, _S, _RESP, _TYPE_MAPPING>>) {
return new (Object.getPrototypeOf(this).constructor)({
...this._self.#options,
...this._self._options,
commandOptions: this._commandOptions,
...overrides
}) as RedisClusterType<_M, _F, _S, _RESP, _TYPE_MAPPING>;
}
async connect() {
await this._self.#slots.connect();
await this._self._slots.connect();
return this as unknown as RedisClusterType<M, F, S, RESP, TYPE_MAPPING>;
}
@@ -429,7 +429,7 @@ export default class RedisCluster<
// return this._commandOptionsProxy('policies', policies);
// }
#handleAsk<T>(
_handleAsk<T>(
fn: (client: RedisClientType<M, F, S, RESP, TYPE_MAPPING>, opts?: ClusterCommandOptions) => Promise<T>
) {
return async (client: RedisClientType<M, F, S, RESP, TYPE_MAPPING>, options?: ClusterCommandOptions) => {
@@ -450,14 +450,14 @@ export default class RedisCluster<
};
}
async #execute<T>(
async _execute<T>(
firstKey: RedisArgument | undefined,
isReadonly: boolean | undefined,
options: ClusterCommandOptions | undefined,
fn: (client: RedisClientType<M, F, S, RESP, TYPE_MAPPING>, opts?: ClusterCommandOptions) => Promise<T>
): Promise<T> {
const maxCommandRedirections = this.#options.maxCommandRedirections ?? 16;
let client = await this.#slots.getClient(firstKey, isReadonly);
const maxCommandRedirections = this._options.maxCommandRedirections ?? 16;
let client = await this._slots.getClient(firstKey, isReadonly);
let i = 0;
let myFn = fn;
@@ -475,10 +475,10 @@ export default class RedisCluster<
if (err.message.startsWith('ASK')) {
const address = err.message.substring(err.message.lastIndexOf(' ') + 1);
let redirectTo = await this.#slots.getMasterByAddress(address);
let redirectTo = await this._slots.getMasterByAddress(address);
if (!redirectTo) {
await this.#slots.rediscover(client);
redirectTo = await this.#slots.getMasterByAddress(address);
await this._slots.rediscover(client);
redirectTo = await this._slots.getMasterByAddress(address);
}
if (!redirectTo) {
@@ -486,13 +486,13 @@ export default class RedisCluster<
}
client = redirectTo;
myFn = this.#handleAsk(fn);
myFn = this._handleAsk(fn);
continue;
}
if (err.message.startsWith('MOVED')) {
await this.#slots.rediscover(client);
client = await this.#slots.getClient(firstKey, isReadonly);
await this._slots.rediscover(client);
client = await this._slots.getClient(firstKey, isReadonly);
continue;
}
@@ -508,7 +508,7 @@ export default class RedisCluster<
options?: ClusterCommandOptions,
// defaultPolicies?: CommandPolicies
): Promise<T> {
return this._self.#execute(
return this._self._execute(
firstKey,
isReadonly,
options,
@@ -520,11 +520,11 @@ export default class RedisCluster<
type Multi = new (...args: ConstructorParameters<typeof RedisClusterMultiCommand>) => RedisClusterMultiCommandType<[], M, F, S, RESP, TYPE_MAPPING>;
return new ((this as any).Multi as Multi)(
async (firstKey, isReadonly, commands) => {
const client = await this._self.#slots.getClient(firstKey, isReadonly);
const client = await this._self._slots.getClient(firstKey, isReadonly);
return client._executeMulti(commands);
},
async (firstKey, isReadonly, commands) => {
const client = await this._self.#slots.getClient(firstKey, isReadonly);
const client = await this._self._slots.getClient(firstKey, isReadonly);
return client._executePipeline(commands);
},
routing,
@@ -539,7 +539,7 @@ export default class RedisCluster<
listener: PubSubListener<T>,
bufferMode?: T
) {
return (await this._self.#slots.getPubSubClient())
return (await this._self._slots.getPubSubClient())
.SUBSCRIBE(channels, listener, bufferMode);
}
@@ -550,7 +550,7 @@ export default class RedisCluster<
listener?: PubSubListener<boolean>,
bufferMode?: T
) {
return this._self.#slots.executeUnsubscribeCommand(client =>
return this._self._slots.executeUnsubscribeCommand(client =>
client.UNSUBSCRIBE(channels, listener, bufferMode)
);
}
@@ -562,7 +562,7 @@ export default class RedisCluster<
listener: PubSubListener<T>,
bufferMode?: T
) {
return (await this._self.#slots.getPubSubClient())
return (await this._self._slots.getPubSubClient())
.PSUBSCRIBE(patterns, listener, bufferMode);
}
@@ -573,7 +573,7 @@ export default class RedisCluster<
listener?: PubSubListener<T>,
bufferMode?: T
) {
return this._self.#slots.executeUnsubscribeCommand(client =>
return this._self._slots.executeUnsubscribeCommand(client =>
client.PUNSUBSCRIBE(patterns, listener, bufferMode)
);
}
@@ -585,9 +585,9 @@ export default class RedisCluster<
listener: PubSubListener<T>,
bufferMode?: T
) {
const maxCommandRedirections = this._self.#options.maxCommandRedirections ?? 16,
const maxCommandRedirections = this._self._options.maxCommandRedirections ?? 16,
firstChannel = Array.isArray(channels) ? channels[0] : channels;
let client = await this._self.#slots.getShardedPubSubClient(firstChannel);
let client = await this._self._slots.getShardedPubSubClient(firstChannel);
for (let i = 0; ; i++) {
try {
return await client.SSUBSCRIBE(channels, listener, bufferMode);
@@ -597,8 +597,8 @@ export default class RedisCluster<
}
if (err.message.startsWith('MOVED')) {
await this._self.#slots.rediscover(client);
client = await this._self.#slots.getShardedPubSubClient(firstChannel);
await this._self._slots.rediscover(client);
client = await this._self._slots.getShardedPubSubClient(firstChannel);
continue;
}
@@ -614,7 +614,7 @@ export default class RedisCluster<
listener?: PubSubListener<T>,
bufferMode?: T
) {
return this._self.#slots.executeShardedUnsubscribeCommand(
return this._self._slots.executeShardedUnsubscribeCommand(
Array.isArray(channels) ? channels[0] : channels,
client => client.SUNSUBSCRIBE(channels, listener, bufferMode)
);
@@ -626,28 +626,28 @@ export default class RedisCluster<
* @deprecated Use `close` instead.
*/
quit() {
return this._self.#slots.quit();
return this._self._slots.quit();
}
/**
* @deprecated Use `destroy` instead.
*/
disconnect() {
return this._self.#slots.disconnect();
return this._self._slots.disconnect();
}
close() {
this._self.#slots.clientSideCache?.onPoolClose();
return this._self.#slots.close();
this._self._slots.clientSideCache?.onPoolClose();
return this._self._slots.close();
}
destroy() {
this._self.#slots.clientSideCache?.onPoolClose();
return this._self.#slots.destroy();
this._self._slots.clientSideCache?.onPoolClose();
return this._self._slots.destroy();
}
nodeClient(node: ShardNode<M, F, S, RESP, TYPE_MAPPING>) {
return this._self.#slots.nodeClient(node);
return this._self._slots.nodeClient(node);
}
/**
@@ -655,7 +655,7 @@ export default class RedisCluster<
* Userful for running "forward" commands (like PUBLISH) on a random node.
*/
getRandomNode() {
return this._self.#slots.getRandomNode();
return this._self._slots.getRandomNode();
}
/**
@@ -663,7 +663,7 @@ export default class RedisCluster<
* Useful for running readonly commands on a slot.
*/
getSlotRandomNode(slot: number) {
return this._self.#slots.getSlotRandomNode(slot);
return this._self._slots.getSlotRandomNode(slot);
}
/**