diff --git a/package-lock.json b/package-lock.json
index a9f846e7a8..6d2460ed19 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8520,7 +8520,7 @@
},
"packages/bloom": {
"name": "@redis/bloom",
- "version": "5.1.0",
+ "version": "5.1.1",
"license": "MIT",
"devDependencies": {
"@redis/test-utils": "*"
@@ -8529,12 +8529,12 @@
"node": ">= 18"
},
"peerDependencies": {
- "@redis/client": "^5.1.0"
+ "@redis/client": "^5.1.1"
}
},
"packages/client": {
"name": "@redis/client",
- "version": "5.1.0",
+ "version": "5.1.1",
"license": "MIT",
"dependencies": {
"cluster-key-slot": "1.1.2"
@@ -8550,7 +8550,7 @@
},
"packages/entraid": {
"name": "@redis/entraid",
- "version": "5.1.0",
+ "version": "5.1.1",
"license": "MIT",
"dependencies": {
"@azure/identity": "^4.7.0",
@@ -8569,7 +8569,7 @@
"node": ">= 18"
},
"peerDependencies": {
- "@redis/client": "^5.1.0"
+ "@redis/client": "^5.1.1"
}
},
"packages/entraid/node_modules/@types/node": {
@@ -8606,7 +8606,7 @@
},
"packages/json": {
"name": "@redis/json",
- "version": "5.1.0",
+ "version": "5.1.1",
"license": "MIT",
"devDependencies": {
"@redis/test-utils": "*"
@@ -8615,18 +8615,18 @@
"node": ">= 18"
},
"peerDependencies": {
- "@redis/client": "^5.1.0"
+ "@redis/client": "^5.1.1"
}
},
"packages/redis": {
- "version": "5.0.1",
+ "version": "5.1.1",
"license": "MIT",
"dependencies": {
- "@redis/bloom": "5.1.0",
- "@redis/client": "5.1.0",
- "@redis/json": "5.1.0",
- "@redis/search": "5.1.0",
- "@redis/time-series": "5.1.0"
+ "@redis/bloom": "5.1.1",
+ "@redis/client": "5.1.1",
+ "@redis/json": "5.1.1",
+ "@redis/search": "5.1.1",
+ "@redis/time-series": "5.1.1"
},
"engines": {
"node": ">= 18"
@@ -8634,7 +8634,7 @@
},
"packages/search": {
"name": "@redis/search",
- "version": "5.1.0",
+ "version": "5.1.1",
"license": "MIT",
"devDependencies": {
"@redis/test-utils": "*"
@@ -8643,7 +8643,7 @@
"node": ">= 18"
},
"peerDependencies": {
- "@redis/client": "^5.1.0"
+ "@redis/client": "^5.1.1"
}
},
"packages/test-utils": {
@@ -8712,7 +8712,7 @@
},
"packages/time-series": {
"name": "@redis/time-series",
- "version": "5.1.0",
+ "version": "5.1.1",
"license": "MIT",
"devDependencies": {
"@redis/test-utils": "*"
@@ -8721,7 +8721,7 @@
"node": ">= 18"
},
"peerDependencies": {
- "@redis/client": "^5.1.0"
+ "@redis/client": "^5.1.1"
}
}
}
diff --git a/packages/bloom/package.json b/packages/bloom/package.json
index 3c8e37c5b7..547e5ee64e 100644
--- a/packages/bloom/package.json
+++ b/packages/bloom/package.json
@@ -1,6 +1,6 @@
{
"name": "@redis/bloom",
- "version": "5.1.0",
+ "version": "5.1.1",
"license": "MIT",
"main": "./dist/lib/index.js",
"types": "./dist/lib/index.d.ts",
@@ -12,7 +12,7 @@
"test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'"
},
"peerDependencies": {
- "@redis/client": "^5.1.0"
+ "@redis/client": "^5.1.1"
},
"devDependencies": {
"@redis/test-utils": "*"
diff --git a/packages/client/lib/client/index.spec.ts b/packages/client/lib/client/index.spec.ts
index cc052dd5b5..4f752210db 100644
--- a/packages/client/lib/client/index.spec.ts
+++ b/packages/client/lib/client/index.spec.ts
@@ -89,8 +89,8 @@ describe('Client', () => {
&& expected?.credentialsProvider?.type === 'async-credentials-provider') {
// Compare the actual output of the credentials functions
- const resultCreds = await result.credentialsProvider.credentials();
- const expectedCreds = await expected.credentialsProvider.credentials();
+ const resultCreds = await result.credentialsProvider?.credentials();
+ const expectedCreds = await expected.credentialsProvider?.credentials();
assert.deepEqual(resultCreds, expectedCreds);
} else {
assert.fail('Credentials provider type mismatch');
diff --git a/packages/client/lib/client/index.ts b/packages/client/lib/client/index.ts
index 8d98aa8ed2..a446ad8e75 100644
--- a/packages/client/lib/client/index.ts
+++ b/packages/client/lib/client/index.ts
@@ -4,7 +4,7 @@ import { BasicAuth, CredentialsError, CredentialsProvider, StreamingCredentialsP
import RedisCommandsQueue, { CommandOptions } from './commands-queue';
import { EventEmitter } from 'node:events';
import { attachConfig, functionArgumentsPrefix, getTransformReply, scriptArgumentsPrefix } from '../commander';
-import { ClientClosedError, ClientOfflineError, DisconnectsClientError, SimpleError, WatchError } from '../errors';
+import { ClientClosedError, ClientOfflineError, DisconnectsClientError, WatchError } from '../errors';
import { URL } from 'node:url';
import { TcpSocketConnectOpts } from 'node:net';
import { PUBSUB_TYPE, PubSubType, PubSubListener, PubSubTypeListeners, ChannelListeners } from './pub-sub';
@@ -626,14 +626,10 @@ export default class RedisClient<
if (!this.#options?.disableClientInfo) {
commands.push({
cmd: ['CLIENT', 'SETINFO', 'LIB-VER', version],
- errorHandler: (err: Error) => {
- // Only throw if not a SimpleError - unknown subcommand
- // Client libraries are expected to ignore failures
- // of type SimpleError - unknown subcommand, which are
- // expected from older servers ( < v7 )
- if (!(err instanceof SimpleError) || !err.isUnknownSubcommand()) {
- throw err;
- }
+ errorHandler: () => {
+ // Client libraries are expected to pipeline this command
+ // after authentication on all connections and ignore failures
+ // since they could be connected to an older version that doesn't support them.
}
});
@@ -646,14 +642,10 @@ export default class RedisClient<
? `node-redis(${this.#options.clientInfoTag})`
: 'node-redis'
],
- errorHandler: (err: Error) => {
- // Only throw if not a SimpleError - unknown subcommand
- // Client libraries are expected to ignore failures
- // of type SimpleError - unknown subcommand, which are
- // expected from older servers ( < v7 )
- if (!(err instanceof SimpleError) || !err.isUnknownSubcommand()) {
- throw err;
- }
+ errorHandler: () => {
+ // Client libraries are expected to pipeline this command
+ // after authentication on all connections and ignore failures
+ // since they could be connected to an older version that doesn't support them.
}
});
}
diff --git a/packages/client/lib/cluster/index.ts b/packages/client/lib/cluster/index.ts
index f738562926..c2c251810e 100644
--- a/packages/client/lib/cluster/index.ts
+++ b/packages/client/lib/cluster/index.ts
@@ -188,7 +188,7 @@ export default class RedisCluster<
const parser = new BasicCommandParser();
command.parseCommand(parser, ...args);
- return this._self.#execute(
+ return this._self._execute(
parser.firstKey,
command.IS_READ_ONLY,
this._commandOptions,
@@ -204,7 +204,7 @@ export default class RedisCluster<
const parser = new BasicCommandParser();
command.parseCommand(parser, ...args);
- return this._self.#execute(
+ return this._self._execute(
parser.firstKey,
command.IS_READ_ONLY,
this._self._commandOptions,
@@ -222,7 +222,7 @@ export default class RedisCluster<
parser.push(...prefix);
fn.parseCommand(parser, ...args);
- return this._self.#execute(
+ return this._self._execute(
parser.firstKey,
fn.IS_READ_ONLY,
this._self._commandOptions,
@@ -240,7 +240,7 @@ export default class RedisCluster<
parser.push(...prefix);
script.parseCommand(parser, ...args);
- return this._self.#execute(
+ return this._self._execute(
parser.firstKey,
script.IS_READ_ONLY,
this._commandOptions,
@@ -293,9 +293,9 @@ export default class RedisCluster<
return RedisCluster.factory(options)(options);
}
- readonly #options: RedisClusterOptions;
+ readonly _options: RedisClusterOptions;
- readonly #slots: RedisClusterSlots;
+ readonly _slots: RedisClusterSlots;
private _self = this;
private _commandOptions?: ClusterCommandOptions;
@@ -305,11 +305,11 @@ export default class RedisCluster<
* Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific node (master or replica).
*/
get slots() {
- return this._self.#slots.slots;
+ return this._self._slots.slots;
}
get clientSideCache() {
- return this._self.#slots.clientSideCache;
+ return this._self._slots.clientSideCache;
}
/**
@@ -317,7 +317,7 @@ export default class RedisCluster<
* Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific master node.
*/
get masters() {
- return this._self.#slots.masters;
+ return this._self._slots.masters;
}
/**
@@ -325,7 +325,7 @@ export default class RedisCluster<
* Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific replica node.
*/
get replicas() {
- return this._self.#slots.replicas;
+ return this._self._slots.replicas;
}
/**
@@ -333,25 +333,25 @@ export default class RedisCluster<
* Use with {@link RedisCluster.prototype.nodeClient} to get the client for a specific node (master or replica).
*/
get nodeByAddress() {
- return this._self.#slots.nodeByAddress;
+ return this._self._slots.nodeByAddress;
}
/**
* The current pub/sub node.
*/
get pubSubNode() {
- return this._self.#slots.pubSubNode;
+ return this._self._slots.pubSubNode;
}
get isOpen() {
- return this._self.#slots.isOpen;
+ return this._self._slots.isOpen;
}
constructor(options: RedisClusterOptions) {
super();
- this.#options = options;
- this.#slots = new RedisClusterSlots(options, this.emit.bind(this));
+ this._options = options;
+ this._slots = new RedisClusterSlots(options, this.emit.bind(this));
if (options?.commandOptions) {
this._commandOptions = options.commandOptions;
@@ -366,14 +366,14 @@ export default class RedisCluster<
_TYPE_MAPPING extends TypeMapping = TYPE_MAPPING
>(overrides?: Partial>) {
return new (Object.getPrototypeOf(this).constructor)({
- ...this._self.#options,
+ ...this._self._options,
commandOptions: this._commandOptions,
...overrides
}) as RedisClusterType<_M, _F, _S, _RESP, _TYPE_MAPPING>;
}
async connect() {
- await this._self.#slots.connect();
+ await this._self._slots.connect();
return this as unknown as RedisClusterType;
}
@@ -429,7 +429,7 @@ export default class RedisCluster<
// return this._commandOptionsProxy('policies', policies);
// }
- #handleAsk(
+ _handleAsk(
fn: (client: RedisClientType, opts?: ClusterCommandOptions) => Promise
) {
return async (client: RedisClientType, options?: ClusterCommandOptions) => {
@@ -450,14 +450,14 @@ export default class RedisCluster<
};
}
- async #execute(
+ async _execute(
firstKey: RedisArgument | undefined,
isReadonly: boolean | undefined,
options: ClusterCommandOptions | undefined,
fn: (client: RedisClientType, opts?: ClusterCommandOptions) => Promise
): Promise {
- const maxCommandRedirections = this.#options.maxCommandRedirections ?? 16;
- let client = await this.#slots.getClient(firstKey, isReadonly);
+ const maxCommandRedirections = this._options.maxCommandRedirections ?? 16;
+ let client = await this._slots.getClient(firstKey, isReadonly);
let i = 0;
let myFn = fn;
@@ -475,10 +475,10 @@ export default class RedisCluster<
if (err.message.startsWith('ASK')) {
const address = err.message.substring(err.message.lastIndexOf(' ') + 1);
- let redirectTo = await this.#slots.getMasterByAddress(address);
+ let redirectTo = await this._slots.getMasterByAddress(address);
if (!redirectTo) {
- await this.#slots.rediscover(client);
- redirectTo = await this.#slots.getMasterByAddress(address);
+ await this._slots.rediscover(client);
+ redirectTo = await this._slots.getMasterByAddress(address);
}
if (!redirectTo) {
@@ -486,13 +486,13 @@ export default class RedisCluster<
}
client = redirectTo;
- myFn = this.#handleAsk(fn);
+ myFn = this._handleAsk(fn);
continue;
}
if (err.message.startsWith('MOVED')) {
- await this.#slots.rediscover(client);
- client = await this.#slots.getClient(firstKey, isReadonly);
+ await this._slots.rediscover(client);
+ client = await this._slots.getClient(firstKey, isReadonly);
continue;
}
@@ -508,7 +508,7 @@ export default class RedisCluster<
options?: ClusterCommandOptions,
// defaultPolicies?: CommandPolicies
): Promise {
- return this._self.#execute(
+ return this._self._execute(
firstKey,
isReadonly,
options,
@@ -520,11 +520,11 @@ export default class RedisCluster<
type Multi = new (...args: ConstructorParameters) => RedisClusterMultiCommandType<[], M, F, S, RESP, TYPE_MAPPING>;
return new ((this as any).Multi as Multi)(
async (firstKey, isReadonly, commands) => {
- const client = await this._self.#slots.getClient(firstKey, isReadonly);
+ const client = await this._self._slots.getClient(firstKey, isReadonly);
return client._executeMulti(commands);
},
async (firstKey, isReadonly, commands) => {
- const client = await this._self.#slots.getClient(firstKey, isReadonly);
+ const client = await this._self._slots.getClient(firstKey, isReadonly);
return client._executePipeline(commands);
},
routing,
@@ -539,7 +539,7 @@ export default class RedisCluster<
listener: PubSubListener,
bufferMode?: T
) {
- return (await this._self.#slots.getPubSubClient())
+ return (await this._self._slots.getPubSubClient())
.SUBSCRIBE(channels, listener, bufferMode);
}
@@ -550,7 +550,7 @@ export default class RedisCluster<
listener?: PubSubListener,
bufferMode?: T
) {
- return this._self.#slots.executeUnsubscribeCommand(client =>
+ return this._self._slots.executeUnsubscribeCommand(client =>
client.UNSUBSCRIBE(channels, listener, bufferMode)
);
}
@@ -562,7 +562,7 @@ export default class RedisCluster<
listener: PubSubListener,
bufferMode?: T
) {
- return (await this._self.#slots.getPubSubClient())
+ return (await this._self._slots.getPubSubClient())
.PSUBSCRIBE(patterns, listener, bufferMode);
}
@@ -573,7 +573,7 @@ export default class RedisCluster<
listener?: PubSubListener,
bufferMode?: T
) {
- return this._self.#slots.executeUnsubscribeCommand(client =>
+ return this._self._slots.executeUnsubscribeCommand(client =>
client.PUNSUBSCRIBE(patterns, listener, bufferMode)
);
}
@@ -585,9 +585,9 @@ export default class RedisCluster<
listener: PubSubListener,
bufferMode?: T
) {
- const maxCommandRedirections = this._self.#options.maxCommandRedirections ?? 16,
+ const maxCommandRedirections = this._self._options.maxCommandRedirections ?? 16,
firstChannel = Array.isArray(channels) ? channels[0] : channels;
- let client = await this._self.#slots.getShardedPubSubClient(firstChannel);
+ let client = await this._self._slots.getShardedPubSubClient(firstChannel);
for (let i = 0; ; i++) {
try {
return await client.SSUBSCRIBE(channels, listener, bufferMode);
@@ -597,8 +597,8 @@ export default class RedisCluster<
}
if (err.message.startsWith('MOVED')) {
- await this._self.#slots.rediscover(client);
- client = await this._self.#slots.getShardedPubSubClient(firstChannel);
+ await this._self._slots.rediscover(client);
+ client = await this._self._slots.getShardedPubSubClient(firstChannel);
continue;
}
@@ -614,7 +614,7 @@ export default class RedisCluster<
listener?: PubSubListener,
bufferMode?: T
) {
- return this._self.#slots.executeShardedUnsubscribeCommand(
+ return this._self._slots.executeShardedUnsubscribeCommand(
Array.isArray(channels) ? channels[0] : channels,
client => client.SUNSUBSCRIBE(channels, listener, bufferMode)
);
@@ -626,28 +626,28 @@ export default class RedisCluster<
* @deprecated Use `close` instead.
*/
quit() {
- return this._self.#slots.quit();
+ return this._self._slots.quit();
}
/**
* @deprecated Use `destroy` instead.
*/
disconnect() {
- return this._self.#slots.disconnect();
+ return this._self._slots.disconnect();
}
close() {
- this._self.#slots.clientSideCache?.onPoolClose();
- return this._self.#slots.close();
+ this._self._slots.clientSideCache?.onPoolClose();
+ return this._self._slots.close();
}
destroy() {
- this._self.#slots.clientSideCache?.onPoolClose();
- return this._self.#slots.destroy();
+ this._self._slots.clientSideCache?.onPoolClose();
+ return this._self._slots.destroy();
}
nodeClient(node: ShardNode) {
- return this._self.#slots.nodeClient(node);
+ return this._self._slots.nodeClient(node);
}
/**
@@ -655,7 +655,7 @@ export default class RedisCluster<
* Userful for running "forward" commands (like PUBLISH) on a random node.
*/
getRandomNode() {
- return this._self.#slots.getRandomNode();
+ return this._self._slots.getRandomNode();
}
/**
@@ -663,7 +663,7 @@ export default class RedisCluster<
* Useful for running readonly commands on a slot.
*/
getSlotRandomNode(slot: number) {
- return this._self.#slots.getSlotRandomNode(slot);
+ return this._self._slots.getSlotRandomNode(slot);
}
/**
diff --git a/packages/client/lib/errors.ts b/packages/client/lib/errors.ts
index 4f05f62ac4..db37ec1a9b 100644
--- a/packages/client/lib/errors.ts
+++ b/packages/client/lib/errors.ts
@@ -70,11 +70,7 @@ export class ErrorReply extends Error {
}
}
-export class SimpleError extends ErrorReply {
- isUnknownSubcommand(): boolean {
- return this.message.toLowerCase().indexOf('err unknown subcommand') !== -1;
- }
-}
+export class SimpleError extends ErrorReply {}
export class BlobError extends ErrorReply {}
diff --git a/packages/client/lib/sentinel/index.spec.ts b/packages/client/lib/sentinel/index.spec.ts
index a2e52b774b..a9bdc8cc95 100644
--- a/packages/client/lib/sentinel/index.spec.ts
+++ b/packages/client/lib/sentinel/index.spec.ts
@@ -197,7 +197,6 @@ describe(`test with scripts`, () => {
}, GLOBAL.SENTINEL.WITH_SCRIPT)
});
-
describe(`test with functions`, () => {
testUtils.testWithClientSentinel('with function', async sentinel => {
await sentinel.functionLoad(
@@ -377,12 +376,9 @@ describe(`test with masterPoolSize 2`, () => {
}, GLOBAL.SENTINEL.WITH_MASTER_POOL_SIZE_2);
});
-
-// TODO: Figure out how to modify the test utils
-// so it would have fine grained controll over
-// sentinel
-// it should somehow replicate the `SentinelFramework` object functionallities
async function steadyState(frame: SentinelFramework) {
+ // wait a bit to ensure that sentinels are seeing eachother
+ await setTimeout(2000)
let checkedMaster = false;
let checkedReplicas = false;
while (!checkedMaster || !checkedReplicas) {
@@ -430,7 +426,7 @@ async function steadyState(frame: SentinelFramework) {
}
}
-describe.skip('legacy tests', () => {
+describe('legacy tests', () => {
const config: RedisSentinelConfig = { sentinelName: "test", numberOfNodes: 3, password: undefined };
const frame = new SentinelFramework(config);
let tracer = new Array();
@@ -439,42 +435,30 @@ describe.skip('legacy tests', () => {
let longestTestDelta = 0;
let last: number;
- before(async function () {
- this.timeout(15000);
-
- last = Date.now();
-
- function deltaMeasurer() {
- const delta = Date.now() - last;
- if (delta > longestDelta) {
- longestDelta = delta;
- }
- if (delta > longestTestDelta) {
- longestTestDelta = delta;
- }
- if (!stopMeasuringBlocking) {
- last = Date.now();
- setImmediate(deltaMeasurer);
- }
- }
- setImmediate(deltaMeasurer);
- await frame.spawnRedisSentinel();
- });
-
- after(async function () {
- this.timeout(15000);
-
- stopMeasuringBlocking = true;
-
- await frame.cleanup();
- })
describe('Sentinel Client', function () {
let sentinel: RedisSentinelType | undefined;
beforeEach(async function () {
- this.timeout(0);
-
+ this.timeout(15000);
+
+ last = Date.now();
+
+ function deltaMeasurer() {
+ const delta = Date.now() - last;
+ if (delta > longestDelta) {
+ longestDelta = delta;
+ }
+ if (delta > longestTestDelta) {
+ longestTestDelta = delta;
+ }
+ if (!stopMeasuringBlocking) {
+ last = Date.now();
+ setImmediate(deltaMeasurer);
+ }
+ }
+ setImmediate(deltaMeasurer);
+ await frame.spawnRedisSentinel();
await frame.getAllRunning();
await steadyState(frame);
longestTestDelta = 0;
@@ -522,6 +506,10 @@ describe.skip('legacy tests', () => {
await sentinel.destroy();
sentinel = undefined;
}
+
+ stopMeasuringBlocking = true;
+
+ await frame.cleanup();
})
it('use', async function () {
@@ -863,7 +851,6 @@ describe.skip('legacy tests', () => {
it('shutdown sentinel node', async function () {
this.timeout(60000);
-
sentinel = frame.getSentinelClient();
sentinel.setTracer(tracer);
sentinel.on("error", () => { });
@@ -1020,7 +1007,7 @@ describe.skip('legacy tests', () => {
this.timeout(30000);
const csc = new BasicPooledClientSideCache();
- sentinel = frame.getSentinelClient({nodeClientOptions: {RESP: 3}, clientSideCache: csc, masterPoolSize: 5});
+ sentinel = frame.getSentinelClient({nodeClientOptions: {RESP: 3 as const}, RESP: 3 as const, clientSideCache: csc, masterPoolSize: 5});
await sentinel.connect();
await sentinel.set('x', 1);
diff --git a/packages/client/lib/sentinel/test-util.ts b/packages/client/lib/sentinel/test-util.ts
index 86bc5b3178..60c1a59689 100644
--- a/packages/client/lib/sentinel/test-util.ts
+++ b/packages/client/lib/sentinel/test-util.ts
@@ -4,12 +4,13 @@ import { once } from 'node:events';
import { promisify } from 'node:util';
import { exec } from 'node:child_process';
import { RedisSentinelOptions, RedisSentinelType } from './types';
-import RedisClient from '../client';
+import RedisClient, {RedisClientType} from '../client';
import RedisSentinel from '.';
import { RedisArgument, RedisFunctions, RedisModules, RedisScripts, RespVersions, TypeMapping } from '../RESP/types';
const execAsync = promisify(exec);
import RedisSentinelModule from './module'
-
+import TestUtils from '@redis/test-utils';
+import { DEBUG_MODE_ARGS } from '../test-utils'
interface ErrorWithCode extends Error {
code: string;
}
@@ -125,7 +126,6 @@ export interface RedisSentinelConfig {
sentinelServerArgument?: Array
sentinelName: string;
- sentinelQuorum?: number;
password?: string;
}
@@ -151,6 +151,7 @@ export interface SentinelController {
}
export class SentinelFramework extends DockerBase {
+ #testUtils: TestUtils;
#nodeList: Awaited> = [];
/* port -> docker info/client */
#nodeMap: Map>>>;
@@ -170,7 +171,11 @@ export class SentinelFramework extends DockerBase {
super();
this.config = config;
-
+ this.#testUtils = TestUtils.createFromConfig({
+ dockerImageName: 'redislabs/client-libs-test',
+ dockerImageVersionArgument: 'redis-version',
+ defaultDockerVersion: '8.0-M05-pre'
+ });
this.#nodeMap = new Map>>>();
this.#sentinelMap = new Map>>>();
}
@@ -190,7 +195,7 @@ export class SentinelFramework extends DockerBase {
const options: RedisSentinelOptions = {
...opts,
name: this.config.sentinelName,
- sentinelRootNodes: this.#sentinelList.map((sentinel) => { return { host: '127.0.0.1', port: sentinel.docker.port } }),
+ sentinelRootNodes: this.#sentinelList.map((sentinel) => { return { host: '127.0.0.1', port: sentinel.port } }),
passthroughClientErrorEvents: errors
}
@@ -218,11 +223,11 @@ export class SentinelFramework extends DockerBase {
throw new Error("inconsistent state with partial setup");
}
- this.#nodeList = await this.spawnRedisSentinelNodes();
- this.#nodeList.map((value) => this.#nodeMap.set(value.docker.port.toString(), value));
+ this.#nodeList = await this.spawnRedisSentinelNodes(2);
+ this.#nodeList.map((value) => this.#nodeMap.set(value.port.toString(), value));
- this.#sentinelList = await this.spawnRedisSentinelSentinels();
- this.#sentinelList.map((value) => this.#sentinelMap.set(value.docker.port.toString(), value));
+ this.#sentinelList = await this.spawnRedisSentinelSentinels(this.#nodeList[0].port, 3)
+ this.#sentinelList.map((value) => this.#sentinelMap.set(value.port.toString(), value));
this.#spawned = true;
}
@@ -234,11 +239,8 @@ export class SentinelFramework extends DockerBase {
return Promise.all(
[...this.#nodeMap!.values(), ...this.#sentinelMap!.values()].map(
- async ({ docker, client }) => {
- if (client.isOpen) {
- client.destroy();
- }
- this.dockerRemove(docker.dockerId);
+ async ({ dockerId }) => {
+ this.dockerRemove(dockerId);
}
)
).finally(async () => {
@@ -248,112 +250,33 @@ export class SentinelFramework extends DockerBase {
});
}
- protected async spawnRedisSentinelNodeDocker() {
- const imageInfo: RedisServerDockerConfig = this.config.nodeDockerConfig ?? { image: "redis/redis-stack-server", version: "latest" };
- const serverArguments: Array = this.config.nodeServerArguments ?? [];
- let environment;
- if (this.config.password !== undefined) {
- environment = `REDIS_ARGS="{port} --requirepass ${this.config.password}"`;
- } else {
- environment = 'REDIS_ARGS="{port}"';
- }
-
- const docker = await this.spawnRedisServerDocker(imageInfo, serverArguments, environment);
- const client = await RedisClient.create({
- password: this.config.password,
- socket: {
- port: docker.port
- }
- }).on("error", () => { }).connect();
-
- return {
- docker,
- client
- };
- }
-
- protected async spawnRedisSentinelNodes() {
- const master = await this.spawnRedisSentinelNodeDocker();
+ protected async spawnRedisSentinelNodes(replicasCount: number) {
+ const master = await this.#testUtils.spawnRedisServer({serverArguments: DEBUG_MODE_ARGS})
+
+ const replicas: Array = []
+ for (let i = 0; i < replicasCount; i++) {
+ const replica = await this.#testUtils.spawnRedisServer({serverArguments: DEBUG_MODE_ARGS})
+ replicas.push(replica)
- const promises: Array> = [];
+ const client = RedisClient.create({
+ socket: {
+ port: replica.port
+ }
+ })
- for (let i = 0; i < (this.config.numberOfNodes ?? 0) - 1; i++) {
- promises.push(
- this.spawnRedisSentinelNodeDocker().then(async node => {
- if (this.config.password !== undefined) {
- await node.client.configSet({'masterauth': this.config.password})
- }
- await node.client.replicaOf('127.0.0.1', master.docker.port);
- return node;
- })
- );
+ await client.connect();
+ await client.replicaOf("127.0.0.1", master.port);
+ await client.close();
}
return [
master,
- ...await Promise.all(promises)
- ];
- }
-
- protected async spawnRedisSentinelSentinelDocker() {
- const imageInfo: RedisServerDockerConfig = this.config.sentinelDockerConfig ?? { image: "redis", version: "latest" }
- let serverArguments: Array;
- if (this.config.password === undefined) {
- serverArguments = this.config.sentinelServerArgument ??
- [
- "/bin/bash",
- "-c",
- "\"touch /tmp/sentinel.conf ; /usr/local/bin/redis-sentinel /tmp/sentinel.conf {port} \""
- ];
- } else {
- serverArguments = this.config.sentinelServerArgument ??
- [
- "/bin/bash",
- "-c",
- `"touch /tmp/sentinel.conf ; /usr/local/bin/redis-sentinel /tmp/sentinel.conf {port} --requirepass ${this.config.password}"`
- ];
- }
-
- const docker = await this.spawnRedisServerDocker(imageInfo, serverArguments);
- const client = await RedisClient.create({
- modules: RedisSentinelModule,
- password: this.config.password,
- socket: {
- port: docker.port
- }
- }).on("error", () => { }).connect();
-
- return {
- docker,
- client
- };
+ ...replicas
+ ]
}
- protected async spawnRedisSentinelSentinels() {
- const quorum = this.config.sentinelQuorum?.toString() ?? "2";
- const node = this.#nodeList[0];
-
- const promises: Array> = [];
-
- for (let i = 0; i < (this.config.numberOfSentinels ?? 3); i++) {
- promises.push(
- this.spawnRedisSentinelSentinelDocker().then(async sentinel => {
- await sentinel.client.sentinel.sentinelMonitor(this.config.sentinelName, '127.0.0.1', node.docker.port.toString(), quorum);
- const options: Array<{option: RedisArgument, value: RedisArgument}> = [];
- options.push({ option: "down-after-milliseconds", value: "100" });
- options.push({ option: "failover-timeout", value: "5000" });
- if (this.config.password !== undefined) {
- options.push({ option: "auth-pass", value: this.config.password });
- }
- await sentinel.client.sentinel.sentinelSet(this.config.sentinelName, options)
- return sentinel;
- })
- );
- }
-
- return [
- ...await Promise.all(promises)
- ]
+ protected async spawnRedisSentinelSentinels(masterPort: number, sentinels: number) {
+ return this.#testUtils.spawnRedisSentinels({serverArguments: DEBUG_MODE_ARGS}, masterPort, this.config.sentinelName, sentinels)
}
async getAllRunning() {
@@ -384,90 +307,71 @@ export class SentinelFramework extends DockerBase {
}
async addSentinel() {
- const quorum = this.config.sentinelQuorum?.toString() ?? "2";
- const node = this.#nodeList[0];
- const sentinel = await this.spawnRedisSentinelSentinelDocker();
-
- await sentinel.client.sentinel.sentinelMonitor(this.config.sentinelName, '127.0.0.1', node.docker.port.toString(), quorum);
- const options: Array<{option: RedisArgument, value: RedisArgument}> = [];
- options.push({ option: "down-after-milliseconds", value: "100" });
- options.push({ option: "failover-timeout", value: "5000" });
- if (this.config.password !== undefined) {
- options.push({ option: "auth-pass", value: this.config.password });
- }
- await sentinel.client.sentinel.sentinelSet(this.config.sentinelName, options);
-
- this.#sentinelList.push(sentinel);
- this.#sentinelMap.set(sentinel.docker.port.toString(), sentinel);
+ const nodes = await this.#testUtils.spawnRedisSentinels({serverArguments: DEBUG_MODE_ARGS}, this.#nodeList[0].port, this.config.sentinelName, 1)
+ this.#sentinelList.push(nodes[0]);
+ this.#sentinelMap.set(nodes[0].port.toString(), nodes[0]);
}
async addNode() {
const masterPort = await this.getMasterPort();
- const newNode = await this.spawnRedisSentinelNodeDocker();
+ const replica = await this.#testUtils.spawnRedisServer({serverArguments: DEBUG_MODE_ARGS})
- if (this.config.password !== undefined) {
- await newNode.client.configSet({'masterauth': this.config.password})
- }
- await newNode.client.replicaOf('127.0.0.1', masterPort);
+ const client = RedisClient.create({
+ socket: {
+ port: replica.port
+ }
+ })
+
+ await client.connect();
+ await client.replicaOf("127.0.0.1", masterPort);
+ await client.close();
+
- this.#nodeList.push(newNode);
- this.#nodeMap.set(newNode.docker.port.toString(), newNode);
+ this.#nodeList.push(replica);
+ this.#nodeMap.set(replica.port.toString(), replica);
}
async getMaster(tracer?: Array): Promise {
- for (const sentinel of this.#sentinelMap!.values()) {
- let info;
-
- try {
- if (!sentinel.client.isReady) {
- continue;
- }
-
- info = await sentinel.client.sentinel.sentinelMaster(this.config.sentinelName);
- if (tracer) {
- tracer.push('getMaster: master data returned from sentinel');
- tracer.push(JSON.stringify(info, undefined, '\t'))
- }
- } catch (err) {
- console.log("getMaster: sentinelMaster call failed: " + err);
- continue;
- }
-
- const master = this.#nodeMap.get(info.port);
- if (master === undefined) {
- throw new Error(`couldn't find master node for ${info.port}`);
- }
-
- if (tracer) {
- tracer.push(`getMaster: master port is either ${info.port} or ${master.docker.port}`);
- }
+ const client = RedisClient.create({
+ name: this.config.sentinelName,
+ socket: {
+ host: "127.0.0.1",
+ port: this.#sentinelList[0].port,
+ },
+ modules: RedisSentinelModule,
+ });
+ await client.connect()
+ const info = await client.sentinel.sentinelMaster(this.config.sentinelName);
+ await client.close()
- if (!master.client.isOpen) {
- throw new Error(`Sentinel's expected master node (${info.port}) is now down`);
- }
+ const master = this.#nodeMap.get(info.port);
+ if (master === undefined) {
+ throw new Error(`couldn't find master node for ${info.port}`);
+ }
- return info.port;
+ if (tracer) {
+ tracer.push(`getMaster: master port is either ${info.port} or ${master.port}`);
}
- throw new Error("Couldn't get master");
+ return info.port;
}
async getMasterPort(tracer?: Array): Promise {
const data = await this.getMaster(tracer)
- return this.#nodeMap.get(data!)!.docker.port;
+ return this.#nodeMap.get(data!)!.port;
}
getRandomNode() {
- return this.#nodeList[Math.floor(Math.random() * this.#nodeList.length)].docker.port.toString();
+ return this.#nodeList[Math.floor(Math.random() * this.#nodeList.length)].port.toString();
}
async getRandonNonMasterNode(): Promise {
const masterPort = await this.getMasterPort();
while (true) {
const node = this.#nodeList[Math.floor(Math.random() * this.#nodeList.length)];
- if (node.docker.port != masterPort) {
- return node.docker.port.toString();
+ if (node.port != masterPort) {
+ return node.port.toString();
}
}
}
@@ -479,11 +383,7 @@ export class SentinelFramework extends DockerBase {
throw new Error("unknown node: " + id);
}
- if (node.client.isOpen) {
- node.client.destroy();
- }
-
- return await this.dockerStop(node.docker.dockerId);
+ return await this.dockerStop(node.dockerId);
}
async restartNode(id: string) {
@@ -492,15 +392,7 @@ export class SentinelFramework extends DockerBase {
throw new Error("unknown node: " + id);
}
- await this.dockerStart(node.docker.dockerId);
- if (!node.client.isOpen) {
- node.client = await RedisClient.create({
- password: this.config.password,
- socket: {
- port: node.docker.port
- }
- }).on("error", () => { }).connect();
- }
+ await this.dockerStart(node.dockerId);
}
async stopSentinel(id: string) {
@@ -509,11 +401,7 @@ export class SentinelFramework extends DockerBase {
throw new Error("unknown sentinel: " + id);
}
- if (sentinel.client.isOpen) {
- sentinel.client.destroy();
- }
-
- return await this.dockerStop(sentinel.docker.dockerId);
+ return await this.dockerStop(sentinel.dockerId);
}
async restartSentinel(id: string) {
@@ -522,16 +410,7 @@ export class SentinelFramework extends DockerBase {
throw new Error("unknown sentinel: " + id);
}
- await this.dockerStart(sentinel.docker.dockerId);
- if (!sentinel.client.isOpen) {
- sentinel.client = await RedisClient.create({
- modules: RedisSentinelModule,
- password: this.config.password,
- socket: {
- port: sentinel.docker.port
- }
- }).on("error", () => { }).connect();
- }
+ await this.dockerStart(sentinel.dockerId);
}
getNodePort(id: string) {
@@ -540,13 +419,13 @@ export class SentinelFramework extends DockerBase {
throw new Error("unknown node: " + id);
}
- return node.docker.port;
+ return node.port;
}
getAllNodesPort() {
let ports: Array = [];
for (const node of this.#nodeList) {
- ports.push(node.docker.port);
+ ports.push(node.port);
}
return ports
@@ -555,7 +434,7 @@ export class SentinelFramework extends DockerBase {
getAllDockerIds() {
let ids = new Map();
for (const node of this.#nodeList) {
- ids.set(node.docker.dockerId, node.docker.port);
+ ids.set(node.dockerId, node.port);
}
return ids;
@@ -567,43 +446,67 @@ export class SentinelFramework extends DockerBase {
throw new Error("unknown sentinel: " + id);
}
- return sentinel.docker.port;
+ return sentinel.port;
}
getAllSentinelsPort() {
let ports: Array = [];
for (const sentinel of this.#sentinelList) {
- ports.push(sentinel.docker.port);
+ ports.push(sentinel.port);
}
return ports
}
getSetinel(i: number): string {
- return this.#sentinelList[i].docker.port.toString();
+ return this.#sentinelList[i].port.toString();
}
- sentinelSentinels() {
- for (const sentinel of this.#sentinelList) {
- if (sentinel.client.isReady) {
- return sentinel.client.sentinel.sentinelSentinels(this.config.sentinelName);
- }
- }
+ async sentinelSentinels() {
+ const client = RedisClient.create({
+ name: this.config.sentinelName,
+ socket: {
+ host: "127.0.0.1",
+ port: this.#sentinelList[0].port,
+ },
+ modules: RedisSentinelModule,
+ });
+ await client.connect()
+ const sentinels = client.sentinel.sentinelSentinels(this.config.sentinelName)
+ await client.close()
+
+ return sentinels
}
- sentinelMaster() {
- for (const sentinel of this.#sentinelList) {
- if (sentinel.client.isReady) {
- return sentinel.client.sentinel.sentinelMaster(this.config.sentinelName);
- }
- }
+ async sentinelMaster() {
+ const client = RedisClient.create({
+ name: this.config.sentinelName,
+ socket: {
+ host: "127.0.0.1",
+ port: this.#sentinelList[0].port,
+ },
+ modules: RedisSentinelModule,
+ });
+ await client.connect()
+ const master = client.sentinel.sentinelMaster(this.config.sentinelName)
+ await client.close()
+
+ return master
}
- sentinelReplicas() {
- for (const sentinel of this.#sentinelList) {
- if (sentinel.client.isReady) {
- return sentinel.client.sentinel.sentinelReplicas(this.config.sentinelName);
- }
- }
+ async sentinelReplicas() {
+ const client = RedisClient.create({
+ name: this.config.sentinelName,
+ socket: {
+ host: "127.0.0.1",
+ port: this.#sentinelList[0].port,
+ },
+ modules: RedisSentinelModule,
+ });
+ await client.connect()
+ const replicas = client.sentinel.sentinelReplicas(this.config.sentinelName)
+ await client.close()
+
+ return replicas
}
}
\ No newline at end of file
diff --git a/packages/client/lib/test-utils.ts b/packages/client/lib/test-utils.ts
index 63f43ba5e6..19bbafc66e 100644
--- a/packages/client/lib/test-utils.ts
+++ b/packages/client/lib/test-utils.ts
@@ -14,7 +14,7 @@ const utils = TestUtils.createFromConfig({
export default utils;
-const DEBUG_MODE_ARGS = utils.isVersionGreaterThan([7]) ?
+export const DEBUG_MODE_ARGS = utils.isVersionGreaterThan([7]) ?
['--enable-debug-command', 'yes'] :
[];
diff --git a/packages/client/package.json b/packages/client/package.json
index dd41dc53e0..a6d44451a6 100644
--- a/packages/client/package.json
+++ b/packages/client/package.json
@@ -1,6 +1,6 @@
{
"name": "@redis/client",
- "version": "5.1.0",
+ "version": "5.1.1",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
diff --git a/packages/entraid/package.json b/packages/entraid/package.json
index ba44351b3b..43f26d1e03 100644
--- a/packages/entraid/package.json
+++ b/packages/entraid/package.json
@@ -1,6 +1,6 @@
{
"name": "@redis/entraid",
- "version": "5.1.0",
+ "version": "5.1.1",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -21,7 +21,7 @@
"@azure/msal-node": "^2.16.1"
},
"peerDependencies": {
- "@redis/client": "^5.1.0"
+ "@redis/client": "^5.1.1"
},
"devDependencies": {
"@types/express": "^4.17.21",
diff --git a/packages/json/lib/commands/helpers.ts b/packages/json/lib/commands/helpers.ts
index 26ff12f683..99579ce81c 100644
--- a/packages/json/lib/commands/helpers.ts
+++ b/packages/json/lib/commands/helpers.ts
@@ -2,7 +2,6 @@ import { isNullReply } from "@redis/client/dist/lib/commands/generic-transformer
import { BlobStringReply, NullReply, UnwrapReply } from "@redis/client/dist/lib/RESP/types";
export function transformRedisJsonNullReply(json: NullReply | BlobStringReply): NullReply | RedisJSON {
- console.log('transformRedisJsonNullReply', json)
return isNullReply(json) ? json : transformRedisJsonReply(json);
}
@@ -17,6 +16,5 @@ export function transformRedisJsonArgument(json: RedisJSON): string {
export function transformRedisJsonReply(json: BlobStringReply): RedisJSON {
const res = JSON.parse((json as unknown as UnwrapReply).toString());
- console.log('transformRedisJsonReply', json, res)
return res;
}
diff --git a/packages/json/package.json b/packages/json/package.json
index fd0b3c768f..3b473dfce0 100644
--- a/packages/json/package.json
+++ b/packages/json/package.json
@@ -1,6 +1,6 @@
{
"name": "@redis/json",
- "version": "5.1.0",
+ "version": "5.1.1",
"license": "MIT",
"main": "./dist/lib/index.js",
"types": "./dist/lib/index.d.ts",
@@ -12,7 +12,7 @@
"test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'"
},
"peerDependencies": {
- "@redis/client": "^5.1.0"
+ "@redis/client": "^5.1.1"
},
"devDependencies": {
"@redis/test-utils": "*"
diff --git a/packages/redis/README.md b/packages/redis/README.md
index d04a19b0d7..ab6b4707e6 100644
--- a/packages/redis/README.md
+++ b/packages/redis/README.md
@@ -45,13 +45,13 @@ npm install redis
| Name | Description |
| ---------------------------------------------- | ------------------------------------------------------------------------------------------- |
-| [`redis`](../redis) | The client with all the ["redis-stack"](https://github.com/redis-stack/redis-stack) modules |
-| [`@redis/client`](../client) | The base clients (i.e `RedisClient`, `RedisCluster`, etc.) |
-| [`@redis/bloom`](../bloom) | [Redis Bloom](https://redis.io/docs/data-types/probabilistic/) commands |
-| [`@redis/json`](../json) | [Redis JSON](https://redis.io/docs/data-types/json/) commands |
-| [`@redis/search`](../search) | [RediSearch](https://redis.io/docs/interact/search-and-query/) commands |
-| [`@redis/time-series`](../time-series) | [Redis Time-Series](https://redis.io/docs/data-types/timeseries/) commands |
-| [`@redis/entraid`](../entraid) | Secure token-based authentication for Redis clients using Microsoft Entra ID |
+| [`redis`](https://github.com/redis/node-redis/tree/master/packages/redis) | The client with all the ["redis-stack"](https://github.com/redis-stack/redis-stack) modules |
+| [`@redis/client`](https://github.com/redis/node-redis/tree/master/packages/client) | The base clients (i.e `RedisClient`, `RedisCluster`, etc.) |
+| [`@redis/bloom`](https://github.com/redis/node-redis/tree/master/packages/bloom) | [Redis Bloom](https://redis.io/docs/data-types/probabilistic/) commands |
+| [`@redis/json`](https://github.com/redis/node-redis/tree/master/packages/json) | [Redis JSON](https://redis.io/docs/data-types/json/) commands |
+| [`@redis/search`](https://github.com/redis/node-redis/tree/master/packages/search) | [RediSearch](https://redis.io/docs/interact/search-and-query/) commands |
+| [`@redis/time-series`](https://github.com/redis/node-redis/tree/master/packages/time-series) | [Redis Time-Series](https://redis.io/docs/data-types/timeseries/) commands |
+| [`@redis/entraid`](https://github.com/redis/node-redis/tree/master/packages/entraid) | Secure token-based authentication for Redis clients using Microsoft Entra ID |
> Looking for a high-level library to handle object mapping?
> See [redis-om-node](https://github.com/redis/redis-om-node)!
@@ -83,7 +83,7 @@ createClient({
```
You can also use discrete parameters, UNIX sockets, and even TLS to connect. Details can be found in
-the [client configuration guide](../../docs/client-configuration.md).
+the [client configuration guide](https://github.com/redis/node-redis/blob/master/docs/client-configuration.md).
To check if the the client is connected and ready to send commands, use `client.isReady` which returns a boolean.
`client.isOpen` is also available. This returns `true` when the client's underlying socket is open, and `false` when it
@@ -188,7 +188,7 @@ await pool.ping();
### Pub/Sub
-See the [Pub/Sub overview](../../docs/pub-sub.md).
+See the [Pub/Sub overview](https://github.com/redis/node-redis/blob/master/docs/pub-sub.md).
### Scan Iterator
@@ -250,7 +250,7 @@ const client = createClient({
});
```
-See the [V5 documentation](../../docs/v5.md#client-side-caching) for more details and advanced usage.
+See the [V5 documentation](https://github.com/redis/node-redis/blob/master/docs/v5.md#client-side-caching) for more details and advanced usage.
### Auto-Pipelining
@@ -274,11 +274,11 @@ await Promise.all([
### Programmability
-See the [Programmability overview](../../docs/programmability.md).
+See the [Programmability overview](https://github.com/redis/node-redis/blob/master/docs/programmability.md).
### Clustering
-Check out the [Clustering Guide](../../docs/clustering.md) when using Node Redis to connect to a Redis Cluster.
+Check out the [Clustering Guide](https://github.com/redis/node-redis/blob/master/docs/clustering.md) when using Node Redis to connect to a Redis Cluster.
### Events
@@ -291,12 +291,12 @@ The Node Redis client class is an Nodejs EventEmitter and it emits an event each
| `end` | Connection has been closed (via `.disconnect()`) | _No arguments_ |
| `error` | An error has occurred—usually a network issue such as "Socket closed unexpectedly" | `(error: Error)` |
| `reconnecting` | Client is trying to reconnect to the server | _No arguments_ |
-| `sharded-channel-moved` | See [here](../../docs/pub-sub.md#sharded-channel-moved-event) | See [here](../../docs/pub-sub.md#sharded-channel-moved-event) |
+| `sharded-channel-moved` | See [here](https://github.com/redis/node-redis/blob/master/docs/pub-sub.md#sharded-channel-moved-event) | See [here](https://github.com/redis/node-redis/blob/master/docs/pub-sub.md#sharded-channel-moved-event) |
> :warning: You **MUST** listen to `error` events. If a client doesn't have at least one `error` listener registered and
> an `error` occurs, that error will be thrown and the Node.js process will exit. See the [ > `EventEmitter` docs](https://nodejs.org/api/events.html#events_error_events) for more details.
-> The client will not emit [any other events](../../docs/v3-to-v4.md#all-the-removed-events) beyond those listed above.
+> The client will not emit [any other events](https://github.com/redis/node-redis/blob/master/docs/v3-to-v4.md#all-the-removed-events) beyond those listed above.
## Supported Redis versions
@@ -313,13 +313,13 @@ Node Redis is supported with the following versions of Redis:
## Migration
-- [From V3 to V4](../../docs/v3-to-v4.md)
-- [From V4 to V5](../../docs/v4-to-v5.md)
-- [V5](../../docs/v5.md)
+- [From V3 to V4](https://github.com/redis/node-redis/blob/master/docs/v3-to-v4.md)
+- [From V4 to V5](https://github.com/redis/node-redis/blob/master/docs/v4-to-v5.md)
+- [V5](https://github.com/redis/node-redis/blob/master/docs/v5.md)
## Contributing
-If you'd like to contribute, check out the [contributing guide](../../CONTRIBUTING.md).
+If you'd like to contribute, check out the [contributing guide](https://github.com/redis/node-redis/blob/master/CONTRIBUTING.md).
Thank you to all the people who already contributed to Node Redis!
@@ -327,4 +327,4 @@ Thank you to all the people who already contributed to Node Redis!
## License
-This repository is licensed under the "MIT" license. See [LICENSE](../../LICENSE).
+This repository is licensed under the "MIT" license. See [LICENSE](https://github.com/redis/node-redis/blob/master/LICENSE).
diff --git a/packages/redis/package.json b/packages/redis/package.json
index b6c3f409a6..05fb70cdd1 100644
--- a/packages/redis/package.json
+++ b/packages/redis/package.json
@@ -1,7 +1,7 @@
{
"name": "redis",
"description": "A modern, high performance Redis client",
- "version": "5.1.0",
+ "version": "5.1.1",
"license": "MIT",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
@@ -10,11 +10,11 @@
"!dist/tsconfig.tsbuildinfo"
],
"dependencies": {
- "@redis/bloom": "5.1.0",
- "@redis/client": "5.1.0",
- "@redis/json": "5.1.0",
- "@redis/search": "5.1.0",
- "@redis/time-series": "5.1.0"
+ "@redis/bloom": "5.1.1",
+ "@redis/client": "5.1.1",
+ "@redis/json": "5.1.1",
+ "@redis/search": "5.1.1",
+ "@redis/time-series": "5.1.1"
},
"engines": {
"node": ">= 18"
diff --git a/packages/search/package.json b/packages/search/package.json
index ac56b5ff5d..7cb73dfc0a 100644
--- a/packages/search/package.json
+++ b/packages/search/package.json
@@ -1,6 +1,6 @@
{
"name": "@redis/search",
- "version": "5.1.0",
+ "version": "5.1.1",
"license": "MIT",
"main": "./dist/lib/index.js",
"types": "./dist/lib/index.d.ts",
@@ -13,7 +13,7 @@
"test-sourcemap": "mocha -r ts-node/register/transpile-only './lib/**/*.spec.ts'"
},
"peerDependencies": {
- "@redis/client": "^5.1.0"
+ "@redis/client": "^5.1.1"
},
"devDependencies": {
"@redis/test-utils": "*"
diff --git a/packages/test-utils/lib/dockers.ts b/packages/test-utils/lib/dockers.ts
index 3814a80923..47257964f6 100644
--- a/packages/test-utils/lib/dockers.ts
+++ b/packages/test-utils/lib/dockers.ts
@@ -62,7 +62,7 @@ export interface RedisServerDocker {
dockerId: string;
}
-async function spawnRedisServerDocker(
+export async function spawnRedisServerDocker(
options: RedisServerDockerOptions, serverArguments: Array): Promise {
let port;
if (options.mode == "sentinel") {
@@ -374,35 +374,16 @@ export async function spawnRedisSentinel(
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), appPrefix));
for (let i = 0; i < sentinelCount; i++) {
- sentinelPromises.push((async () => {
- const port = (await portIterator.next()).value;
-
- let sentinelConfig = `port ${port}
-sentinel monitor mymaster 127.0.0.1 ${master.port} 2
-sentinel down-after-milliseconds mymaster 5000
-sentinel failover-timeout mymaster 6000
-`;
- if (password !== undefined) {
- sentinelConfig += `requirepass ${password}\n`;
- sentinelConfig += `sentinel auth-pass mymaster ${password}\n`;
- }
-
- const dir = fs.mkdtempSync(path.join(tmpDir, i.toString()));
- fs.writeFile(`${dir}/redis.conf`, sentinelConfig, err => {
- if (err) {
- console.error("failed to create temporary config file", err);
- }
- });
-
- return await spawnRedisServerDocker(
- {
- image: dockerConfigs.image,
- version: dockerConfigs.version,
- mode: "sentinel",
- mounts: [`${dir}/redis.conf:/redis/config/node-sentinel-1/redis.conf`],
- port: port,
- }, serverArguments);
- })());
+ sentinelPromises.push(
+ spawnSentinelNode(
+ dockerConfigs,
+ serverArguments,
+ master.port,
+ "mymaster",
+ path.join(tmpDir, i.toString()),
+ password,
+ ),
+ )
}
const sentinelNodes = await Promise.all(sentinelPromises);
@@ -424,3 +405,43 @@ after(() => {
})
);
});
+
+
+export async function spawnSentinelNode(
+ dockerConfigs: RedisServerDockerOptions,
+ serverArguments: Array,
+ masterPort: number,
+ sentinelName: string,
+ tmpDir: string,
+ password?: string,
+) {
+ const port = (await portIterator.next()).value;
+
+ let sentinelConfig = `port ${port}
+sentinel monitor ${sentinelName} 127.0.0.1 ${masterPort} 2
+sentinel down-after-milliseconds ${sentinelName} 500
+sentinel failover-timeout ${sentinelName} 1000
+`;
+ if (password !== undefined) {
+ sentinelConfig += `requirepass ${password}\n`;
+ sentinelConfig += `sentinel auth-pass ${sentinelName} ${password}\n`;
+ }
+
+ const dir = fs.mkdtempSync(tmpDir);
+ fs.writeFile(`${dir}/redis.conf`, sentinelConfig, err => {
+ if (err) {
+ console.error("failed to create temporary config file", err);
+ }
+ });
+
+ return await spawnRedisServerDocker(
+ {
+ image: dockerConfigs.image,
+ version: dockerConfigs.version,
+ mode: "sentinel",
+ mounts: [`${dir}/redis.conf:/redis/config/node-sentinel-1/redis.conf`],
+ port: port,
+ },
+ serverArguments,
+ );
+}
\ No newline at end of file
diff --git a/packages/test-utils/lib/index.ts b/packages/test-utils/lib/index.ts
index d92c5c9e3d..a41f970e0c 100644
--- a/packages/test-utils/lib/index.ts
+++ b/packages/test-utils/lib/index.ts
@@ -19,10 +19,13 @@ import {
RedisClusterType
} from '@redis/client/index';
import { RedisNode } from '@redis/client/lib/sentinel/types'
-import { spawnRedisServer, spawnRedisCluster, spawnRedisSentinel, RedisServerDockerOptions } from './dockers';
+import { spawnRedisServer, spawnRedisCluster, spawnRedisSentinel, RedisServerDockerOptions, RedisServerDocker, spawnSentinelNode, spawnRedisServerDocker } from './dockers';
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
+import * as fs from 'node:fs';
+import * as os from 'node:os';
+import * as path from 'node:path';
interface TestUtilsConfig {
/**
@@ -395,19 +398,19 @@ export default class TestUtils {
S extends RedisScripts = {},
RESP extends RespVersions = 2,
TYPE_MAPPING extends TypeMapping = {}
->(
- range: ([minVersion: Array, maxVersion: Array] | [minVersion: Array, 'LATEST']),
- title: string,
- fn: (sentinel: RedisSentinelType) => unknown,
- options: SentinelTestOptions
-): void {
-
- if (this.isVersionInRange(range[0], range[1] === 'LATEST' ? [Infinity, Infinity, Infinity] : range[1])) {
- return this.testWithClientSentinel(`${title} [${range[0].join('.')}] - [${(range[1] === 'LATEST') ? range[1] : range[1].join(".")}] `, fn, options)
- } else {
- console.warn(`Skipping test ${title} because server version ${this.#VERSION_NUMBERS.join('.')} is not within range ${range[0].join(".")} - ${range[1] !== 'LATEST' ? range[1].join(".") : 'LATEST'}`)
+ >(
+ range: ([minVersion: Array, maxVersion: Array] | [minVersion: Array, 'LATEST']),
+ title: string,
+ fn: (sentinel: RedisSentinelType) => unknown,
+ options: SentinelTestOptions
+ ): void {
+
+ if (this.isVersionInRange(range[0], range[1] === 'LATEST' ? [Infinity, Infinity, Infinity] : range[1])) {
+ return this.testWithClientSentinel(`${title} [${range[0].join('.')}] - [${(range[1] === 'LATEST') ? range[1] : range[1].join(".")}] `, fn, options)
+ } else {
+ console.warn(`Skipping test ${title} because server version ${this.#VERSION_NUMBERS.join('.')} is not within range ${range[0].join(".")} - ${range[1] !== 'LATEST' ? range[1].join(".") : 'LATEST'}`)
+ }
}
-}
testWithClientPool<
M extends RedisModules = {},
@@ -541,4 +544,46 @@ export default class TestUtils {
this.testWithClient(`client.${title}`, fn, options.client);
this.testWithCluster(`cluster.${title}`, fn, options.cluster);
}
+
+
+ spawnRedisServer<
+ M extends RedisModules = {},
+ F extends RedisFunctions = {},
+ S extends RedisScripts = {},
+ RESP extends RespVersions = 2,
+ TYPE_MAPPING extends TypeMapping = {}
+ // POLICIES extends CommandPolicies = {}
+ >(
+ options: ClientPoolTestOptions
+ ): Promise {
+ return spawnRedisServerDocker(this.#DOCKER_IMAGE, options.serverArguments)
+ }
+
+ async spawnRedisSentinels<
+ M extends RedisModules = {},
+ F extends RedisFunctions = {},
+ S extends RedisScripts = {},
+ RESP extends RespVersions = 2,
+ TYPE_MAPPING extends TypeMapping = {}
+ // POLICIES extends CommandPolicies = {}
+ >(
+ options: ClientPoolTestOptions,
+ masterPort: number,
+ sentinelName: string,
+ count: number
+ ): Promise> {
+ const sentinels: Array = [];
+ for (let i = 0; i < count; i++) {
+ const appPrefix = 'sentinel-config-dir';
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), appPrefix));
+
+ sentinels.push(await spawnSentinelNode(this.#DOCKER_IMAGE, options.serverArguments, masterPort, sentinelName, tmpDir))
+
+ if (tmpDir) {
+ fs.rmSync(tmpDir, { recursive: true });
+ }
+ }
+
+ return sentinels
+ }
}
diff --git a/packages/time-series/package.json b/packages/time-series/package.json
index 466c53df22..888f0f317e 100644
--- a/packages/time-series/package.json
+++ b/packages/time-series/package.json
@@ -1,6 +1,6 @@
{
"name": "@redis/time-series",
- "version": "5.1.0",
+ "version": "5.1.1",
"license": "MIT",
"main": "./dist/lib/index.js",
"types": "./dist/lib/index.d.ts",
@@ -12,7 +12,7 @@
"test": "nyc -r text-summary -r lcov mocha -r tsx './lib/**/*.spec.ts'"
},
"peerDependencies": {
- "@redis/client": "^5.1.0"
+ "@redis/client": "^5.1.1"
},
"devDependencies": {
"@redis/test-utils": "*"