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

client pool

This commit is contained in:
Leibale
2023-09-04 17:26:48 -04:00
parent c7a03acfd3
commit 3895eb5926
15 changed files with 565 additions and 352 deletions

View File

@@ -1,4 +1,4 @@
import { RedisClientOptions } from '../client';
import { RedisClientOptions, RedisClientType } from '../client';
import { CommandOptions } from '../client/commands-queue';
import { Command, CommandArguments, CommanderConfig, CommandPolicies, CommandWithPoliciesSignature, TypeMapping, RedisArgument, RedisFunction, RedisFunctions, RedisModules, RedisScript, RedisScripts, ReplyUnion, RespVersions } from '../RESP/types';
import COMMANDS from '../commands';
@@ -224,8 +224,8 @@ export default class RedisCluster<
BaseClass: RedisCluster,
commands: COMMANDS,
createCommand: RedisCluster._createCommand,
createFunctionCommand: RedisCluster._createFunctionCommand,
createModuleCommand: RedisCluster._createModuleCommand,
createFunctionCommand: RedisCluster._createFunctionCommand,
createScriptCommand: RedisCluster._createScriptCommand,
config
});
@@ -233,8 +233,7 @@ export default class RedisCluster<
Cluster.prototype.Multi = RedisClusterMultiCommand.extend(config);
return (options?: Omit<RedisClusterOptions, keyof Exclude<typeof config, undefined>>) => {
// returning a proxy of the client to prevent the namespaces.self to leak between proxies
// namespaces will be bootstraped on first access per proxy
// returning a "proxy" to prevent the namespaces.self to leak between "proxies"
return Object.create(new Cluster(options)) as RedisClusterType<M, F, S, RESP, TYPE_MAPPING, POLICIES>;
};
}
@@ -388,21 +387,17 @@ export default class RedisCluster<
return this._commandOptionsProxy('policies', policies);
}
async sendCommand<T = ReplyUnion>(
async #execute<T>(
firstKey: RedisArgument | undefined,
isReadonly: boolean | undefined,
args: CommandArguments,
options?: ClusterCommandOptions,
deafultPolicies?: CommandPolicies
fn: (client: RedisClientType<M, F, S, RESP>) => Promise<T>
): Promise<T> {
// const requestPolicy = options?.policies?.request ?? deafultPolicies?.request,
// responsePolicy = options?.policies?.response ?? deafultPolicies?.response;
const maxCommandRedirections = this._options.maxCommandRedirections ?? 16;
let client = await this._slots.getClient(firstKey, isReadonly);
for (let i = 0; ; i++) {
let client = await this._slots.getClient(firstKey, isReadonly),
i = 0;
while (true) {
try {
return await client.sendCommand<T>(args, options);
return await fn(client);
} catch (err) {
// TODO: error class
if (++i > maxCommandRedirections || !(err instanceof Error)) {
@@ -424,39 +419,69 @@ export default class RedisCluster<
await redirectTo.asking();
client = redirectTo;
continue;
} else if (err.message.startsWith('MOVED')) {
}
if (err.message.startsWith('MOVED')) {
await this._slots.rediscover(client);
client = await this._slots.getClient(firstKey, isReadonly);
continue;
}
throw err;
}
}
}
}
/**
* @internal
*/
async executePipeline(
async sendCommand<T = ReplyUnion>(
firstKey: RedisArgument | undefined,
isReadonly: boolean | undefined,
commands: Array<RedisMultiQueuedCommand>
args: CommandArguments,
options?: ClusterCommandOptions,
defaultPolicies?: CommandPolicies
): Promise<T> {
return this.#execute(
firstKey,
isReadonly,
client => client.sendCommand(args, options)
);
}
executeScript(
script: RedisScript,
firstKey: RedisArgument | undefined,
isReadonly: boolean | undefined,
args: Array<RedisArgument>,
options?: CommandOptions
) {
const client = await this._slots.getClient(firstKey, isReadonly);
return client.executePipeline(commands);
return this.#execute(
firstKey,
isReadonly,
client => client.executeScript(script, args, options)
);
}
/**
* @internal
*/
async executeMulti(
async _executePipeline(
firstKey: RedisArgument | undefined,
isReadonly: boolean | undefined,
commands: Array<RedisMultiQueuedCommand>
) {
const client = await this._slots.getClient(firstKey, isReadonly);
return client.executeMulti(commands);
return client._executePipeline(commands);
}
/**
* @internal
*/
async _executeMulti(
firstKey: RedisArgument | undefined,
isReadonly: boolean | undefined,
commands: Array<RedisMultiQueuedCommand>
) {
const client = await this._slots.getClient(firstKey, isReadonly);
return client._executeMulti(commands);
}
MULTI(routing?: RedisArgument): RedisClusterMultiCommandType<[], M, F, S, RESP, TYPE_MAPPING> {

View File

@@ -213,7 +213,7 @@ export default class RedisClusterMultiCommand<REPLIES = []> {
if (execAsPipeline) return this.execAsPipeline<T>();
return this._multi.transformReplies(
await this._cluster.executeMulti(
await this._cluster._executeMulti(
this._firstKey,
this._isReadonly,
this._multi.queue
@@ -231,7 +231,7 @@ export default class RedisClusterMultiCommand<REPLIES = []> {
if (this._multi.queue.length === 0) return [] as MultiReplyType<T, REPLIES>;
return this._multi.transformReplies(
await this._cluster.executePipeline(
await this._cluster._executePipeline(
this._firstKey,
this._isReadonly,
this._multi.queue