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
|
```typescript
|
||||||
createClient({
|
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
|
```typescript
|
||||||
await client.set('key', 'value', {
|
await client.set('key', 'value', {
|
||||||
EX: 10,
|
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:
|
This works with `HSCAN`, `SSCAN`, and `ZSCAN` too:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
for await (const member of client.hScanIterator('hash')) {
|
for await (const member of client.hScanIterator('hash')) {}
|
||||||
}
|
for await (const { field, value } of client.sScanIterator('set')) {}
|
||||||
for await (const { field, value } of client.sScanIterator('set')) {
|
for await (const { member, score } of client.zScanIterator('sorted-set')) {}
|
||||||
}
|
|
||||||
for await (const { member, score } of client.zScanIterator('sorted-set')) {
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
You can override the default options by providing a configuration object:
|
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:
|
Define new functions using [Lua scripts](https://redis.io/commands/eval) which execute on the Redis server:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { createClient, defineScript } from 'redis';
|
import { createClient } from 'redis';
|
||||||
|
import { defineScript } from 'redis/lua-script';
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const client = createClient({
|
const client = createClient({
|
||||||
@@ -218,9 +216,9 @@ import { createClient, defineScript } from 'redis';
|
|||||||
},
|
},
|
||||||
transformReply(reply: number): number {
|
transformReply(reply: number): number {
|
||||||
return reply;
|
return reply;
|
||||||
},
|
}
|
||||||
}),
|
})
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await client.connect();
|
await client.connect();
|
||||||
@@ -241,14 +239,12 @@ import { createCluster } from 'redis';
|
|||||||
const cluster = createCluster({
|
const cluster = createCluster({
|
||||||
rootNodes: [
|
rootNodes: [
|
||||||
{
|
{
|
||||||
host: '10.0.0.1',
|
url: 'redis://10.0.0.1:30001'
|
||||||
port: 30001,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
host: '10.0.0.2',
|
url: 'redis://10.0.0.2:30002'
|
||||||
port: 30002,
|
}
|
||||||
},
|
]
|
||||||
],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
cluster.on('error', (err) => console.log('Redis Cluster Error', err));
|
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
|
```typescript
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
client.set('Tm9kZSBSZWRpcw==', 'users:1'),
|
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 commandOptions = RedisClient.commandOptions;
|
||||||
|
|
||||||
export const createCluster = RedisCluster.create;
|
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 RedisClient, { RedisClientType } from './client';
|
||||||
import { RedisSocketOptions } from './socket';
|
import { RedisSocketOptions } from './socket';
|
||||||
import { RedisClusterMasterNode, RedisClusterReplicaNode } from './commands/CLUSTER_NODES';
|
import { RedisClusterMasterNode, RedisClusterReplicaNode } from './commands/CLUSTER_NODES';
|
||||||
import { RedisClusterOptions } from './cluster';
|
import { RedisClusterClientOptions, RedisClusterOptions } from './cluster';
|
||||||
import { RedisModules } from './commands';
|
import { RedisModules } from './commands';
|
||||||
import { RedisLuaScripts } from './lua-script';
|
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> {
|
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()) {
|
for (const { client } of this.#nodeByUrl.values()) {
|
||||||
if (client === startWith) continue;
|
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');
|
throw new Error('None of the cluster nodes is available');
|
||||||
}
|
}
|
||||||
|
|
||||||
async #discoverNodes(socketOptions?: RedisSocketOptions): Promise<boolean> {
|
async #discoverNodes(clientOptions?: RedisClusterClientOptions): Promise<boolean> {
|
||||||
const client = RedisClient.create({
|
const client = RedisClient.create(clientOptions);
|
||||||
socket: socketOptions
|
|
||||||
});
|
|
||||||
|
|
||||||
await client.connect();
|
await client.connect();
|
||||||
|
|
||||||
|
@@ -8,7 +8,7 @@ import { ClusterSlotStates } from './commands/CLUSTER_SETSLOT';
|
|||||||
describe('Cluster', () => {
|
describe('Cluster', () => {
|
||||||
it('sendCommand', async () => {
|
it('sendCommand', async () => {
|
||||||
const cluster = RedisCluster.create({
|
const cluster = RedisCluster.create({
|
||||||
rootNodes: TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN],
|
...TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN],
|
||||||
useReplicas: true
|
useReplicas: true
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ describe('Cluster', () => {
|
|||||||
|
|
||||||
it('scripts', async () => {
|
it('scripts', async () => {
|
||||||
const cluster = RedisCluster.create({
|
const cluster = RedisCluster.create({
|
||||||
rootNodes: TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN],
|
...TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN],
|
||||||
scripts: {
|
scripts: {
|
||||||
add: defineScript({
|
add: defineScript({
|
||||||
NUMBER_OF_KEYS: 0,
|
NUMBER_OF_KEYS: 0,
|
||||||
|
@@ -1,14 +1,16 @@
|
|||||||
import { RedisCommand, RedisCommandReply, RedisModules, TransformArgumentsReply } from './commands';
|
import { RedisCommand, RedisCommandReply, RedisModules, TransformArgumentsReply } from './commands';
|
||||||
import RedisClient, { ClientCommandOptions, RedisClientType, WithPlugins } from './client';
|
import RedisClient, { ClientCommandOptions, RedisClientOptions, RedisClientType, WithPlugins } from './client';
|
||||||
import { RedisSocketOptions } from './socket';
|
|
||||||
import RedisClusterSlots, { ClusterNode } from './cluster-slots';
|
import RedisClusterSlots, { ClusterNode } from './cluster-slots';
|
||||||
import { RedisLuaScript, RedisLuaScripts } from './lua-script';
|
import { RedisLuaScript, RedisLuaScripts } from './lua-script';
|
||||||
import { extendWithModulesAndScripts, extendWithDefaultCommands, transformCommandArguments, transformCommandReply } from './commander';
|
import { extendWithModulesAndScripts, extendWithDefaultCommands, transformCommandArguments, transformCommandReply } from './commander';
|
||||||
import RedisMultiCommand, { MultiQueuedCommand, RedisMultiCommandType } from './multi-command';
|
import RedisMultiCommand, { MultiQueuedCommand, RedisMultiCommandType } from './multi-command';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
|
|
||||||
export interface RedisClusterOptions<M = RedisModules, S = RedisLuaScripts> {
|
export type RedisClusterClientOptions = Omit<RedisClientOptions<{}, {}>, 'modules' | 'scripts'>;
|
||||||
rootNodes: Array<RedisSocketOptions>;
|
|
||||||
|
export interface RedisClusterOptions<M = {}, S = {}> {
|
||||||
|
rootNodes: Array<RedisClusterClientOptions>;
|
||||||
|
defaults?: RedisClusterClientOptions;
|
||||||
modules?: M;
|
modules?: M;
|
||||||
scripts?: S;
|
scripts?: S;
|
||||||
useReplicas?: boolean;
|
useReplicas?: boolean;
|
||||||
|
@@ -2,7 +2,6 @@ import { strict as assert } from 'assert';
|
|||||||
import { TestRedisServers, itWithClient } from '../test-utils';
|
import { TestRedisServers, itWithClient } from '../test-utils';
|
||||||
import { transformArguments, transformReply } from './BZPOPMAX';
|
import { transformArguments, transformReply } from './BZPOPMAX';
|
||||||
import { commandOptions } from '../../index';
|
import { commandOptions } from '../../index';
|
||||||
import { describe } from 'mocha';
|
|
||||||
|
|
||||||
describe('BZPOPMAX', () => {
|
describe('BZPOPMAX', () => {
|
||||||
describe('transformArguments', () => {
|
describe('transformArguments', () => {
|
||||||
|
@@ -5,7 +5,7 @@ import { once } from 'events';
|
|||||||
import { RedisSocketOptions } from './socket';
|
import { RedisSocketOptions } from './socket';
|
||||||
import which from 'which';
|
import which from 'which';
|
||||||
import { SinonSpy } from 'sinon';
|
import { SinonSpy } from 'sinon';
|
||||||
import RedisCluster, { RedisClusterType } from './cluster';
|
import RedisCluster, { RedisClusterOptions, RedisClusterType } from './cluster';
|
||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import { Context as MochaContext } from 'mocha';
|
import { Context as MochaContext } from 'mocha';
|
||||||
import { promiseTimeout } from './utils';
|
import { promiseTimeout } from './utils';
|
||||||
@@ -60,7 +60,7 @@ export enum TestRedisClusters {
|
|||||||
OPEN
|
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;
|
let port = 6379;
|
||||||
|
|
||||||
@@ -248,9 +248,13 @@ async function spawnPasswordServer(): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function spawnOpenCluster(): Promise<void> {
|
async function spawnOpenCluster(): Promise<void> {
|
||||||
TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN] = (await spawnGlobalRedisCluster(TestRedisClusters.OPEN, 3)).map(port => ({
|
TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN] = {
|
||||||
port
|
rootNodes: (await spawnGlobalRedisCluster(TestRedisClusters.OPEN, 3)).map(port => ({
|
||||||
}));
|
socket: {
|
||||||
|
port
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
before(function () {
|
before(function () {
|
||||||
@@ -314,9 +318,7 @@ export function itWithCluster(
|
|||||||
it(title, async function () {
|
it(title, async function () {
|
||||||
if (handleMinimumRedisVersion(this, options?.minimumRedisVersion)) return;
|
if (handleMinimumRedisVersion(this, options?.minimumRedisVersion)) return;
|
||||||
|
|
||||||
const cluster = RedisCluster.create({
|
const cluster = RedisCluster.create(TEST_REDIS_CLUSTERES[type]);
|
||||||
rootNodes: TEST_REDIS_CLUSTERES[type]
|
|
||||||
});
|
|
||||||
|
|
||||||
await cluster.connect();
|
await cluster.connect();
|
||||||
|
|
||||||
@@ -337,7 +339,9 @@ export function itWithDedicatedCluster(title: string, fn: (cluster: RedisCluster
|
|||||||
const spawnResults = await spawnRedisCluster(null, 3),
|
const spawnResults = await spawnRedisCluster(null, 3),
|
||||||
cluster = RedisCluster.create({
|
cluster = RedisCluster.create({
|
||||||
rootNodes: [{
|
rootNodes: [{
|
||||||
port: spawnResults[0].port
|
socket: {
|
||||||
|
port: spawnResults[0].port
|
||||||
|
}
|
||||||
}]
|
}]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user