You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-09 00:22:08 +03:00
fix #1650 - add support for Buffer in some commands, add GET_BUFFER command
This commit is contained in:
@@ -195,6 +195,13 @@ describe('Client', () => {
|
||||
assert.equal(await client.sendCommand(['PING']), 'PONG');
|
||||
});
|
||||
|
||||
itWithClient(TestRedisServers.OPEN, 'bufferMode', async client => {
|
||||
assert.deepEqual(
|
||||
await client.sendCommand(['PING'], undefined, true),
|
||||
Buffer.from('PONG')
|
||||
);
|
||||
});
|
||||
|
||||
describe('AbortController', () => {
|
||||
before(function () {
|
||||
if (!global.AbortController) {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import RedisSocket, { RedisSocketOptions } from './socket';
|
||||
import RedisCommandsQueue, { PubSubListener, PubSubSubscribeCommands, PubSubUnsubscribeCommands, QueueCommandOptions } from './commands-queue';
|
||||
import COMMANDS from './commands';
|
||||
import COMMANDS, { TransformArgumentsReply } from './commands';
|
||||
import { RedisCommand, RedisModules, RedisReply } from './commands';
|
||||
import RedisMultiCommand, { MultiQueuedCommand, RedisMultiCommandType } from './multi-command';
|
||||
import EventEmitter from 'events';
|
||||
@@ -62,12 +62,10 @@ export default class RedisClient<M extends RedisModules = RedisModules, S extend
|
||||
): Promise<ReturnType<typeof command['transformReply']>> {
|
||||
const { args: redisArgs, options } = transformCommandArguments<ClientCommandOptions>(command, args);
|
||||
|
||||
const reply = command.transformReply(
|
||||
await this.#sendCommand(redisArgs, options),
|
||||
redisArgs.preserve
|
||||
return command.transformReply(
|
||||
await this.#sendCommand(redisArgs, options, command.BUFFER_MODE),
|
||||
redisArgs.preserve,
|
||||
);
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
static async #scriptsExecutor(
|
||||
@@ -77,12 +75,10 @@ export default class RedisClient<M extends RedisModules = RedisModules, S extend
|
||||
): Promise<typeof script['transformArguments']> {
|
||||
const { args: redisArgs, options } = transformCommandArguments<ClientCommandOptions>(script, args);
|
||||
|
||||
const reply = script.transformReply(
|
||||
await this.executeScript(script, redisArgs, options),
|
||||
return script.transformReply(
|
||||
await this.executeScript(script, redisArgs, options, script.BUFFER_MODE),
|
||||
redisArgs.preserve
|
||||
);
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
static create<M extends RedisModules, S extends RedisLuaScripts>(options?: RedisClientOptions<M, S>): RedisClientType<M, S> {
|
||||
@@ -182,10 +178,7 @@ export default class RedisClient<M extends RedisModules = RedisModules, S extend
|
||||
}
|
||||
|
||||
#initiateQueue(): RedisCommandsQueue {
|
||||
return new RedisCommandsQueue(
|
||||
this.#options?.commandsQueueMaxLength,
|
||||
encodedCommands => this.#socket.write(encodedCommands)
|
||||
);
|
||||
return new RedisCommandsQueue(this.#options?.commandsQueueMaxLength);
|
||||
}
|
||||
|
||||
#legacyMode(): void {
|
||||
@@ -299,7 +292,7 @@ export default class RedisClient<M extends RedisModules = RedisModules, S extend
|
||||
|
||||
QUIT(): Promise<void> {
|
||||
return this.#socket.quit(() => {
|
||||
const promise = this.#queue.addEncodedCommand(encodeCommand(['QUIT']));
|
||||
const promise = this.#queue.addCommand(['QUIT']);
|
||||
this.#tick();
|
||||
return promise;
|
||||
});
|
||||
@@ -307,46 +300,64 @@ export default class RedisClient<M extends RedisModules = RedisModules, S extend
|
||||
|
||||
quit = this.QUIT;
|
||||
|
||||
sendCommand<T = unknown>(args: Array<string>, options?: ClientCommandOptions): Promise<T> {
|
||||
return this.#sendCommand(args, options);
|
||||
sendCommand<T = RedisReply>(args: TransformArgumentsReply, options?: ClientCommandOptions, bufferMode?: boolean): Promise<T> {
|
||||
return this.#sendCommand(args, options, bufferMode);
|
||||
}
|
||||
|
||||
// using `#sendCommand` cause `sendCommand` is overwritten in legacy mode
|
||||
#sendCommand<T = RedisReply>(args: Array<string>, options?: ClientCommandOptions): Promise<T> {
|
||||
return this.sendEncodedCommand(encodeCommand(args), options);
|
||||
}
|
||||
|
||||
async sendEncodedCommand<T = RedisReply>(encodedCommand: string, options?: ClientCommandOptions): Promise<T> {
|
||||
async #sendCommand<T = RedisReply>(args: TransformArgumentsReply, options?: ClientCommandOptions, bufferMode?: boolean): Promise<T> {
|
||||
if (!this.#socket.isOpen) {
|
||||
throw new ClientClosedError();
|
||||
}
|
||||
|
||||
if (options?.isolated) {
|
||||
return this.executeIsolated(isolatedClient =>
|
||||
isolatedClient.sendEncodedCommand(encodedCommand, {
|
||||
isolatedClient.sendCommand(args, {
|
||||
...options,
|
||||
isolated: false
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const promise = this.#queue.addEncodedCommand<T>(encodedCommand, options);
|
||||
const promise = this.#queue.addCommand<T>(args, options, bufferMode);
|
||||
this.#tick();
|
||||
return await promise;
|
||||
}
|
||||
|
||||
#tick(): void {
|
||||
if (!this.#socket.isSocketExists) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.#socket.cork();
|
||||
|
||||
while (true) {
|
||||
const args = this.#queue.getCommandToSend();
|
||||
if (args === undefined) break;
|
||||
|
||||
let writeResult;
|
||||
for (const toWrite of encodeCommand(args)) {
|
||||
writeResult = this.#socket.write(toWrite);
|
||||
}
|
||||
|
||||
if (!writeResult) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
executeIsolated<T>(fn: (client: RedisClientType<M, S>) => T | Promise<T>): Promise<T> {
|
||||
return this.#isolationPool.use(fn);
|
||||
}
|
||||
|
||||
async executeScript(script: RedisLuaScript, args: Array<string>, options?: ClientCommandOptions): Promise<ReturnType<typeof script['transformReply']>> {
|
||||
async executeScript(script: RedisLuaScript, args: TransformArgumentsReply, options?: ClientCommandOptions, bufferMode?: boolean): Promise<ReturnType<typeof script['transformReply']>> {
|
||||
try {
|
||||
return await this.#sendCommand([
|
||||
'EVALSHA',
|
||||
script.SHA1,
|
||||
script.NUMBER_OF_KEYS.toString(),
|
||||
...args
|
||||
], options);
|
||||
], options, bufferMode);
|
||||
} catch (err: any) {
|
||||
if (!err?.message?.startsWith?.('NOSCRIPT')) {
|
||||
throw err;
|
||||
@@ -357,14 +368,14 @@ export default class RedisClient<M extends RedisModules = RedisModules, S extend
|
||||
script.SCRIPT,
|
||||
script.NUMBER_OF_KEYS.toString(),
|
||||
...args
|
||||
], options);
|
||||
], options, bufferMode);
|
||||
}
|
||||
}
|
||||
|
||||
#multiExecutor(commands: Array<MultiQueuedCommand>, chainId?: symbol): Promise<Array<RedisReply>> {
|
||||
const promise = Promise.all(
|
||||
commands.map(({encodedCommand}) => {
|
||||
return this.#queue.addEncodedCommand(encodedCommand, RedisClient.commandOptions({
|
||||
commands.map(({ args }) => {
|
||||
return this.#queue.addCommand(args, RedisClient.commandOptions({
|
||||
chainId
|
||||
}));
|
||||
})
|
||||
@@ -438,31 +449,6 @@ export default class RedisClient<M extends RedisModules = RedisModules, S extend
|
||||
await this.#isolationPool.drain();
|
||||
await this.#isolationPool.clear();
|
||||
}
|
||||
|
||||
#isTickQueued = false;
|
||||
|
||||
#tick(): void {
|
||||
const {chunkRecommendedSize} = this.#socket;
|
||||
if (!chunkRecommendedSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.#isTickQueued && this.#queue.waitingToBeSentCommandsLength < chunkRecommendedSize) {
|
||||
queueMicrotask(() => this.#tick());
|
||||
this.#isTickQueued = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const isBuffering = this.#queue.executeChunk(chunkRecommendedSize);
|
||||
if (isBuffering === true) {
|
||||
this.#socket.once('drain', () => this.#tick());
|
||||
} else if (isBuffering === false) {
|
||||
this.#tick();
|
||||
return;
|
||||
}
|
||||
|
||||
this.#isTickQueued = false;
|
||||
}
|
||||
}
|
||||
|
||||
extendWithDefaultCommands(RedisClient, RedisClient.commandsExecutor);
|
||||
|
@@ -172,7 +172,7 @@ export default class RedisClusterSlots<M extends RedisModules, S extends RedisLu
|
||||
return value.client;
|
||||
}
|
||||
|
||||
getClient(firstKey?: string, isReadonly?: boolean): RedisClientType<M, S> {
|
||||
getClient(firstKey?: string | Buffer, isReadonly?: boolean): RedisClientType<M, S> {
|
||||
if (!firstKey) {
|
||||
return this.#getRandomClient();
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import { RedisCommand, RedisModules } from './commands';
|
||||
import { RedisCommand, RedisModules, TransformArgumentsReply } from './commands';
|
||||
import RedisClient, { ClientCommandOptions, RedisClientType, WithPlugins } from './client';
|
||||
import { RedisSocketOptions } from './socket';
|
||||
import RedisClusterSlots, { ClusterNode } from './cluster-slots';
|
||||
@@ -6,6 +6,7 @@ import { RedisLuaScript, RedisLuaScripts } from './lua-script';
|
||||
import { extendWithModulesAndScripts, extendWithDefaultCommands, transformCommandArguments } from './commander';
|
||||
import RedisMultiCommand, { MultiQueuedCommand, RedisMultiCommandType } from './multi-command';
|
||||
import { EventEmitter } from 'events';
|
||||
import cluster from 'cluster';
|
||||
|
||||
export interface RedisClusterOptions<M = RedisModules, S = RedisLuaScripts> {
|
||||
rootNodes: Array<RedisSocketOptions>;
|
||||
@@ -19,7 +20,7 @@ export type RedisClusterType<M extends RedisModules, S extends RedisLuaScripts>
|
||||
WithPlugins<M, S> & RedisCluster;
|
||||
|
||||
export default class RedisCluster<M extends RedisModules = RedisModules, S extends RedisLuaScripts = RedisLuaScripts> extends EventEmitter {
|
||||
static #extractFirstKey(command: RedisCommand, originalArgs: Array<unknown>, redisArgs: Array<string>): string | undefined {
|
||||
static #extractFirstKey(command: RedisCommand, originalArgs: Array<unknown>, redisArgs: TransformArgumentsReply): string | Buffer | undefined {
|
||||
if (command.FIRST_KEY_INDEX === undefined) {
|
||||
return undefined;
|
||||
} else if (typeof command.FIRST_KEY_INDEX === 'number') {
|
||||
@@ -41,7 +42,8 @@ export default class RedisCluster<M extends RedisModules = RedisModules, S exten
|
||||
RedisCluster.#extractFirstKey(command, args, redisArgs),
|
||||
command.IS_READ_ONLY,
|
||||
redisArgs,
|
||||
options
|
||||
options,
|
||||
command.BUFFER_MODE
|
||||
),
|
||||
redisArgs.preserve
|
||||
);
|
||||
@@ -100,22 +102,23 @@ export default class RedisCluster<M extends RedisModules = RedisModules, S exten
|
||||
}
|
||||
|
||||
async sendCommand<C extends RedisCommand>(
|
||||
firstKey: string | undefined,
|
||||
firstKey: string | Buffer | undefined,
|
||||
isReadonly: boolean | undefined,
|
||||
args: Array<string>,
|
||||
args: TransformArgumentsReply,
|
||||
options?: ClientCommandOptions,
|
||||
bufferMode?: boolean,
|
||||
redirections = 0
|
||||
): Promise<ReturnType<C['transformReply']>> {
|
||||
const client = this.#slots.getClient(firstKey, isReadonly);
|
||||
|
||||
try {
|
||||
return await client.sendCommand(args, options);
|
||||
return await client.sendCommand(args, options, bufferMode);
|
||||
} catch (err: any) {
|
||||
const shouldRetry = await this.#handleCommandError(err, client, redirections);
|
||||
if (shouldRetry === true) {
|
||||
return this.sendCommand(firstKey, isReadonly, args, options, redirections + 1);
|
||||
return this.sendCommand(firstKey, isReadonly, args, options, bufferMode, redirections + 1);
|
||||
} else if (shouldRetry) {
|
||||
return shouldRetry.sendCommand(args, options);
|
||||
return shouldRetry.sendCommand(args, options, bufferMode);
|
||||
}
|
||||
|
||||
throw err;
|
||||
@@ -125,7 +128,7 @@ export default class RedisCluster<M extends RedisModules = RedisModules, S exten
|
||||
async executeScript(
|
||||
script: RedisLuaScript,
|
||||
originalArgs: Array<unknown>,
|
||||
redisArgs: Array<string>,
|
||||
redisArgs: TransformArgumentsReply,
|
||||
options?: ClientCommandOptions,
|
||||
redirections = 0
|
||||
): Promise<ReturnType<typeof script['transformReply']>> {
|
||||
@@ -135,13 +138,13 @@ export default class RedisCluster<M extends RedisModules = RedisModules, S exten
|
||||
);
|
||||
|
||||
try {
|
||||
return await client.executeScript(script, redisArgs, options);
|
||||
return await client.executeScript(script, redisArgs, options, script.BUFFER_MODE);
|
||||
} catch (err: any) {
|
||||
const shouldRetry = await this.#handleCommandError(err, client, redirections);
|
||||
if (shouldRetry === true) {
|
||||
return this.executeScript(script, originalArgs, redisArgs, options, redirections + 1);
|
||||
} else if (shouldRetry) {
|
||||
return shouldRetry.executeScript(script, redisArgs, options);
|
||||
return shouldRetry.executeScript(script, redisArgs, options, script.BUFFER_MODE);
|
||||
}
|
||||
|
||||
throw err;
|
||||
@@ -181,8 +184,8 @@ export default class RedisCluster<M extends RedisModules = RedisModules, S exten
|
||||
const client = this.#slots.getClient(routing);
|
||||
|
||||
return Promise.all(
|
||||
commands.map(({encodedCommand}) => {
|
||||
return client.sendEncodedCommand(encodedCommand, RedisClient.commandOptions({
|
||||
commands.map(({ args }) => {
|
||||
return client.sendCommand(args, RedisClient.commandOptions({
|
||||
chainId
|
||||
}));
|
||||
})
|
||||
|
@@ -2,27 +2,43 @@ import { strict as assert } from 'assert';
|
||||
import { describe } from 'mocha';
|
||||
import { encodeCommand } from './commander';
|
||||
|
||||
function encodeCommandToString(...args: Parameters<typeof encodeCommand>): string {
|
||||
const arr = [];
|
||||
for (const item of encodeCommand(...args)) {
|
||||
arr.push(item.toString());
|
||||
}
|
||||
|
||||
return arr.join('');
|
||||
}
|
||||
|
||||
describe('Commander', () => {
|
||||
describe('encodeCommand (see #1628)', () => {
|
||||
it('1 byte', () => {
|
||||
assert.equal(
|
||||
encodeCommand(['a', 'z']),
|
||||
encodeCommandToString(['a', 'z']),
|
||||
'*2\r\n$1\r\na\r\n$1\r\nz\r\n'
|
||||
);
|
||||
});
|
||||
|
||||
it('2 bytes', () => {
|
||||
assert.equal(
|
||||
encodeCommand(['א', 'ת']),
|
||||
encodeCommandToString(['א', 'ת']),
|
||||
'*2\r\n$2\r\nא\r\n$2\r\nת\r\n'
|
||||
);
|
||||
});
|
||||
|
||||
it('4 bytes', () => {
|
||||
assert.equal(
|
||||
encodeCommand(['🐣', '🐤']),
|
||||
encodeCommandToString(['🐣', '🐤']),
|
||||
'*2\r\n$4\r\n🐣\r\n$4\r\n🐤\r\n'
|
||||
);
|
||||
});
|
||||
|
||||
it('with a buffer', () => {
|
||||
assert.equal(
|
||||
encodeCommandToString([Buffer.from('string')]),
|
||||
'*1\r\n$6\r\nstring\r\n'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -2,6 +2,7 @@
|
||||
import COMMANDS, { RedisCommand, RedisModules, TransformArgumentsReply } from './commands';
|
||||
import { RedisLuaScript, RedisLuaScripts } from './lua-script';
|
||||
import { CommandOptions, isCommandOptions } from './command-options';
|
||||
import { off } from 'process';
|
||||
|
||||
type Instantiable<T = any> = new(...args: Array<any>) => T;
|
||||
|
||||
@@ -94,16 +95,15 @@ export function transformCommandArguments<T = unknown>(
|
||||
};
|
||||
}
|
||||
|
||||
export function encodeCommand(args: Array<string>): string {
|
||||
const encoded = [
|
||||
`*${args.length}`,
|
||||
`$${Buffer.byteLength(args[0]).toString()}`,
|
||||
args[0]
|
||||
];
|
||||
const DELIMITER = '\r\n';
|
||||
|
||||
for (let i = 1; i < args.length; i++) {
|
||||
encoded.push(`$${Buffer.byteLength(args[i]).toString()}`, args[i]);
|
||||
export function* encodeCommand(args: TransformArgumentsReply): IterableIterator<string | Buffer> {
|
||||
yield `*${args.length}${DELIMITER}`;
|
||||
|
||||
for (const arg of args) {
|
||||
const byteLength = typeof arg === 'string' ? Buffer.byteLength(arg): arg.length;
|
||||
yield `$${byteLength.toString()}${DELIMITER}`;
|
||||
yield arg;
|
||||
yield DELIMITER;
|
||||
}
|
||||
|
||||
return encoded.join('\r\n') + '\r\n';
|
||||
}
|
||||
|
@@ -2,17 +2,15 @@ import LinkedList from 'yallist';
|
||||
import RedisParser from 'redis-parser';
|
||||
import { AbortError } from './errors';
|
||||
import { RedisReply } from './commands';
|
||||
import { encodeCommand } from './commander';
|
||||
|
||||
export interface QueueCommandOptions {
|
||||
asap?: boolean;
|
||||
signal?: any; // TODO: `AbortSignal` type is incorrect
|
||||
chainId?: symbol;
|
||||
signal?: any; // TODO: `AbortSignal` type is incorrect
|
||||
}
|
||||
|
||||
interface CommandWaitingToBeSent extends CommandWaitingForReply {
|
||||
encodedCommand: string;
|
||||
byteLength: number;
|
||||
args: Array<string | Buffer>;
|
||||
chainId?: symbol;
|
||||
abort?: {
|
||||
signal: any; // TODO: `AbortSignal` type is incorrect
|
||||
@@ -24,10 +22,9 @@ interface CommandWaitingForReply {
|
||||
resolve(reply?: any): void;
|
||||
reject(err: Error): void;
|
||||
channelsCounter?: number;
|
||||
bufferMode?: boolean;
|
||||
}
|
||||
|
||||
export type CommandsQueueExecutor = (encodedCommands: string) => boolean | undefined;
|
||||
|
||||
export enum PubSubSubscribeCommands {
|
||||
SUBSCRIBE = 'SUBSCRIBE',
|
||||
PSUBSCRIBE = 'PSUBSCRIBE'
|
||||
@@ -57,16 +54,8 @@ export default class RedisCommandsQueue {
|
||||
|
||||
readonly #maxLength: number | null | undefined;
|
||||
|
||||
readonly #executor: CommandsQueueExecutor;
|
||||
|
||||
readonly #waitingToBeSent = new LinkedList<CommandWaitingToBeSent>();
|
||||
|
||||
#waitingToBeSentCommandsLength = 0;
|
||||
|
||||
get waitingToBeSentCommandsLength() {
|
||||
return this.#waitingToBeSentCommandsLength;
|
||||
}
|
||||
|
||||
readonly #waitingForReply = new LinkedList<CommandWaitingForReply>();
|
||||
|
||||
readonly #pubSubState = {
|
||||
@@ -114,12 +103,11 @@ export default class RedisCommandsQueue {
|
||||
|
||||
#chainInExecution: symbol | undefined;
|
||||
|
||||
constructor(maxLength: number | null | undefined, executor: CommandsQueueExecutor) {
|
||||
constructor(maxLength: number | null | undefined) {
|
||||
this.#maxLength = maxLength;
|
||||
this.#executor = executor;
|
||||
}
|
||||
|
||||
addEncodedCommand<T = RedisReply>(encodedCommand: string, options?: QueueCommandOptions): Promise<T> {
|
||||
addCommand<T = RedisReply>(args: Array<string | Buffer>, options?: QueueCommandOptions, bufferMode?: boolean): Promise<T> {
|
||||
if (this.#pubSubState.subscribing || this.#pubSubState.subscribed) {
|
||||
return Promise.reject(new Error('Cannot send commands in PubSub mode'));
|
||||
} else if (this.#maxLength && this.#waitingToBeSent.length + this.#waitingForReply.length >= this.#maxLength) {
|
||||
@@ -130,11 +118,11 @@ export default class RedisCommandsQueue {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const node = new LinkedList.Node<CommandWaitingToBeSent>({
|
||||
encodedCommand,
|
||||
byteLength: Buffer.byteLength(encodedCommand),
|
||||
args,
|
||||
chainId: options?.chainId,
|
||||
bufferMode,
|
||||
resolve,
|
||||
reject
|
||||
reject,
|
||||
});
|
||||
|
||||
if (options?.signal) {
|
||||
@@ -157,8 +145,6 @@ export default class RedisCommandsQueue {
|
||||
} else {
|
||||
this.#waitingToBeSent.pushNode(node);
|
||||
}
|
||||
|
||||
this.#waitingToBeSentCommandsLength += node.value.byteLength;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -233,11 +219,8 @@ export default class RedisCommandsQueue {
|
||||
|
||||
this.#pubSubState[inProgressKey] += channelsCounter;
|
||||
|
||||
const encodedCommand = encodeCommand(commandArgs),
|
||||
byteLength = Buffer.byteLength(encodedCommand);
|
||||
this.#waitingToBeSent.push({
|
||||
encodedCommand,
|
||||
byteLength,
|
||||
args: commandArgs,
|
||||
channelsCounter,
|
||||
resolve: () => {
|
||||
this.#pubSubState[inProgressKey] -= channelsCounter;
|
||||
@@ -249,7 +232,6 @@ export default class RedisCommandsQueue {
|
||||
reject();
|
||||
}
|
||||
});
|
||||
this.#waitingToBeSentCommandsLength += byteLength;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -267,47 +249,25 @@ export default class RedisCommandsQueue {
|
||||
]);
|
||||
}
|
||||
|
||||
executeChunk(recommendedSize: number): boolean | undefined {
|
||||
if (!this.#waitingToBeSent.length) return;
|
||||
|
||||
const encoded: Array<string> = [];
|
||||
let size = 0,
|
||||
lastCommandChainId: symbol | undefined;
|
||||
for (const command of this.#waitingToBeSent) {
|
||||
encoded.push(command.encodedCommand);
|
||||
size += command.byteLength;
|
||||
if (size > recommendedSize) {
|
||||
lastCommandChainId = command.chainId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!lastCommandChainId && encoded.length === this.#waitingToBeSent.length) {
|
||||
lastCommandChainId = this.#waitingToBeSent.tail!.value.chainId;
|
||||
}
|
||||
|
||||
lastCommandChainId ??= this.#waitingToBeSent.tail?.value.chainId;
|
||||
|
||||
this.#executor(encoded.join(''));
|
||||
|
||||
for (let i = 0; i < encoded.length; i++) {
|
||||
const waitingToBeSent = this.#waitingToBeSent.shift()!;
|
||||
if (waitingToBeSent.abort) {
|
||||
waitingToBeSent.abort.signal.removeEventListener('abort', waitingToBeSent.abort.listener);
|
||||
}
|
||||
getCommandToSend(): Array<string | Buffer> | undefined {
|
||||
const toSend = this.#waitingToBeSent.shift();
|
||||
|
||||
if (toSend) {
|
||||
this.#waitingForReply.push({
|
||||
resolve: waitingToBeSent.resolve,
|
||||
reject: waitingToBeSent.reject,
|
||||
channelsCounter: waitingToBeSent.channelsCounter
|
||||
resolve: toSend.resolve,
|
||||
reject: toSend.reject,
|
||||
channelsCounter: toSend.channelsCounter,
|
||||
bufferMode: toSend.bufferMode
|
||||
});
|
||||
}
|
||||
|
||||
this.#chainInExecution = lastCommandChainId;
|
||||
this.#waitingToBeSentCommandsLength -= size;
|
||||
this.#chainInExecution = toSend?.chainId;
|
||||
|
||||
return toSend?.args;
|
||||
}
|
||||
|
||||
parseResponse(data: Buffer): void {
|
||||
this.#parser.setReturnBuffers(!!this.#waitingForReply.head?.value.bufferMode);
|
||||
this.#parser.execute(data);
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyNumber } from './generic-transformers';
|
||||
|
||||
export function transformArguments(username: string | Array<string>): Array<string> {
|
||||
export function transformArguments(username: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['ACL', 'DELUSER'], username);
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyString } from './generic-transformers';
|
||||
|
||||
export function transformArguments(username: string, rule: string | Array<string>): Array<string> {
|
||||
export function transformArguments(username: string, rule: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['ACL', 'SETUSER', username], rule);
|
||||
}
|
||||
|
||||
|
@@ -1,10 +1,11 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyNumber } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 2;
|
||||
|
||||
type BitOperations = 'AND' | 'OR' | 'XOR' | 'NOT';
|
||||
|
||||
export function transformArguments(operation: BitOperations, destKey: string, key: string | Array<string>): Array<string> {
|
||||
export function transformArguments(operation: BitOperations, destKey: string, key: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['BITOP', operation, destKey], key);
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(keys: string | Array<string>, timeout: number): Array<string> {
|
||||
export function transformArguments(keys: string | Buffer | Array<string | Buffer>, timeout: number): TransformArgumentsReply {
|
||||
const args = pushVerdictArguments(['BLPOP'], keys);
|
||||
|
||||
args.push(timeout.toString());
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(key: string | Array<string>, timeout: number): Array<string> {
|
||||
export function transformArguments(key: string | Array<string>, timeout: number): TransformArgumentsReply {
|
||||
const args = pushVerdictArguments(['BRPOP'], key);
|
||||
|
||||
args.push(timeout.toString());
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyNumberInfinity, ZMember } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(key: string | Array<string>, timeout: number): Array<string> {
|
||||
export function transformArguments(key: string | Array<string>, timeout: number): TransformArgumentsReply {
|
||||
const args = pushVerdictArguments(['BZPOPMAX'], key);
|
||||
|
||||
args.push(timeout.toString());
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyNumberInfinity, ZMember } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(key: string | Array<string>, timeout: number): Array<string> {
|
||||
export function transformArguments(key: string | Array<string>, timeout: number): TransformArgumentsReply {
|
||||
const args = pushVerdictArguments(['BZPOPMIN'], key);
|
||||
|
||||
args.push(timeout.toString());
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyNumber } from './generic-transformers';
|
||||
|
||||
export function transformArguments(keys: string | Array<string>): Array<string> {
|
||||
export function transformArguments(keys: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['DEL'], keys);
|
||||
}
|
||||
|
||||
|
@@ -1,10 +1,11 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyBoolean } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export const IS_READ_ONLY = true;
|
||||
|
||||
export function transformArguments(keys: string | Array<string>): Array<string> {
|
||||
export function transformArguments(keys: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['EXISTS'], keys);
|
||||
}
|
||||
|
||||
|
@@ -1,10 +1,11 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export const IS_READ_ONLY = true;
|
||||
|
||||
export function transformArguments(key: string, member: string | Array<string>): Array<string> {
|
||||
export function transformArguments(key: string, member: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['GEOHASH', key], member);
|
||||
}
|
||||
|
||||
|
@@ -1,10 +1,11 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export const IS_READ_ONLY = true;
|
||||
|
||||
export function transformArguments(key: string, member: string | Array<string>): Array<string> {
|
||||
export function transformArguments(key: string, member: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['GEOPOS', key], member);
|
||||
}
|
||||
|
||||
|
@@ -1,10 +1,11 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { transformReplyString } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export const IS_READ_ONLY = true;
|
||||
|
||||
export function transformArguments(key: string): Array<string> {
|
||||
export function transformArguments(key: string | Buffer): TransformArgumentsReply {
|
||||
return ['GET', key];
|
||||
}
|
||||
|
||||
|
22
lib/commands/GET_BUFFER.spec.ts
Normal file
22
lib/commands/GET_BUFFER.spec.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { strict as assert } from 'assert';
|
||||
import { TestRedisServers, itWithClient, TestRedisClusters, itWithCluster } from '../test-utils';
|
||||
|
||||
describe('GET_BUFFER', () => {
|
||||
itWithClient(TestRedisServers.OPEN, 'client.getBuffer', async client => {
|
||||
const buffer = Buffer.from('string');
|
||||
await client.set('key', buffer);
|
||||
assert.deepEqual(
|
||||
buffer,
|
||||
await client.getBuffer('key')
|
||||
);
|
||||
});
|
||||
|
||||
itWithCluster(TestRedisClusters.OPEN, 'cluster.getBuffer', async cluster => {
|
||||
const buffer = Buffer.from('string');
|
||||
await cluster.set('key', buffer);
|
||||
assert.deepEqual(
|
||||
buffer,
|
||||
await cluster.getBuffer('key')
|
||||
);
|
||||
});
|
||||
});
|
7
lib/commands/GET_BUFFER.ts
Normal file
7
lib/commands/GET_BUFFER.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { transformReplyBuffer } from './generic-transformers';
|
||||
|
||||
export { FIRST_KEY_INDEX, IS_READ_ONLY, transformArguments } from './GET';
|
||||
|
||||
export const BUFFER_MODE = true;
|
||||
|
||||
export const transformReply = transformReplyBuffer;
|
@@ -1,8 +1,9 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyNumber } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(key: string, field: string | Array<string>): Array<string> {
|
||||
export function transformArguments(key: string, field: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['HDEL', key], field);
|
||||
}
|
||||
|
||||
|
@@ -1,10 +1,11 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export const IS_READ_ONLY = true;
|
||||
|
||||
export function transformArguments(key: string, fields: string | Array<string>): Array<string> {
|
||||
export function transformArguments(key: string, fields: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['HMGET', key], fields);
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyNumber } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(key: string, elements: string | Array<string>): Array<string> {
|
||||
export function transformArguments(key: string, elements: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['LPUSH', key], elements);}
|
||||
|
||||
export const transformReply = transformReplyNumber;
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyNumber } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(key: string, element: string | Array<string>): Array<string> {
|
||||
export function transformArguments(key: string, element: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['LPUSHX', key], element);
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyBoolean } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(key: string, element: string | Array<string>): Array<string> {
|
||||
export function transformArguments(key: string, element: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['PFADD', key], element);
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyNumber } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(key: string | Array<string>): Array<string> {
|
||||
export function transformArguments(key: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['PFCOUNT'], key);
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyString } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(destination: string, source: string | Array<string>): Array<string> {
|
||||
export function transformArguments(destination: string, source: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['PFMERGE', destination], source);
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyNumber } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(key: string, element: string | Array<string>): Array<string> {
|
||||
export function transformArguments(key: string, element: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['RPUSH', key], element);
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyNumber } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(key: string, element: string | Array<string>): Array<string> {
|
||||
export function transformArguments(key: string, element: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['RPUSHX', key], element);
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyNumber } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(key: string, members: string | Array<string>): Array<string> {
|
||||
export function transformArguments(key: string, members: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['SADD', key], members);
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyBooleanArray } from './generic-transformers';
|
||||
|
||||
export function transformArguments(sha1: string | Array<string>): Array<string> {
|
||||
export function transformArguments(sha1: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['SCRIPT', 'EXISTS'], sha1);
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(keys: string | Array<string>): Array<string> {
|
||||
export function transformArguments(keys: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['SDIFF'], keys);
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyNumber } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(destination: string, keys: string | Array<string>): Array<string> {
|
||||
export function transformArguments(destination: string, keys: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['SDIFFSTORE', destination], keys);
|
||||
}
|
||||
|
||||
|
@@ -106,7 +106,7 @@ describe('SET', () => {
|
||||
'OK'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
itWithClient(TestRedisServers.OPEN, 'with GET on empty key', async client => {
|
||||
assert.equal(
|
||||
await client.set('key', 'value', {
|
||||
|
@@ -1,3 +1,5 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
interface EX {
|
||||
@@ -38,7 +40,7 @@ interface SetCommonOptions {
|
||||
|
||||
type SetOptions = SetTTL & SetGuards & (SetCommonOptions | {});
|
||||
|
||||
export function transformArguments(key: string, value: string, options?: SetOptions): Array<string> {
|
||||
export function transformArguments(key: string | Buffer, value: string | Buffer, options?: SetOptions): TransformArgumentsReply {
|
||||
const args = ['SET', key, value];
|
||||
|
||||
if (!options) {
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { transformReplyString } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(key: string, seconds: number, value: string): Array<string> {
|
||||
export function transformArguments(key: string | Buffer, seconds: number, value: string): TransformArgumentsReply {
|
||||
return [
|
||||
'SETEX',
|
||||
key,
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(keys: string | Array<string>): Array<string> {
|
||||
export function transformArguments(keys: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['SINTER'], keys);
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(destination: string, keys: string | Array<string>): Array<string> {
|
||||
export function transformArguments(destination: string, keys: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['SINTERSTORE', destination], keys);
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyNumber } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(key: string, members: string | Array<string>): Array<string> {
|
||||
export function transformArguments(key: string, members: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['SREM', key], members);
|
||||
}
|
||||
|
||||
|
@@ -1,10 +1,11 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyStringArray } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export const IS_READ_ONLY = true;
|
||||
|
||||
export function transformArguments(keys: string | Array<string>): Array<string> {
|
||||
export function transformArguments(keys: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['SUNION'], keys);
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyNumber } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(destination: string, keys: string | Array<string>): Array<string> {
|
||||
export function transformArguments(destination: string, keys: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['SUNIONSTORE', destination], keys);
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyNumber } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(key: string | Array<string>): Array<string> {
|
||||
export function transformArguments(key: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['TOUCH'], key);
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyNumber } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(key: string | Array<string>): Array<string> {
|
||||
export function transformArguments(key: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['UNLINK'], key);
|
||||
}
|
||||
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyString } from './generic-transformers';
|
||||
|
||||
export function transformArguments(key: string | Array<string>): Array<string> {
|
||||
export function transformArguments(key: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['WATCH'], key);
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyNumber } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(key: string, group: string, id: string | Array<string>): Array<string> {
|
||||
export function transformArguments(key: string, group: string, id: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['XACK', key, group], id);
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyNumber } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(key: string, id: string | Array<string>): Array<string> {
|
||||
export function transformArguments(key: string, id: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['XDEL', key], id);
|
||||
}
|
||||
|
||||
|
@@ -1,10 +1,11 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArgument, transformReplyStringArray } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 2;
|
||||
|
||||
export const IS_READ_ONLY = true;
|
||||
|
||||
export function transformArguments(keys: Array<string> | string): Array<string> {
|
||||
export function transformArguments(keys: Array<string> | string): TransformArgumentsReply {
|
||||
return pushVerdictArgument(['ZDIFF'], keys);
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArgument, transformReplyNumber } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(destination: string, keys: Array<string> | string): Array<string> {
|
||||
export function transformArguments(destination: string, keys: Array<string> | string): TransformArgumentsReply {
|
||||
return pushVerdictArgument(['ZDIFFSTORE', destination], keys);
|
||||
}
|
||||
|
||||
|
@@ -1,9 +1,10 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { transformReplySortedSetWithScores } from './generic-transformers';
|
||||
import { transformArguments as transformZDiffArguments } from './ZDIFF';
|
||||
|
||||
export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZDIFF';
|
||||
|
||||
export function transformArguments(...args: Parameters<typeof transformZDiffArguments>): Array<string> {
|
||||
export function transformArguments(...args: Parameters<typeof transformZDiffArguments>): TransformArgumentsReply {
|
||||
return [
|
||||
...transformZDiffArguments(...args),
|
||||
'WITHSCORES'
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArgument, transformReplyStringArray } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 2;
|
||||
@@ -9,7 +10,7 @@ interface ZInterOptions {
|
||||
AGGREGATE?: 'SUM' | 'MIN' | 'MAX';
|
||||
}
|
||||
|
||||
export function transformArguments(keys: Array<string> | string, options?: ZInterOptions): Array<string> {
|
||||
export function transformArguments(keys: Array<string> | string, options?: ZInterOptions): TransformArgumentsReply {
|
||||
const args = pushVerdictArgument(['ZINTER'], keys);
|
||||
|
||||
if (options?.WEIGHTS) {
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArgument, transformReplyNumber } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
@@ -7,7 +8,7 @@ interface ZInterStoreOptions {
|
||||
AGGREGATE?: 'SUM' | 'MIN' | 'MAX';
|
||||
}
|
||||
|
||||
export function transformArguments(destination: string, keys: Array<string> | string, options?: ZInterStoreOptions): Array<string> {
|
||||
export function transformArguments(destination: string, keys: Array<string> | string, options?: ZInterStoreOptions): TransformArgumentsReply {
|
||||
const args = pushVerdictArgument(['ZINTERSTORE', destination], keys);
|
||||
|
||||
if (options?.WEIGHTS) {
|
||||
|
@@ -1,9 +1,10 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { transformReplySortedSetWithScores } from './generic-transformers';
|
||||
import { transformArguments as transformZInterArguments } from './ZINTER';
|
||||
|
||||
export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZINTER';
|
||||
|
||||
export function transformArguments(...args: Parameters<typeof transformZInterArguments>): Array<string> {
|
||||
export function transformArguments(...args: Parameters<typeof transformZInterArguments>): TransformArgumentsReply {
|
||||
return [
|
||||
...transformZInterArguments(...args),
|
||||
'WITHSCORES'
|
||||
|
@@ -1,10 +1,11 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyNumberInfinityNullArray } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export const IS_READ_ONLY = true;
|
||||
|
||||
export function transformArguments(key: string, member: string | Array<string>): Array<string> {
|
||||
export function transformArguments(key: string, member: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['ZMSCORE', key], member);
|
||||
}
|
||||
|
||||
|
@@ -1,8 +1,9 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArguments, transformReplyNumber } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
|
||||
export function transformArguments(key: string, member: string | Array<string>): Array<string> {
|
||||
export function transformArguments(key: string, member: string | Array<string>): TransformArgumentsReply {
|
||||
return pushVerdictArguments(['ZREM', key], member);
|
||||
}
|
||||
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArgument, transformReplyStringArray } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 2;
|
||||
@@ -9,7 +10,7 @@ interface ZUnionOptions {
|
||||
AGGREGATE?: 'SUM' | 'MIN' | 'MAX';
|
||||
}
|
||||
|
||||
export function transformArguments(keys: Array<string> | string, options?: ZUnionOptions): Array<string> {
|
||||
export function transformArguments(keys: Array<string> | string, options?: ZUnionOptions): TransformArgumentsReply {
|
||||
const args = pushVerdictArgument(['ZUNION'], keys);
|
||||
|
||||
if (options?.WEIGHTS) {
|
||||
|
@@ -1,3 +1,4 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { pushVerdictArgument, transformReplyNumber } from './generic-transformers';
|
||||
|
||||
export const FIRST_KEY_INDEX = 1;
|
||||
@@ -7,7 +8,7 @@ interface ZUnionOptions {
|
||||
AGGREGATE?: 'SUM' | 'MIN' | 'MAX';
|
||||
}
|
||||
|
||||
export function transformArguments(destination: string, keys: Array<string> | string, options?: ZUnionOptions): Array<string> {
|
||||
export function transformArguments(destination: string, keys: Array<string> | string, options?: ZUnionOptions): TransformArgumentsReply {
|
||||
const args = pushVerdictArgument(['ZUNIONSTORE', destination], keys);
|
||||
|
||||
if (options?.WEIGHTS) {
|
||||
|
@@ -1,9 +1,10 @@
|
||||
import { TransformArgumentsReply } from '.';
|
||||
import { transformReplySortedSetWithScores } from './generic-transformers';
|
||||
import { transformArguments as transformZUnionArguments } from './ZUNION';
|
||||
|
||||
export { FIRST_KEY_INDEX, IS_READ_ONLY } from './ZUNION';
|
||||
|
||||
export function transformArguments(...args: Parameters<typeof transformZUnionArguments>): Array<string> {
|
||||
export function transformArguments(...args: Parameters<typeof transformZUnionArguments>): TransformArgumentsReply {
|
||||
return [
|
||||
...transformZUnionArguments(...args),
|
||||
'WITHSCORES'
|
||||
|
@@ -20,6 +20,10 @@ export function transformReplyString(reply: string): string {
|
||||
return reply;
|
||||
}
|
||||
|
||||
export function transformReplyBuffer(reply: Buffer): Buffer {
|
||||
return reply;
|
||||
}
|
||||
|
||||
export function transformReplyStringNull(reply: string | null): string | null {
|
||||
return reply;
|
||||
}
|
||||
@@ -352,11 +356,11 @@ export function pushStringTuplesArguments(args: Array<string>, tuples: StringTup
|
||||
return args;
|
||||
}
|
||||
|
||||
export function pushVerdictArguments(args: TransformArgumentsReply, value: string | Array<string>): TransformArgumentsReply {
|
||||
if (typeof value === 'string') {
|
||||
args.push(value);
|
||||
} else {
|
||||
export function pushVerdictArguments(args: TransformArgumentsReply, value: string | Buffer | Array<string | Buffer>): TransformArgumentsReply {
|
||||
if (Array.isArray(value)) {
|
||||
args.push(...value);
|
||||
} else {
|
||||
args.push(value);
|
||||
}
|
||||
|
||||
return args;
|
||||
|
@@ -61,6 +61,7 @@ import * as GEOPOS from './GEOPOS';
|
||||
import * as GEOSEARCH_WITH from './GEOSEARCH_WITH';
|
||||
import * as GEOSEARCH from './GEOSEARCH';
|
||||
import * as GEOSEARCHSTORE from './GEOSEARCHSTORE';
|
||||
import * as GET_BUFFER from './GET_BUFFER';
|
||||
import * as GET from './GET';
|
||||
import * as GETBIT from './GETBIT';
|
||||
import * as GETDEL from './GETDEL';
|
||||
@@ -370,6 +371,8 @@ export default {
|
||||
geoSearch: GEOSEARCH,
|
||||
GEOSEARCHSTORE,
|
||||
geoSearchStore: GEOSEARCHSTORE,
|
||||
GET_BUFFER,
|
||||
getBuffer: GET_BUFFER,
|
||||
GET,
|
||||
get: GET,
|
||||
GETBIT,
|
||||
@@ -733,15 +736,16 @@ export default {
|
||||
zUnionStore: ZUNIONSTORE
|
||||
};
|
||||
|
||||
export type RedisReply = string | number | Array<RedisReply> | null | undefined;
|
||||
export type RedisReply = string | number | Buffer | Array<RedisReply> | null | undefined;
|
||||
|
||||
export type TransformArgumentsReply = Array<string> & { preserve?: unknown };
|
||||
export type TransformArgumentsReply = Array<string | Buffer> & { preserve?: unknown };
|
||||
|
||||
export interface RedisCommand {
|
||||
FIRST_KEY_INDEX?: number | ((...args: Array<any>) => string);
|
||||
IS_READ_ONLY?: boolean;
|
||||
transformArguments(...args: Array<any>): TransformArgumentsReply;
|
||||
transformReply(reply: RedisReply, preserved: unknown): any;
|
||||
BUFFER_MODE?: boolean;
|
||||
transformReply(reply: RedisReply, preserved?: unknown): any;
|
||||
}
|
||||
|
||||
export interface RedisCommands {
|
||||
|
@@ -1,6 +1,5 @@
|
||||
import { strict as assert } from 'assert';
|
||||
import RedisMultiCommand from './multi-command';
|
||||
import { encodeCommand } from './commander';
|
||||
import { WatchError } from './errors';
|
||||
import { spy } from 'sinon';
|
||||
import { SQUARE_SCRIPT } from './client.spec';
|
||||
@@ -10,11 +9,11 @@ describe('Multi Command', () => {
|
||||
it('simple', async () => {
|
||||
const multi = RedisMultiCommand.create((queue, symbol) => {
|
||||
assert.deepEqual(
|
||||
queue.map(({encodedCommand}) => encodedCommand),
|
||||
queue.map(({ args }) => args),
|
||||
[
|
||||
encodeCommand(['MULTI']),
|
||||
encodeCommand(['PING']),
|
||||
encodeCommand(['EXEC']),
|
||||
['MULTI'],
|
||||
['PING'],
|
||||
['EXEC'],
|
||||
]
|
||||
);
|
||||
|
||||
@@ -55,8 +54,8 @@ describe('Multi Command', () => {
|
||||
it('execAsPipeline', async () => {
|
||||
const multi = RedisMultiCommand.create(queue => {
|
||||
assert.deepEqual(
|
||||
queue.map(({encodedCommand}) => encodedCommand),
|
||||
[encodeCommand(['PING'])]
|
||||
queue.map(({ args }) => args),
|
||||
[['PING']]
|
||||
);
|
||||
|
||||
return Promise.resolve(['PONG']);
|
||||
@@ -75,8 +74,8 @@ describe('Multi Command', () => {
|
||||
it('simple', async () => {
|
||||
const multi = RedisMultiCommand.create(queue => {
|
||||
assert.deepEqual(
|
||||
queue.map(({encodedCommand}) => encodedCommand),
|
||||
[encodeCommand(['PING'])]
|
||||
queue.map(({ args }) => args),
|
||||
[['PING']]
|
||||
);
|
||||
|
||||
return Promise.resolve(['PONG']);
|
||||
@@ -111,10 +110,10 @@ describe('Multi Command', () => {
|
||||
assert.deepEqual(
|
||||
await new MultiWithScript(queue => {
|
||||
assert.deepEqual(
|
||||
queue.map(({encodedCommand}) => encodedCommand),
|
||||
queue.map(({ args }) => args),
|
||||
[
|
||||
encodeCommand(['EVAL', SQUARE_SCRIPT.SCRIPT, '0', '2']),
|
||||
encodeCommand(['EVALSHA', SQUARE_SCRIPT.SHA1, '0', '3']),
|
||||
['EVAL', SQUARE_SCRIPT.SCRIPT, '0', '2'],
|
||||
['EVALSHA', SQUARE_SCRIPT.SHA1, '0', '3'],
|
||||
]
|
||||
);
|
||||
|
||||
|
@@ -2,7 +2,7 @@ import COMMANDS, { TransformArgumentsReply } from './commands';
|
||||
import { RedisCommand, RedisModules, RedisReply } from './commands';
|
||||
import { RedisLuaScript, RedisLuaScripts } from './lua-script';
|
||||
import { RedisClientOptions } from './client';
|
||||
import { extendWithModulesAndScripts, extendWithDefaultCommands, encodeCommand } from './commander';
|
||||
import { extendWithModulesAndScripts, extendWithDefaultCommands } from './commander';
|
||||
import { WatchError } from './errors';
|
||||
|
||||
type RedisMultiCommandSignature<C extends RedisCommand, M extends RedisModules, S extends RedisLuaScripts> = (...args: Parameters<C['transformArguments']>) => RedisMultiCommandType<M, S>;
|
||||
@@ -24,7 +24,7 @@ type WithScripts<M extends RedisModules, S extends RedisLuaScripts> = {
|
||||
export type RedisMultiCommandType<M extends RedisModules, S extends RedisLuaScripts> = RedisMultiCommand & WithCommands<M, S> & WithModules<M, S> & WithScripts<M, S>;
|
||||
|
||||
export interface MultiQueuedCommand {
|
||||
encodedCommand: string;
|
||||
args: TransformArgumentsReply;
|
||||
preservedArguments?: unknown;
|
||||
transformReply?: RedisCommand['transformReply'];
|
||||
}
|
||||
@@ -62,7 +62,9 @@ export default class RedisMultiCommand<M extends RedisModules = RedisModules, S
|
||||
|
||||
const scriptArguments = script.transformArguments(...args);
|
||||
transformedArguments.push(...scriptArguments);
|
||||
transformedArguments.preserve = scriptArguments.preserve;
|
||||
if (scriptArguments.preserve) {
|
||||
transformedArguments.preserve = scriptArguments.preserve;
|
||||
}
|
||||
|
||||
return this.addCommand(
|
||||
transformedArguments,
|
||||
@@ -119,7 +121,7 @@ export default class RedisMultiCommand<M extends RedisModules = RedisModules, S
|
||||
this.#v4.addCommand = this.addCommand.bind(this);
|
||||
(this as any).addCommand = (...args: Array<unknown>): this => {
|
||||
this.#queue.push({
|
||||
encodedCommand: encodeCommand(args.flat() as Array<string>)
|
||||
args: args.flat() as Array<string>
|
||||
});
|
||||
return this;
|
||||
}
|
||||
@@ -153,7 +155,7 @@ export default class RedisMultiCommand<M extends RedisModules = RedisModules, S
|
||||
|
||||
addCommand(args: TransformArgumentsReply, transformReply?: RedisCommand['transformReply']): this {
|
||||
this.#queue.push({
|
||||
encodedCommand: encodeCommand(args),
|
||||
args,
|
||||
preservedArguments: args.preserve,
|
||||
transformReply
|
||||
});
|
||||
@@ -170,13 +172,9 @@ export default class RedisMultiCommand<M extends RedisModules = RedisModules, S
|
||||
|
||||
const queue = this.#queue.splice(0),
|
||||
rawReplies = await this.#executor([
|
||||
{
|
||||
encodedCommand: encodeCommand(['MULTI'])
|
||||
},
|
||||
{ args: ['MULTI'] },
|
||||
...queue,
|
||||
{
|
||||
encodedCommand: encodeCommand(['EXEC'])
|
||||
}
|
||||
{ args: ['EXEC'] }
|
||||
], Symbol('[RedisMultiCommand] Chain ID')),
|
||||
execReply = rawReplies[rawReplies.length - 1] as (null | Array<RedisReply>);
|
||||
|
||||
|
@@ -91,10 +91,8 @@ export default class RedisSocket extends EventEmitter {
|
||||
return this.#isOpen;
|
||||
}
|
||||
|
||||
get chunkRecommendedSize(): number {
|
||||
if (!this.#socket) return 0;
|
||||
|
||||
return this.#socket.writableHighWaterMark - this.#socket.writableLength;
|
||||
get isSocketExists(): boolean {
|
||||
return !!this.#socket;
|
||||
}
|
||||
|
||||
constructor(initiator?: RedisSocketInitiator, options?: RedisSocketOptions) {
|
||||
@@ -214,12 +212,12 @@ export default class RedisSocket extends EventEmitter {
|
||||
.catch(err => this.emit('error', err));
|
||||
}
|
||||
|
||||
write(encodedCommands: string): boolean {
|
||||
write(toWrite: string | Buffer): boolean {
|
||||
if (!this.#socket) {
|
||||
throw new ClientClosedError();
|
||||
}
|
||||
|
||||
return this.#socket.write(encodedCommands);
|
||||
return this.#socket.write(toWrite);
|
||||
}
|
||||
|
||||
async disconnect(ignoreIsOpen = false): Promise<void> {
|
||||
@@ -251,4 +249,22 @@ export default class RedisSocket extends EventEmitter {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
#isCorked = false;
|
||||
|
||||
cork(): void {
|
||||
if (!this.#socket) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.#isCorked) {
|
||||
this.#socket.cork();
|
||||
this.#isCorked = true;
|
||||
|
||||
queueMicrotask(() => {
|
||||
this.#socket?.uncork();
|
||||
this.#isCorked = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
2
lib/ts-declarations/cluster-key-slot.d.ts
vendored
2
lib/ts-declarations/cluster-key-slot.d.ts
vendored
@@ -1,3 +1,3 @@
|
||||
declare module 'cluster-key-slot' {
|
||||
export default function calculateSlot(key: string): number;
|
||||
export default function calculateSlot(key: string | Buffer): number;
|
||||
}
|
||||
|
2
lib/ts-declarations/redis-parser.d.ts
vendored
2
lib/ts-declarations/redis-parser.d.ts
vendored
@@ -8,6 +8,8 @@ declare module 'redis-parser' {
|
||||
export default class RedisParser {
|
||||
constructor(callbacks: RedisParserCallbacks);
|
||||
|
||||
setReturnBuffers(returnBuffers?: boolean): void;
|
||||
|
||||
execute(buffer: Buffer): void;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user