You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-06 02:15:48 +03:00
fix #1671 - add support for all client configurations in cluster
This commit is contained in:
34
README.md
34
README.md
@@ -53,7 +53,7 @@ The above code connects to localhost on port 6379. To connect to a different hos
|
||||
|
||||
```typescript
|
||||
createClient({
|
||||
url: 'redis://alice:foobared@awesome.redis.server:6380',
|
||||
url: 'redis://alice:foobared@awesome.redis.server:6380'
|
||||
});
|
||||
```
|
||||
|
||||
@@ -78,7 +78,7 @@ Modifiers to commands are specified using a JavaScript object:
|
||||
```typescript
|
||||
await client.set('key', 'value', {
|
||||
EX: 10,
|
||||
NX: true,
|
||||
NX: true
|
||||
});
|
||||
```
|
||||
|
||||
@@ -181,12 +181,9 @@ for await (const key of client.scanIterator()) {
|
||||
This works with `HSCAN`, `SSCAN`, and `ZSCAN` too:
|
||||
|
||||
```typescript
|
||||
for await (const member of client.hScanIterator('hash')) {
|
||||
}
|
||||
for await (const { field, value } of client.sScanIterator('set')) {
|
||||
}
|
||||
for await (const { member, score } of client.zScanIterator('sorted-set')) {
|
||||
}
|
||||
for await (const member of client.hScanIterator('hash')) {}
|
||||
for await (const { field, value } of client.sScanIterator('set')) {}
|
||||
for await (const { member, score } of client.zScanIterator('sorted-set')) {}
|
||||
```
|
||||
|
||||
You can override the default options by providing a configuration object:
|
||||
@@ -204,7 +201,8 @@ client.scanIterator({
|
||||
Define new functions using [Lua scripts](https://redis.io/commands/eval) which execute on the Redis server:
|
||||
|
||||
```typescript
|
||||
import { createClient, defineScript } from 'redis';
|
||||
import { createClient } from 'redis';
|
||||
import { defineScript } from 'redis/lua-script';
|
||||
|
||||
(async () => {
|
||||
const client = createClient({
|
||||
@@ -218,9 +216,9 @@ import { createClient, defineScript } from 'redis';
|
||||
},
|
||||
transformReply(reply: number): number {
|
||||
return reply;
|
||||
},
|
||||
}),
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
await client.connect();
|
||||
@@ -241,14 +239,12 @@ import { createCluster } from 'redis';
|
||||
const cluster = createCluster({
|
||||
rootNodes: [
|
||||
{
|
||||
host: '10.0.0.1',
|
||||
port: 30001,
|
||||
url: 'redis://10.0.0.1:30001'
|
||||
},
|
||||
{
|
||||
host: '10.0.0.2',
|
||||
port: 30002,
|
||||
},
|
||||
],
|
||||
url: 'redis://10.0.0.2:30002'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
cluster.on('error', (err) => console.log('Redis Cluster Error', err));
|
||||
@@ -274,7 +270,7 @@ Of course, if you don't do something with your Promises you're certain to get [u
|
||||
```typescript
|
||||
await Promise.all([
|
||||
client.set('Tm9kZSBSZWRpcw==', 'users:1'),
|
||||
client.sAdd('users:1:tokens', 'Tm9kZSBSZWRpcw=='),
|
||||
client.sAdd('users:1:tokens', 'Tm9kZSBSZWRpcw==')
|
||||
]);
|
||||
```
|
||||
|
||||
|
2
index.ts
2
index.ts
@@ -6,5 +6,3 @@ export const createClient = RedisClient.create;
|
||||
export const commandOptions = RedisClient.commandOptions;
|
||||
|
||||
export const createCluster = RedisCluster.create;
|
||||
|
||||
export { defineScript } from './lib/lua-script';
|
||||
|
@@ -2,7 +2,7 @@ import calculateSlot from 'cluster-key-slot';
|
||||
import RedisClient, { RedisClientType } from './client';
|
||||
import { RedisSocketOptions } from './socket';
|
||||
import { RedisClusterMasterNode, RedisClusterReplicaNode } from './commands/CLUSTER_NODES';
|
||||
import { RedisClusterOptions } from './cluster';
|
||||
import { RedisClusterClientOptions, RedisClusterOptions } from './cluster';
|
||||
import { RedisModules } from './commands';
|
||||
import { RedisLuaScripts } from './lua-script';
|
||||
|
||||
@@ -39,21 +39,19 @@ export default class RedisClusterSlots<M extends RedisModules, S extends RedisLu
|
||||
}
|
||||
|
||||
async discover(startWith: RedisClientType<M, S>): Promise<void> {
|
||||
if (await this.#discoverNodes(startWith.options?.socket)) return;
|
||||
if (await this.#discoverNodes(startWith.options)) return;
|
||||
|
||||
for (const { client } of this.#nodeByUrl.values()) {
|
||||
if (client === startWith) continue;
|
||||
|
||||
if (await this.#discoverNodes(client.options?.socket)) return;
|
||||
if (await this.#discoverNodes(client.options)) return;
|
||||
}
|
||||
|
||||
throw new Error('None of the cluster nodes is available');
|
||||
}
|
||||
|
||||
async #discoverNodes(socketOptions?: RedisSocketOptions): Promise<boolean> {
|
||||
const client = RedisClient.create({
|
||||
socket: socketOptions
|
||||
});
|
||||
async #discoverNodes(clientOptions?: RedisClusterClientOptions): Promise<boolean> {
|
||||
const client = RedisClient.create(clientOptions);
|
||||
|
||||
await client.connect();
|
||||
|
||||
|
@@ -8,7 +8,7 @@ import { ClusterSlotStates } from './commands/CLUSTER_SETSLOT';
|
||||
describe('Cluster', () => {
|
||||
it('sendCommand', async () => {
|
||||
const cluster = RedisCluster.create({
|
||||
rootNodes: TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN],
|
||||
...TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN],
|
||||
useReplicas: true
|
||||
});
|
||||
|
||||
@@ -42,7 +42,7 @@ describe('Cluster', () => {
|
||||
|
||||
it('scripts', async () => {
|
||||
const cluster = RedisCluster.create({
|
||||
rootNodes: TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN],
|
||||
...TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN],
|
||||
scripts: {
|
||||
add: defineScript({
|
||||
NUMBER_OF_KEYS: 0,
|
||||
|
@@ -1,14 +1,16 @@
|
||||
import { RedisCommand, RedisCommandReply, RedisModules, TransformArgumentsReply } from './commands';
|
||||
import RedisClient, { ClientCommandOptions, RedisClientType, WithPlugins } from './client';
|
||||
import { RedisSocketOptions } from './socket';
|
||||
import RedisClient, { ClientCommandOptions, RedisClientOptions, RedisClientType, WithPlugins } from './client';
|
||||
import RedisClusterSlots, { ClusterNode } from './cluster-slots';
|
||||
import { RedisLuaScript, RedisLuaScripts } from './lua-script';
|
||||
import { extendWithModulesAndScripts, extendWithDefaultCommands, transformCommandArguments, transformCommandReply } from './commander';
|
||||
import RedisMultiCommand, { MultiQueuedCommand, RedisMultiCommandType } from './multi-command';
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
export interface RedisClusterOptions<M = RedisModules, S = RedisLuaScripts> {
|
||||
rootNodes: Array<RedisSocketOptions>;
|
||||
export type RedisClusterClientOptions = Omit<RedisClientOptions<{}, {}>, 'modules' | 'scripts'>;
|
||||
|
||||
export interface RedisClusterOptions<M = {}, S = {}> {
|
||||
rootNodes: Array<RedisClusterClientOptions>;
|
||||
defaults?: RedisClusterClientOptions;
|
||||
modules?: M;
|
||||
scripts?: S;
|
||||
useReplicas?: boolean;
|
||||
|
@@ -2,7 +2,6 @@ import { strict as assert } from 'assert';
|
||||
import { TestRedisServers, itWithClient } from '../test-utils';
|
||||
import { transformArguments, transformReply } from './BZPOPMAX';
|
||||
import { commandOptions } from '../../index';
|
||||
import { describe } from 'mocha';
|
||||
|
||||
describe('BZPOPMAX', () => {
|
||||
describe('transformArguments', () => {
|
||||
|
@@ -5,7 +5,7 @@ import { once } from 'events';
|
||||
import { RedisSocketOptions } from './socket';
|
||||
import which from 'which';
|
||||
import { SinonSpy } from 'sinon';
|
||||
import RedisCluster, { RedisClusterType } from './cluster';
|
||||
import RedisCluster, { RedisClusterOptions, RedisClusterType } from './cluster';
|
||||
import { promises as fs } from 'fs';
|
||||
import { Context as MochaContext } from 'mocha';
|
||||
import { promiseTimeout } from './utils';
|
||||
@@ -60,7 +60,7 @@ export enum TestRedisClusters {
|
||||
OPEN
|
||||
}
|
||||
|
||||
export const TEST_REDIS_CLUSTERES: Record<TestRedisClusters, Array<RedisSocketOptions>> = <any>{};
|
||||
export const TEST_REDIS_CLUSTERES: Record<TestRedisClusters, RedisClusterOptions<RedisModules, RedisLuaScripts>> = <any>{};
|
||||
|
||||
let port = 6379;
|
||||
|
||||
@@ -248,9 +248,13 @@ async function spawnPasswordServer(): Promise<void> {
|
||||
}
|
||||
|
||||
async function spawnOpenCluster(): Promise<void> {
|
||||
TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN] = (await spawnGlobalRedisCluster(TestRedisClusters.OPEN, 3)).map(port => ({
|
||||
port
|
||||
}));
|
||||
TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN] = {
|
||||
rootNodes: (await spawnGlobalRedisCluster(TestRedisClusters.OPEN, 3)).map(port => ({
|
||||
socket: {
|
||||
port
|
||||
}
|
||||
}))
|
||||
};
|
||||
}
|
||||
|
||||
before(function () {
|
||||
@@ -314,9 +318,7 @@ export function itWithCluster(
|
||||
it(title, async function () {
|
||||
if (handleMinimumRedisVersion(this, options?.minimumRedisVersion)) return;
|
||||
|
||||
const cluster = RedisCluster.create({
|
||||
rootNodes: TEST_REDIS_CLUSTERES[type]
|
||||
});
|
||||
const cluster = RedisCluster.create(TEST_REDIS_CLUSTERES[type]);
|
||||
|
||||
await cluster.connect();
|
||||
|
||||
@@ -337,7 +339,9 @@ export function itWithDedicatedCluster(title: string, fn: (cluster: RedisCluster
|
||||
const spawnResults = await spawnRedisCluster(null, 3),
|
||||
cluster = RedisCluster.create({
|
||||
rootNodes: [{
|
||||
port: spawnResults[0].port
|
||||
socket: {
|
||||
port: spawnResults[0].port
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
|
Reference in New Issue
Block a user