import COMMANDS from './commands/cluster'; import { RedisCommand, RedisModule, RedisModules } from './commands'; import { RedisCommandSignature } from './client'; import { RedisSocketOptions } from './socket'; import RedisClusterSlots from './cluster-slots'; export interface RedisClusterOptions { rootNodes: Array; modules?: M; useReplicas?: boolean; maxCommandRedirections?: number; } type WithCommands = { [P in keyof typeof COMMANDS]: RedisCommandSignature<(typeof COMMANDS)[P]> }; type WithModules> = { [P in keyof M[number]]: RedisCommandSignature }; export type RedisClusterType = WithCommands & WithModules & RedisCluster; export default class RedisCluster { static defineCommand(on: any, name: string, command: RedisCommand): void { on[name] = async function (...args: Array): Promise { const transformedArguments = command.transformArguments(...args); return command.transformReply( await this.sendCommand( transformedArguments, command.FIRST_KEY_INDEX, command.IS_READ_ONLY ) ); }; } static create(options: RedisClusterOptions): RedisClusterType { return new RedisCluster(options); } readonly #options: RedisClusterOptions; readonly #slots: RedisClusterSlots; constructor(options: RedisClusterOptions) { this.#options = options; this.#slots = new RedisClusterSlots(options); } async connect(): Promise { return this.#slots.connect(); } async sendCommand(args: Array, firstKeyIndex?: number, isReadonly?: boolean, redirections: number = 0): Promise { const firstKey = firstKeyIndex ? args[firstKeyIndex] : undefined, client = this.#slots.getClient(firstKey, isReadonly); try { return await client.sendCommand(args); } catch (err) { if (err.message.startsWith('ASK')) { // TODO } else if (err.message.startsWith('MOVED')) { await this.#slots.discover(client); if (redirections < (this.#options.maxCommandRedirections ?? 16)) { return this.sendCommand(args, firstKeyIndex, isReadonly, redirections + 1); } } throw err; } } disconnect(): Promise { return this.#slots.disconnect(); } } for (const [name, command] of Object.entries(COMMANDS)) { RedisCluster.defineCommand(RedisCluster.prototype, name, command); }