You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-07 13:22:56 +03:00
cluster multi
This commit is contained in:
@@ -71,6 +71,13 @@ legacyClient.set('key', 'value', (err, reply) => {
|
|||||||
TODO
|
TODO
|
||||||
The `isolationPool` has been moved to it's on class `ClientPool`. You can create pool from a client using `client.createPool()`.
|
The `isolationPool` has been moved to it's on class `ClientPool`. You can create pool from a client using `client.createPool()`.
|
||||||
|
|
||||||
|
## Cluster MULTI
|
||||||
|
|
||||||
|
Cluster MULTI supports readonly/replicas
|
||||||
|
`cluster.multi.addCommand` now requires `isReadonly` as the second argument, to match `cluster.sendCommand`
|
||||||
|
|
||||||
|
TODO
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
Some command arguments/replies have changed to align more closely to data types returned by Redis:
|
Some command arguments/replies have changed to align more closely to data types returned by Redis:
|
||||||
|
@@ -3,7 +3,7 @@ import RedisSocket, { RedisSocketOptions, RedisTlsSocketOptions } from './socket
|
|||||||
import RedisCommandsQueue, { QueueCommandOptions } from './commands-queue';
|
import RedisCommandsQueue, { QueueCommandOptions } from './commands-queue';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import { attachConfig, functionArgumentsPrefix, getTransformReply, scriptArgumentsPrefix } from '../commander';
|
import { attachConfig, functionArgumentsPrefix, getTransformReply, scriptArgumentsPrefix } from '../commander';
|
||||||
import { ClientClosedError, ClientOfflineError, DisconnectsClientError } from '../errors';
|
import { ClientClosedError, ClientOfflineError, DisconnectsClientError, WatchError } from '../errors';
|
||||||
import { URL } from 'url';
|
import { URL } from 'url';
|
||||||
import { TcpSocketConnectOpts } from 'net';
|
import { TcpSocketConnectOpts } from 'net';
|
||||||
import { PubSubType, PubSubListener, PubSubTypeListeners, ChannelListeners } from './pub-sub';
|
import { PubSubType, PubSubListener, PubSubTypeListeners, ChannelListeners } from './pub-sub';
|
||||||
@@ -692,7 +692,7 @@ export default class RedisClient<
|
|||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
async executePipeline(commands: Array<RedisMultiQueuedCommand>) {
|
executePipeline(commands: Array<RedisMultiQueuedCommand>) {
|
||||||
if (!this._socket.isOpen) {
|
if (!this._socket.isOpen) {
|
||||||
return Promise.reject(new ClientClosedError());
|
return Promise.reject(new ClientClosedError());
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import COMMANDS from '../commands';
|
import COMMANDS from '../commands';
|
||||||
import RedisMultiCommand, { RedisMultiQueuedCommand } from '../multi-command';
|
import RedisMultiCommand, { MULTI_REPLY, MultiReply, MultiReplyType } from '../multi-command';
|
||||||
import { ReplyWithFlags, CommandReply, Command, CommandArguments, CommanderConfig, RedisFunctions, RedisModules, RedisScripts, RespVersions, TransformReply, RedisScript, RedisFunction, Flags, ReplyUnion } from '../RESP/types';
|
import { ReplyWithFlags, CommandReply, Command, CommandArguments, CommanderConfig, RedisFunctions, RedisModules, RedisScripts, RespVersions, TransformReply, RedisScript, RedisFunction, Flags, ReplyUnion } from '../RESP/types';
|
||||||
import { attachConfig, functionArgumentsPrefix, getTransformReply } from '../commander';
|
import { attachConfig, functionArgumentsPrefix, getTransformReply } from '../commander';
|
||||||
import { RedisClientType } from '.';
|
import { RedisClientType } from '.';
|
||||||
@@ -84,64 +84,50 @@ export type RedisClientMultiCommandType<
|
|||||||
WithScripts<REPLIES, M, F, S, RESP, FLAGS>
|
WithScripts<REPLIES, M, F, S, RESP, FLAGS>
|
||||||
);
|
);
|
||||||
|
|
||||||
type MULTI_REPLY = {
|
export default class RedisClientMultiCommand<REPLIES = []> {
|
||||||
GENERIC: 'generic';
|
private static _createCommand(command: Command, resp: RespVersions) {
|
||||||
TYPED: 'typed';
|
|
||||||
};
|
|
||||||
|
|
||||||
type MultiReply = MULTI_REPLY[keyof MULTI_REPLY];
|
|
||||||
|
|
||||||
type ReplyType<T extends MultiReply, REPLIES> = T extends MULTI_REPLY['TYPED'] ? REPLIES : Array<unknown>;
|
|
||||||
|
|
||||||
export type RedisClientMultiExecutor = (
|
|
||||||
queue: Array<RedisMultiQueuedCommand>,
|
|
||||||
selectedDB?: number,
|
|
||||||
chainId?: symbol
|
|
||||||
) => Promise<Array<unknown>>;
|
|
||||||
|
|
||||||
export default class RedisClientMultiCommand<REPLIES = []> extends RedisMultiCommand {
|
|
||||||
static #createCommand(command: Command, resp: RespVersions) {
|
|
||||||
const transformReply = getTransformReply(command, resp);
|
const transformReply = getTransformReply(command, resp);
|
||||||
return function (this: RedisClientMultiCommand) {
|
return function (this: RedisClientMultiCommand, ...args: Array<unknown>) {
|
||||||
return this.addCommand(
|
return this._multi.addCommand(
|
||||||
command.transformArguments.apply(undefined, arguments as any),
|
command.transformArguments(...args),
|
||||||
transformReply
|
transformReply
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static #createModuleCommand(command: Command, resp: RespVersions) {
|
private static _createModuleCommand(command: Command, resp: RespVersions) {
|
||||||
const transformReply = getTransformReply(command, resp);
|
const transformReply = getTransformReply(command, resp);
|
||||||
return function (this: { self: RedisClientMultiCommand }) {
|
return function (this: { self: RedisClientMultiCommand }, ...args: Array<unknown>) {
|
||||||
return this.self.addCommand(
|
return this.self._multi.addCommand(
|
||||||
command.transformArguments.apply(undefined, arguments as any),
|
command.transformArguments(...args),
|
||||||
transformReply
|
transformReply
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static #createFunctionCommand(name: string, fn: RedisFunction, resp: RespVersions) {
|
private static _createFunctionCommand(name: string, fn: RedisFunction, resp: RespVersions) {
|
||||||
const prefix = functionArgumentsPrefix(name, fn),
|
const prefix = functionArgumentsPrefix(name, fn),
|
||||||
transformReply = getTransformReply(fn, resp);
|
transformReply = getTransformReply(fn, resp);
|
||||||
return function (this: { self: RedisClientMultiCommand }) {
|
return function (this: { self: RedisClientMultiCommand }, ...args: Array<unknown>) {
|
||||||
const fnArgs = fn.transformArguments.apply(undefined, arguments as any),
|
const fnArgs = fn.transformArguments(...args),
|
||||||
args: CommandArguments = prefix.concat(fnArgs);
|
redisArgs: CommandArguments = prefix.concat(fnArgs);
|
||||||
args.preserve = fnArgs.preserve;
|
redisArgs.preserve = fnArgs.preserve;
|
||||||
return this.self.addCommand(
|
return this.self._multi.addCommand(
|
||||||
args,
|
redisArgs,
|
||||||
transformReply
|
transformReply
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static #createScriptCommand(script: RedisScript, resp: RespVersions) {
|
private static _createScriptCommand(script: RedisScript, resp: RespVersions) {
|
||||||
const transformReply = getTransformReply(script, resp);
|
const transformReply = getTransformReply(script, resp);
|
||||||
return function (this: RedisClientMultiCommand) {
|
return function (this: RedisClientMultiCommand, ...args: Array<unknown>) {
|
||||||
return this.addScript(
|
this._multi.addScript(
|
||||||
script,
|
script,
|
||||||
script.transformArguments.apply(undefined, arguments as any),
|
script.transformArguments(...args),
|
||||||
transformReply
|
transformReply
|
||||||
);
|
);
|
||||||
|
return this;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,35 +140,36 @@ export default class RedisClientMultiCommand<REPLIES = []> extends RedisMultiCom
|
|||||||
return attachConfig({
|
return attachConfig({
|
||||||
BaseClass: RedisClientMultiCommand,
|
BaseClass: RedisClientMultiCommand,
|
||||||
commands: COMMANDS,
|
commands: COMMANDS,
|
||||||
createCommand: RedisClientMultiCommand.#createCommand,
|
createCommand: RedisClientMultiCommand._createCommand,
|
||||||
createModuleCommand: RedisClientMultiCommand.#createModuleCommand,
|
createModuleCommand: RedisClientMultiCommand._createModuleCommand,
|
||||||
createFunctionCommand: RedisClientMultiCommand.#createFunctionCommand,
|
createFunctionCommand: RedisClientMultiCommand._createFunctionCommand,
|
||||||
createScriptCommand: RedisClientMultiCommand.#createScriptCommand,
|
createScriptCommand: RedisClientMultiCommand._createScriptCommand,
|
||||||
config
|
config
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly #client: RedisClientType;
|
private readonly _multi = new RedisMultiCommand();
|
||||||
#selectedDB?: number;
|
private readonly _client: RedisClientType;
|
||||||
|
private _selectedDB?: number;
|
||||||
|
|
||||||
constructor(client: RedisClientType) {
|
constructor(client: RedisClientType) {
|
||||||
super();
|
this._client = client;
|
||||||
this.#client = client;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SELECT(db: number, transformReply?: TransformReply): this {
|
SELECT(db: number, transformReply?: TransformReply): this {
|
||||||
this.#selectedDB = db;
|
this._selectedDB = db;
|
||||||
return this.addCommand(['SELECT', db.toString()], transformReply);
|
this._multi.addCommand(['SELECT', db.toString()], transformReply);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
select = this.SELECT;
|
select = this.SELECT;
|
||||||
|
|
||||||
async exec<T extends MultiReply = MULTI_REPLY['GENERIC']>(execAsPipeline = false): Promise<ReplyType<T, REPLIES>> {
|
async exec<T extends MultiReply = MULTI_REPLY['GENERIC']>(execAsPipeline = false): Promise<MultiReplyType<T, REPLIES>> {
|
||||||
if (execAsPipeline) return this.execAsPipeline<T>();
|
if (execAsPipeline) return this.execAsPipeline<T>();
|
||||||
|
|
||||||
return this.transformReplies(
|
return this._multi.transformReplies(
|
||||||
await this.#client.executeMulti(this.queue, this.#selectedDB)
|
await this._client.executeMulti(this._multi.queue, this._selectedDB)
|
||||||
) as ReplyType<T, REPLIES>;
|
) as MultiReplyType<T, REPLIES>;
|
||||||
}
|
}
|
||||||
|
|
||||||
EXEC = this.exec;
|
EXEC = this.exec;
|
||||||
@@ -191,12 +178,12 @@ export default class RedisClientMultiCommand<REPLIES = []> extends RedisMultiCom
|
|||||||
return this.exec<MULTI_REPLY['TYPED']>(execAsPipeline);
|
return this.exec<MULTI_REPLY['TYPED']>(execAsPipeline);
|
||||||
}
|
}
|
||||||
|
|
||||||
async execAsPipeline<T extends MultiReply = MULTI_REPLY['GENERIC']>(): Promise<ReplyType<T, REPLIES>> {
|
async execAsPipeline<T extends MultiReply = MULTI_REPLY['GENERIC']>(): Promise<MultiReplyType<T, REPLIES>> {
|
||||||
if (this.queue.length === 0) return [] as ReplyType<T, REPLIES>;
|
if (this._multi.queue.length === 0) return [] as MultiReplyType<T, REPLIES>;
|
||||||
|
|
||||||
return this.transformReplies(
|
return this._multi.transformReplies(
|
||||||
await this.#client.executePipeline(this.queue)
|
await this._client.executePipeline(this._multi.queue)
|
||||||
) as ReplyType<T, REPLIES>;
|
) as MultiReplyType<T, REPLIES>;
|
||||||
}
|
}
|
||||||
|
|
||||||
execAsPipelineTyped() {
|
execAsPipelineTyped() {
|
||||||
|
@@ -1,11 +1,11 @@
|
|||||||
import { ClientCommandOptions, RedisClientOptions, RedisClientType } from '../client';
|
import { ClientCommandOptions, RedisClientOptions } from '../client';
|
||||||
import { Command, CommandArguments, CommanderConfig, CommandPolicies, CommandSignature, CommandWithPoliciesSignature, Flags, RedisArgument, RedisFunction, RedisFunctions, RedisModules, RedisScript, RedisScripts, ReplyUnion, RespVersions, TransformReply } from '../RESP/types';
|
import { Command, CommandArguments, CommanderConfig, CommandPolicies, CommandWithPoliciesSignature, Flags, RedisArgument, RedisFunction, RedisFunctions, RedisModules, RedisScript, RedisScripts, ReplyUnion, RespVersions } from '../RESP/types';
|
||||||
import COMMANDS from '../commands';
|
import COMMANDS from '../commands';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import { attachConfig, functionArgumentsPrefix, getTransformReply, scriptArgumentsPrefix } from '../commander';
|
import { attachConfig, functionArgumentsPrefix, getTransformReply, scriptArgumentsPrefix } from '../commander';
|
||||||
import RedisClusterSlots, { NodeAddressMap, ShardNode } from './cluster-slots';
|
import RedisClusterSlots, { NodeAddressMap, ShardNode } from './cluster-slots';
|
||||||
// import RedisClusterMultiCommand, { InstantiableRedisClusterMultiCommandType, RedisClusterMultiCommandType } from './multi-command';
|
import RedisClusterMultiCommand, { RedisClusterMultiCommandType } from './multi-command';
|
||||||
// import { RedisMultiQueuedCommand } from '../multi-command';
|
import { RedisMultiQueuedCommand } from '../multi-command';
|
||||||
import { PubSubListener } from '../client/pub-sub';
|
import { PubSubListener } from '../client/pub-sub';
|
||||||
import { ErrorReply } from '../errors';
|
import { ErrorReply } from '../errors';
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ export default class RedisCluster<
|
|||||||
FLAGS extends Flags,
|
FLAGS extends Flags,
|
||||||
POLICIES extends CommandPolicies
|
POLICIES extends CommandPolicies
|
||||||
> extends EventEmitter {
|
> extends EventEmitter {
|
||||||
private static _extractFirstKey<C extends Command>(
|
static extractFirstKey<C extends Command>(
|
||||||
command: C,
|
command: C,
|
||||||
args: Parameters<C['transformArguments']>,
|
args: Parameters<C['transformArguments']>,
|
||||||
redisArgs: Array<RedisArgument>
|
redisArgs: Array<RedisArgument>
|
||||||
@@ -101,46 +101,46 @@ export default class RedisCluster<
|
|||||||
|
|
||||||
private static _createCommand(command: Command, resp: RespVersions) {
|
private static _createCommand(command: Command, resp: RespVersions) {
|
||||||
const transformReply = getTransformReply(command, resp);
|
const transformReply = getTransformReply(command, resp);
|
||||||
return async function (this: ProxyCluster) {
|
return async function (this: ProxyCluster, ...args: Array<unknown>) {
|
||||||
const args = command.transformArguments.apply(undefined, arguments as any),
|
const redisArgs = command.transformArguments(...args),
|
||||||
firstKey = RedisCluster._extractFirstKey(
|
firstKey = RedisCluster.extractFirstKey(
|
||||||
command,
|
command,
|
||||||
arguments as any,
|
args,
|
||||||
args
|
redisArgs
|
||||||
),
|
),
|
||||||
reply = await this.sendCommand(
|
reply = await this.sendCommand(
|
||||||
firstKey,
|
firstKey,
|
||||||
command.IS_READ_ONLY,
|
command.IS_READ_ONLY,
|
||||||
args,
|
redisArgs,
|
||||||
this.commandOptions,
|
this.commandOptions,
|
||||||
command.POLICIES
|
command.POLICIES
|
||||||
);
|
);
|
||||||
|
|
||||||
return transformReply ?
|
return transformReply ?
|
||||||
transformReply(reply, args.preserve) :
|
transformReply(reply, redisArgs.preserve) :
|
||||||
reply;
|
reply;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static _createModuleCommand(command: Command, resp: RespVersions) {
|
private static _createModuleCommand(command: Command, resp: RespVersions) {
|
||||||
const transformReply = getTransformReply(command, resp);
|
const transformReply = getTransformReply(command, resp);
|
||||||
return async function (this: NamespaceProxyCluster) {
|
return async function (this: NamespaceProxyCluster, ...args: Array<unknown>) {
|
||||||
const args = command.transformArguments.apply(undefined, arguments as any),
|
const redisArgs = command.transformArguments(...args),
|
||||||
firstKey = RedisCluster._extractFirstKey(
|
firstKey = RedisCluster.extractFirstKey(
|
||||||
command,
|
command,
|
||||||
arguments as any,
|
args,
|
||||||
args
|
redisArgs
|
||||||
),
|
),
|
||||||
reply = await this.self.sendCommand(
|
reply = await this.self.sendCommand(
|
||||||
firstKey,
|
firstKey,
|
||||||
command.IS_READ_ONLY,
|
command.IS_READ_ONLY,
|
||||||
args,
|
redisArgs,
|
||||||
this.self.commandOptions,
|
this.self.commandOptions,
|
||||||
command.POLICIES
|
command.POLICIES
|
||||||
);
|
);
|
||||||
|
|
||||||
return transformReply ?
|
return transformReply ?
|
||||||
transformReply(reply, args.preserve) :
|
transformReply(reply, redisArgs.preserve) :
|
||||||
reply;
|
reply;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -148,18 +148,18 @@ export default class RedisCluster<
|
|||||||
private static _createFunctionCommand(name: string, fn: RedisFunction, resp: RespVersions) {
|
private static _createFunctionCommand(name: string, fn: RedisFunction, resp: RespVersions) {
|
||||||
const prefix = functionArgumentsPrefix(name, fn),
|
const prefix = functionArgumentsPrefix(name, fn),
|
||||||
transformReply = getTransformReply(fn, resp);
|
transformReply = getTransformReply(fn, resp);
|
||||||
return async function (this: NamespaceProxyCluster) {
|
return async function (this: NamespaceProxyCluster, ...args: Array<unknown>) {
|
||||||
const fnArgs = fn.transformArguments.apply(undefined, arguments as any),
|
const fnArgs = fn.transformArguments(...args),
|
||||||
args = prefix.concat(fnArgs),
|
redisArgs = prefix.concat(fnArgs),
|
||||||
firstKey = RedisCluster._extractFirstKey(
|
firstKey = RedisCluster.extractFirstKey(
|
||||||
fn,
|
fn,
|
||||||
arguments as any,
|
fnArgs,
|
||||||
args
|
redisArgs
|
||||||
),
|
),
|
||||||
reply = await this.self.sendCommand(
|
reply = await this.self.sendCommand(
|
||||||
firstKey,
|
firstKey,
|
||||||
fn.IS_READ_ONLY,
|
fn.IS_READ_ONLY,
|
||||||
args,
|
redisArgs,
|
||||||
this.self.commandOptions,
|
this.self.commandOptions,
|
||||||
fn.POLICIES
|
fn.POLICIES
|
||||||
);
|
);
|
||||||
@@ -173,18 +173,18 @@ export default class RedisCluster<
|
|||||||
private static _createScriptCommand(script: RedisScript, resp: RespVersions) {
|
private static _createScriptCommand(script: RedisScript, resp: RespVersions) {
|
||||||
const prefix = scriptArgumentsPrefix(script),
|
const prefix = scriptArgumentsPrefix(script),
|
||||||
transformReply = getTransformReply(script, resp);
|
transformReply = getTransformReply(script, resp);
|
||||||
return async function (this: ProxyCluster) {
|
return async function (this: ProxyCluster, ...args: Array<unknown>) {
|
||||||
const scriptArgs = script.transformArguments.apply(undefined, arguments as any),
|
const scriptArgs = script.transformArguments(...args),
|
||||||
args = prefix.concat(scriptArgs),
|
redisArgs = prefix.concat(scriptArgs),
|
||||||
firstKey = RedisCluster._extractFirstKey(
|
firstKey = RedisCluster.extractFirstKey(
|
||||||
script,
|
script,
|
||||||
arguments as any,
|
scriptArgs,
|
||||||
args
|
redisArgs
|
||||||
),
|
),
|
||||||
reply = await this.sendCommand(
|
reply = await this.sendCommand(
|
||||||
firstKey,
|
firstKey,
|
||||||
script.IS_READ_ONLY,
|
script.IS_READ_ONLY,
|
||||||
args,
|
redisArgs,
|
||||||
this.commandOptions,
|
this.commandOptions,
|
||||||
script.POLICIES
|
script.POLICIES
|
||||||
);
|
);
|
||||||
@@ -211,7 +211,7 @@ export default class RedisCluster<
|
|||||||
config
|
config
|
||||||
});
|
});
|
||||||
|
|
||||||
// Client.prototype.Multi = RedisClientMultiCommand.extend(config);
|
Cluster.prototype.Multi = RedisClusterMultiCommand.extend(config);
|
||||||
|
|
||||||
return (options?: Omit<RedisClusterOptions, keyof Exclude<typeof config, undefined>>) => {
|
return (options?: Omit<RedisClusterOptions, keyof Exclude<typeof config, undefined>>) => {
|
||||||
// returning a proxy of the client to prevent the namespaces.self to leak between proxies
|
// returning a proxy of the client to prevent the namespaces.self to leak between proxies
|
||||||
@@ -280,8 +280,6 @@ export default class RedisCluster<
|
|||||||
return this._slots.pubSubNode;
|
return this._slots.pubSubNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
// readonly #Multi: InstantiableRedisClusterMultiCommandType<M, F, S>;
|
|
||||||
|
|
||||||
get isOpen() {
|
get isOpen() {
|
||||||
return this._slots.isOpen;
|
return this._slots.isOpen;
|
||||||
}
|
}
|
||||||
@@ -291,7 +289,6 @@ export default class RedisCluster<
|
|||||||
|
|
||||||
this._options = options;
|
this._options = options;
|
||||||
this._slots = new RedisClusterSlots(options, this.emit.bind(this));
|
this._slots = new RedisClusterSlots(options, this.emit.bind(this));
|
||||||
// this.#Multi = RedisClusterMultiCommand.extend(options);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
duplicate(overrides?: Partial<RedisClusterOptions<M, F, S>>): RedisClusterType<M, F, S> {
|
duplicate(overrides?: Partial<RedisClusterOptions<M, F, S>>): RedisClusterType<M, F, S> {
|
||||||
@@ -400,20 +397,38 @@ export default class RedisCluster<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MULTI(routing?: RedisCommandArgument): RedisClusterMultiCommandType<M, F, S> {
|
/**
|
||||||
// return new this.#Multi(
|
* @internal
|
||||||
// (commands: Array<RedisMultiQueuedCommand>, firstKey?: RedisCommandArgument, chainId?: symbol) => {
|
*/
|
||||||
// return this.#execute(
|
async executePipeline(
|
||||||
// firstKey,
|
firstKey: RedisArgument | undefined,
|
||||||
// false,
|
isReadonly: boolean | undefined,
|
||||||
// client => client.multiExecutor(commands, undefined, chainId)
|
commands: Array<RedisMultiQueuedCommand>
|
||||||
// );
|
) {
|
||||||
// },
|
const client = await this._slots.getClient(firstKey, isReadonly);
|
||||||
// routing
|
return client.executePipeline(commands);
|
||||||
// );
|
}
|
||||||
// }
|
|
||||||
|
|
||||||
// multi = this.MULTI;
|
/**
|
||||||
|
* @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, FLAGS> {
|
||||||
|
return new (this as any).Multi(
|
||||||
|
this,
|
||||||
|
routing
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
multi = this.MULTI;
|
||||||
|
|
||||||
async SUBSCRIBE<T extends boolean = false>(
|
async SUBSCRIBE<T extends boolean = false>(
|
||||||
channels: string | Array<string>,
|
channels: string | Array<string>,
|
||||||
|
@@ -1,141 +1,245 @@
|
|||||||
// import COMMANDS from './commands';
|
import COMMANDS from '../commands';
|
||||||
// import { RedisCommand, RedisCommandArgument, RedisCommandArguments, RedisCommandRawReply, RedisFunctions, RedisModules, RedisExtensions, RedisScript, RedisScripts, ExcludeMappedString, RedisFunction } from '../commands';
|
import RedisMultiCommand, { MULTI_REPLY, MultiReply, MultiReplyType } from '../multi-command';
|
||||||
// import RedisMultiCommand, { RedisMultiQueuedCommand } from '../multi-command';
|
import { ReplyWithFlags, CommandReply, Command, CommandArguments, CommanderConfig, RedisFunctions, RedisModules, RedisScripts, RespVersions, TransformReply, RedisScript, RedisFunction, Flags, ReplyUnion, RedisArgument } from '../RESP/types';
|
||||||
// import { attachCommands, attachExtensions } from '../commander';
|
import { attachConfig, functionArgumentsPrefix, getTransformReply } from '../commander';
|
||||||
// import RedisCluster from '.';
|
import RedisCluster, { RedisClusterType } from '.';
|
||||||
|
|
||||||
// type RedisClusterMultiCommandSignature<
|
type CommandSignature<
|
||||||
// C extends RedisCommand,
|
REPLIES extends Array<unknown>,
|
||||||
// M extends RedisModules,
|
C extends Command,
|
||||||
// F extends RedisFunctions,
|
M extends RedisModules,
|
||||||
// S extends RedisScripts
|
F extends RedisFunctions,
|
||||||
// > = (...args: Parameters<C['transformArguments']>) => RedisClusterMultiCommandType<M, F, S>;
|
S extends RedisScripts,
|
||||||
|
RESP extends RespVersions,
|
||||||
|
FLAGS extends Flags
|
||||||
|
> = (...args: Parameters<C['transformArguments']>) => RedisClusterMultiCommandType<
|
||||||
|
[...REPLIES, ReplyWithFlags<CommandReply<C, RESP>, FLAGS>],
|
||||||
|
M,
|
||||||
|
F,
|
||||||
|
S,
|
||||||
|
RESP,
|
||||||
|
FLAGS
|
||||||
|
>;
|
||||||
|
|
||||||
// type WithCommands<
|
type WithCommands<
|
||||||
// M extends RedisModules,
|
REPLIES extends Array<unknown>,
|
||||||
// F extends RedisFunctions,
|
M extends RedisModules,
|
||||||
// S extends RedisScripts
|
F extends RedisFunctions,
|
||||||
// > = {
|
S extends RedisScripts,
|
||||||
// [P in keyof typeof COMMANDS]: RedisClusterMultiCommandSignature<(typeof COMMANDS)[P], M, F, S>;
|
RESP extends RespVersions,
|
||||||
// };
|
FLAGS extends Flags
|
||||||
|
> = {
|
||||||
|
[P in keyof typeof COMMANDS]: CommandSignature<REPLIES, (typeof COMMANDS)[P], M, F, S, RESP, FLAGS>;
|
||||||
|
};
|
||||||
|
|
||||||
// type WithModules<
|
type WithModules<
|
||||||
// M extends RedisModules,
|
REPLIES extends Array<unknown>,
|
||||||
// F extends RedisFunctions,
|
M extends RedisModules,
|
||||||
// S extends RedisScripts
|
F extends RedisFunctions,
|
||||||
// > = {
|
S extends RedisScripts,
|
||||||
// [P in keyof M as ExcludeMappedString<P>]: {
|
RESP extends RespVersions,
|
||||||
// [C in keyof M[P] as ExcludeMappedString<C>]: RedisClusterMultiCommandSignature<M[P][C], M, F, S>;
|
FLAGS extends Flags
|
||||||
// };
|
> = {
|
||||||
// };
|
[P in keyof M]: {
|
||||||
|
[C in keyof M[P]]: CommandSignature<REPLIES, M[P][C], M, F, S, RESP, FLAGS>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// type WithFunctions<
|
type WithFunctions<
|
||||||
// M extends RedisModules,
|
REPLIES extends Array<unknown>,
|
||||||
// F extends RedisFunctions,
|
M extends RedisModules,
|
||||||
// S extends RedisScripts
|
F extends RedisFunctions,
|
||||||
// > = {
|
S extends RedisScripts,
|
||||||
// [P in keyof F as ExcludeMappedString<P>]: {
|
RESP extends RespVersions,
|
||||||
// [FF in keyof F[P] as ExcludeMappedString<FF>]: RedisClusterMultiCommandSignature<F[P][FF], M, F, S>;
|
FLAGS extends Flags
|
||||||
// };
|
> = {
|
||||||
// };
|
[L in keyof F]: {
|
||||||
|
[C in keyof F[L]]: CommandSignature<REPLIES, F[L][C], M, F, S, RESP, FLAGS>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
// type WithScripts<
|
type WithScripts<
|
||||||
// M extends RedisModules,
|
REPLIES extends Array<unknown>,
|
||||||
// F extends RedisFunctions,
|
M extends RedisModules,
|
||||||
// S extends RedisScripts
|
F extends RedisFunctions,
|
||||||
// > = {
|
S extends RedisScripts,
|
||||||
// [P in keyof S as ExcludeMappedString<P>]: RedisClusterMultiCommandSignature<S[P], M, F, S>;
|
RESP extends RespVersions,
|
||||||
// };
|
FLAGS extends Flags
|
||||||
|
> = {
|
||||||
|
[P in keyof S]: CommandSignature<REPLIES, S[P], M, F, S, RESP, FLAGS>;
|
||||||
|
};
|
||||||
|
|
||||||
// export type RedisClusterMultiCommandType<
|
export type RedisClusterMultiCommandType<
|
||||||
// M extends RedisModules,
|
REPLIES extends Array<any>,
|
||||||
// F extends RedisFunctions,
|
M extends RedisModules,
|
||||||
// S extends RedisScripts
|
F extends RedisFunctions,
|
||||||
// > = RedisClusterMultiCommand & WithCommands<M, F, S> & WithModules<M, F, S> & WithFunctions<M, F, S> & WithScripts<M, F, S>;
|
S extends RedisScripts,
|
||||||
|
RESP extends RespVersions,
|
||||||
|
FLAGS extends Flags
|
||||||
|
> = (
|
||||||
|
RedisClusterMultiCommand<REPLIES> &
|
||||||
|
WithCommands<REPLIES, M, F, S, RESP, FLAGS> &
|
||||||
|
WithModules<REPLIES, M, F, S, RESP, FLAGS> &
|
||||||
|
WithFunctions<REPLIES, M, F, S, RESP, FLAGS> &
|
||||||
|
WithScripts<REPLIES, M, F, S, RESP, FLAGS>
|
||||||
|
);
|
||||||
|
|
||||||
// export type InstantiableRedisClusterMultiCommandType<
|
export default class RedisClusterMultiCommand<REPLIES = []> {
|
||||||
// M extends RedisModules,
|
private static _createCommand(command: Command, resp: RespVersions) {
|
||||||
// F extends RedisFunctions,
|
const transformReply = getTransformReply(command, resp);
|
||||||
// S extends RedisScripts
|
return function (this: RedisClusterMultiCommand, ...args: Array<unknown>) {
|
||||||
// > = new (...args: ConstructorParameters<typeof RedisClusterMultiCommand>) => RedisClusterMultiCommandType<M, F, S>;
|
const redisArgs = command.transformArguments(...args),
|
||||||
|
firstKey = RedisCluster.extractFirstKey(
|
||||||
|
command,
|
||||||
|
args,
|
||||||
|
redisArgs
|
||||||
|
);
|
||||||
|
return this.addCommand(
|
||||||
|
firstKey,
|
||||||
|
command.IS_READ_ONLY,
|
||||||
|
redisArgs,
|
||||||
|
transformReply
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// export type RedisClusterMultiExecutor = (queue: Array<RedisMultiQueuedCommand>, firstKey?: RedisCommandArgument, chainId?: symbol) => Promise<Array<RedisCommandRawReply>>;
|
private static _createModuleCommand(command: Command, resp: RespVersions) {
|
||||||
|
const transformReply = getTransformReply(command, resp);
|
||||||
|
return function (this: { self: RedisClusterMultiCommand }, ...args: Array<unknown>) {
|
||||||
|
const redisArgs = command.transformArguments(...args),
|
||||||
|
firstKey = RedisCluster.extractFirstKey(
|
||||||
|
command,
|
||||||
|
args,
|
||||||
|
redisArgs
|
||||||
|
);
|
||||||
|
return this.self.addCommand(
|
||||||
|
firstKey,
|
||||||
|
command.IS_READ_ONLY,
|
||||||
|
redisArgs,
|
||||||
|
transformReply
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// export default class RedisClusterMultiCommand {
|
private static _createFunctionCommand(name: string, fn: RedisFunction, resp: RespVersions) {
|
||||||
// readonly #multi = new RedisMultiCommand();
|
const prefix = functionArgumentsPrefix(name, fn),
|
||||||
// readonly #executor: RedisClusterMultiExecutor;
|
transformReply = getTransformReply(fn, resp);
|
||||||
// #firstKey: RedisCommandArgument | undefined;
|
return function (this: { self: RedisClusterMultiCommand }, ...args: Array<unknown>) {
|
||||||
|
const fnArgs = fn.transformArguments(...args),
|
||||||
|
redisArgs: CommandArguments = prefix.concat(fnArgs),
|
||||||
|
firstKey = RedisCluster.extractFirstKey(
|
||||||
|
fn,
|
||||||
|
args,
|
||||||
|
fnArgs
|
||||||
|
);
|
||||||
|
redisArgs.preserve = fnArgs.preserve;
|
||||||
|
return this.self.addCommand(
|
||||||
|
firstKey,
|
||||||
|
fn.IS_READ_ONLY,
|
||||||
|
redisArgs,
|
||||||
|
transformReply
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// static extend<
|
private static _createScriptCommand(script: RedisScript, resp: RespVersions) {
|
||||||
// M extends RedisModules,
|
const transformReply = getTransformReply(script, resp);
|
||||||
// F extends RedisFunctions,
|
return function (this: RedisClusterMultiCommand, ...args: Array<unknown>) {
|
||||||
// S extends RedisScripts
|
const scriptArgs = script.transformArguments(...args);
|
||||||
// >(extensions?: RedisExtensions<M, F, S>): InstantiableRedisClusterMultiCommandType<M, F, S> {
|
this._setState(
|
||||||
// return attachExtensions({
|
RedisCluster.extractFirstKey(
|
||||||
// BaseClass: RedisClusterMultiCommand,
|
script,
|
||||||
// modulesExecutor: RedisClusterMultiCommand.prototype.commandsExecutor,
|
args,
|
||||||
// modules: extensions?.modules,
|
scriptArgs
|
||||||
// functionsExecutor: RedisClusterMultiCommand.prototype.functionsExecutor,
|
),
|
||||||
// functions: extensions?.functions,
|
script.IS_READ_ONLY
|
||||||
// scriptsExecutor: RedisClusterMultiCommand.prototype.scriptsExecutor,
|
);
|
||||||
// scripts: extensions?.scripts
|
this._multi.addScript(
|
||||||
// });
|
script,
|
||||||
// }
|
scriptArgs,
|
||||||
|
transformReply
|
||||||
|
);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// constructor(executor: RedisClusterMultiExecutor, firstKey?: RedisCommandArgument) {
|
static extend<
|
||||||
// this.#executor = executor;
|
M extends RedisModules = Record<string, never>,
|
||||||
// this.#firstKey = firstKey;
|
F extends RedisFunctions = Record<string, never>,
|
||||||
// }
|
S extends RedisScripts = Record<string, never>,
|
||||||
|
RESP extends RespVersions = 2
|
||||||
|
>(config?: CommanderConfig<M, F, S, RESP>) {
|
||||||
|
return attachConfig({
|
||||||
|
BaseClass: RedisClusterMultiCommand,
|
||||||
|
commands: COMMANDS,
|
||||||
|
createCommand: RedisClusterMultiCommand._createCommand,
|
||||||
|
createModuleCommand: RedisClusterMultiCommand._createModuleCommand,
|
||||||
|
createFunctionCommand: RedisClusterMultiCommand._createFunctionCommand,
|
||||||
|
createScriptCommand: RedisClusterMultiCommand._createScriptCommand,
|
||||||
|
config
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// commandsExecutor(command: RedisCommand, args: Array<unknown>): this {
|
private readonly _multi = new RedisMultiCommand();
|
||||||
// const transformedArguments = command.transformArguments(...args);
|
private readonly _cluster: RedisClusterType;
|
||||||
// this.#firstKey ??= RedisCluster.extractFirstKey(command, args, transformedArguments);
|
private _firstKey: RedisArgument | undefined;
|
||||||
// return this.addCommand(undefined, transformedArguments, command.transformReply);
|
private _isReadonly: boolean | undefined = true;
|
||||||
// }
|
|
||||||
|
|
||||||
// addCommand(
|
constructor(cluster: RedisClusterType, routing: RedisArgument | undefined) {
|
||||||
// firstKey: RedisCommandArgument | undefined,
|
this._cluster = cluster;
|
||||||
// args: RedisCommandArguments,
|
this._firstKey = routing;
|
||||||
// transformReply?: RedisCommand['transformReply']
|
}
|
||||||
// ): this {
|
|
||||||
// this.#firstKey ??= firstKey;
|
|
||||||
// this.#multi.addCommand(args, transformReply);
|
|
||||||
// return this;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// functionsExecutor(fn: RedisFunction, args: Array<unknown>, name: string): this {
|
private _setState(
|
||||||
// const transformedArguments = this.#multi.addFunction(name, fn, args);
|
firstKey: RedisArgument | undefined,
|
||||||
// this.#firstKey ??= RedisCluster.extractFirstKey(fn, args, transformedArguments);
|
isReadonly: boolean | undefined,
|
||||||
// return this;
|
) {
|
||||||
// }
|
this._firstKey ??= firstKey;
|
||||||
|
this._isReadonly &&= isReadonly;
|
||||||
|
}
|
||||||
|
|
||||||
// scriptsExecutor(script: RedisScript, args: Array<unknown>): this {
|
addCommand(
|
||||||
// const transformedArguments = this.#multi.addScript(script, args);
|
firstKey: RedisArgument | undefined,
|
||||||
// this.#firstKey ??= RedisCluster.extractFirstKey(script, args, transformedArguments);
|
isReadonly: boolean | undefined,
|
||||||
// return this;
|
args: CommandArguments,
|
||||||
// }
|
transformReply?: TransformReply
|
||||||
|
) {
|
||||||
|
this._setState(firstKey, isReadonly);
|
||||||
|
this._multi.addCommand(args, transformReply);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
// async exec(execAsPipeline = false): Promise<Array<RedisCommandRawReply>> {
|
async exec<T extends MultiReply = MULTI_REPLY['GENERIC']>(execAsPipeline = false) {
|
||||||
// if (execAsPipeline) {
|
if (execAsPipeline) return this.execAsPipeline<T>();
|
||||||
// return this.execAsPipeline();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return this.#multi.handleExecReplies(
|
return this._multi.transformReplies(
|
||||||
// await this.#executor(this.#multi.queue, this.#firstKey, RedisMultiCommand.generateChainId())
|
await this._cluster.executeMulti(
|
||||||
// );
|
this._firstKey,
|
||||||
// }
|
this._isReadonly,
|
||||||
|
this._multi.queue
|
||||||
|
)
|
||||||
|
) as MultiReplyType<T, REPLIES>;
|
||||||
|
}
|
||||||
|
|
||||||
// EXEC = this.exec;
|
EXEC = this.exec;
|
||||||
|
|
||||||
// async execAsPipeline(): Promise<Array<RedisCommandRawReply>> {
|
execTyped(execAsPipeline = false) {
|
||||||
// return this.#multi.transformReplies(
|
return this.exec<MULTI_REPLY['TYPED']>(execAsPipeline);
|
||||||
// await this.#executor(this.#multi.queue, this.#firstKey)
|
}
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// attachCommands({
|
async execAsPipeline<T extends MultiReply = MULTI_REPLY['GENERIC']>() {
|
||||||
// BaseClass: RedisClusterMultiCommand,
|
if (this._multi.queue.length === 0) return [] as MultiReplyType<T, REPLIES>;
|
||||||
// commands: COMMANDS,
|
|
||||||
// executor: RedisClusterMultiCommand.prototype.commandsExecutor
|
return this._multi.transformReplies(
|
||||||
// });
|
await this._cluster.executePipeline(
|
||||||
|
this._firstKey,
|
||||||
|
this._isReadonly,
|
||||||
|
this._multi.queue
|
||||||
|
)
|
||||||
|
) as MultiReplyType<T, REPLIES>;
|
||||||
|
}
|
||||||
|
|
||||||
|
execAsPipelineTyped() {
|
||||||
|
return this.execAsPipeline<MULTI_REPLY['TYPED']>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -1,5 +1,15 @@
|
|||||||
import { CommandArguments, RedisScript, TransformReply } from './RESP/types';
|
import { CommandArguments, RedisScript, TransformReply } from './RESP/types';
|
||||||
|
|
||||||
|
// TODO: enum?
|
||||||
|
export type MULTI_REPLY = {
|
||||||
|
GENERIC: 'generic';
|
||||||
|
TYPED: 'typed';
|
||||||
|
};
|
||||||
|
|
||||||
|
export type MultiReply = MULTI_REPLY[keyof MULTI_REPLY];
|
||||||
|
|
||||||
|
export type MultiReplyType<T extends MultiReply, REPLIES> = T extends MULTI_REPLY['TYPED'] ? REPLIES : Array<unknown>;
|
||||||
|
|
||||||
export interface RedisMultiQueuedCommand {
|
export interface RedisMultiQueuedCommand {
|
||||||
args: CommandArguments;
|
args: CommandArguments;
|
||||||
transformReply?: TransformReply;
|
transformReply?: TransformReply;
|
||||||
@@ -15,7 +25,6 @@ export default class RedisMultiCommand {
|
|||||||
args,
|
args,
|
||||||
transformReply
|
transformReply
|
||||||
});
|
});
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
addScript(script: RedisScript, args: CommandArguments, transformReply?: TransformReply) {
|
addScript(script: RedisScript, args: CommandArguments, transformReply?: TransformReply) {
|
||||||
@@ -34,7 +43,7 @@ export default class RedisMultiCommand {
|
|||||||
redisArgs.push(...args);
|
redisArgs.push(...args);
|
||||||
redisArgs.preserve = args.preserve;
|
redisArgs.preserve = args.preserve;
|
||||||
|
|
||||||
return this.addCommand(redisArgs, transformReply);
|
this.addCommand(redisArgs, transformReply);
|
||||||
}
|
}
|
||||||
|
|
||||||
transformReplies(rawReplies: Array<unknown>): Array<unknown> {
|
transformReplies(rawReplies: Array<unknown>): Array<unknown> {
|
||||||
|
Reference in New Issue
Block a user