1
0
mirror of https://github.com/redis/node-redis.git synced 2025-08-17 19:41:06 +03:00

implement a couple of more commands, make cluster random iterator be per node (instead of per slot)

This commit is contained in:
leibale
2021-06-24 18:25:19 -04:00
parent bfacde3ae4
commit b6cd2b36e7
15 changed files with 282 additions and 14 deletions

View File

@@ -239,7 +239,7 @@ export default class RedisClient<M extends RedisModules = RedisModules, S extend
} }
callback(err); callback(err);
}) });
} }
for (const name of Object.keys(COMMANDS)) { for (const name of Object.keys(COMMANDS)) {

View File

@@ -75,18 +75,17 @@ export default class RedisClusterSlots<M extends RedisModules, S extends RedisLu
const promises: Array<Promise<void>> = [], const promises: Array<Promise<void>> = [],
clientsInUse = new Set<string>(); clientsInUse = new Set<string>();
for (const master of masters) { for (const master of masters) {
const masterClient = this.#initiateClientForNode(master, false, clientsInUse, promises), const slot = {
replicasClients = this.#options.useReplicas ? master: this.#initiateClientForNode(master, false, clientsInUse, promises),
replicas: this.#options.useReplicas ?
master.replicas.map(replica => this.#initiateClientForNode(replica, true, clientsInUse, promises)) : master.replicas.map(replica => this.#initiateClientForNode(replica, true, clientsInUse, promises)) :
[]; [],
iterator: undefined // will be initiated in use
};
for (const slot of master.slots) { for (const { from, to } of master.slots) {
for (let i = slot.from; i < slot.to; i++) { for (let i = from; i < to; i++) {
this.#slots[i] = { this.#slots[i] = slot;
master: masterClient,
replicas: replicasClients,
iterator: undefined // will be initiated in use
};
} }
} }
} }

View File

@@ -0,0 +1,42 @@
import { strict as assert } from 'assert';
import { transformArguments, transformReply } from './CLIENT_INFO';
describe('CLIENT INFO', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments(),
['CLIENT', 'INFO']
);
});
it('transformReply', () => {
assert.deepEqual(
transformReply('id=526512 addr=127.0.0.1:36244 laddr=127.0.0.1:6379 fd=8 name= age=11213 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=40928 argv-mem=10 obl=0 oll=0 omem=0 tot-mem=61466 events=r cmd=client user=default redir=-1\n'),
{
id: 526512,
addr: '127.0.0.1:36244',
laddr: '127.0.0.1:6379',
fd: 8,
name: '',
age: 11213,
idle: 0,
flags: 'N',
db: 0,
sub: 0,
psub: 0,
multi: -1,
qbuf: 26,
qbufFree: 40928,
argvMem: 10,
obl: 0,
oll: 0,
omem: 0,
totMem: 61466,
events: 'r',
cmd: 'client',
user: 'default',
redir: -1
}
);
});
});

View File

@@ -0,0 +1,36 @@
import { strict as assert } from 'assert';
import { TestRedisServers, itWithClient } from '../test-utils';
import { RedisFlushModes } from './FLUSHALL';
import { transformArguments } from './FLUSHDB';
describe('FLUSHDB', () => {
describe('transformArguments', () => {
it('default', () => {
assert.deepEqual(
transformArguments(),
['FLUSHDB']
);
});
it('ASYNC', () => {
assert.deepEqual(
transformArguments(RedisFlushModes.ASYNC),
['FLUSHDB', 'ASYNC']
);
});
it('SYNC', () => {
assert.deepEqual(
transformArguments(RedisFlushModes.SYNC),
['FLUSHDB', 'SYNC']
);
});
});
itWithClient(TestRedisServers.OPEN, 'client.flushDb', async client => {
assert.equal(
await client.flushDb(),
'OK'
);
});
});

14
lib/commands/FLUSHDB.ts Normal file
View File

@@ -0,0 +1,14 @@
import { RedisFlushModes } from './FLUSHALL';
import { transformReplyString } from './generic-transformers';
export function transformArguments(mode?: RedisFlushModes): Array<string> {
const args = ['FLUSHDB'];
if (mode) {
args.push(mode);
}
return args;
}
export const transformReply = transformReplyString;

View File

@@ -0,0 +1,20 @@
import { strict as assert } from 'assert';
import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils';
import { transformArguments } from './LASTSAVE';
describe('LASTSAVE', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments(),
['LASTSAVE']
);
});
itWithClient(TestRedisServers.OPEN, 'client.lastSave', async client => {
assert.ok((await client.lastSave()) instanceof Date);
});
itWithCluster(TestRedisClusters.OPEN, 'cluster.lastSave', async cluster => {
assert.ok((await cluster.lastSave()) instanceof Date);
});
});

9
lib/commands/LASTSAVE.ts Normal file
View File

@@ -0,0 +1,9 @@
export const IS_READ_ONLY = true;
export function transformArguments(): Array<string> {
return ['LASTSAVE'];
}
export function transformReply(reply: number): Date {
return new Date(reply);
}

View File

@@ -0,0 +1,43 @@
import { strict as assert } from 'assert';
import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils';
import { transformArguments } from './LOLWUT';
describe('LOLWUT', () => {
describe('transformArguments', () => {
it('simple', () => {
assert.deepEqual(
transformArguments(),
['LOLWUT']
);
});
it('with version', () => {
assert.deepEqual(
transformArguments(5),
['LOLWUT', 'VERSION', '5']
);
});
it('with version and optional arguments', () => {
assert.deepEqual(
transformArguments(5, 1, 2, 3),
['LOLWUT', 'VERSION', '5', '1', '2', '3']
);
});
});
itWithClient(TestRedisServers.OPEN, 'client.LOLWUT', async client => {
assert.equal(
typeof (await client.LOLWUT()),
'string'
);
});
itWithCluster(TestRedisClusters.OPEN, 'cluster.LOLWUT', async cluster => {
assert.equal(
typeof (await cluster.LOLWUT()),
'string'
);
});
});

19
lib/commands/LOLWUT.ts Normal file
View File

@@ -0,0 +1,19 @@
import { transformReplyString } from './generic-transformers';
export const IS_READ_ONLY = true;
export function transformArguments(version?: number, ...optionalArguments: Array<number>): Array<string> {
const args = ['LOLWUT'];
if (version) {
args.push(
'VERSION',
version.toString(),
...optionalArguments.map(String),
);
}
return args;
}
export const transformReply = transformReplyString;

View File

@@ -0,0 +1,26 @@
import { strict as assert } from 'assert';
import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils';
import { transformArguments } from './SETRANGE';
describe('SETRANGE', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments('key', 0, 'value'),
['SETRANGE', 'key', '0', 'value']
);
});
itWithClient(TestRedisServers.OPEN, 'client.setRange', async client => {
assert.equal(
await client.setRange('key', 0, 'value'),
5
);
});
itWithCluster(TestRedisClusters.OPEN, 'cluster.setRange', async cluster => {
assert.equal(
await cluster.setRange('key', 0, 'value'),
5
);
});
});

9
lib/commands/SETRANGE.ts Normal file
View File

@@ -0,0 +1,9 @@
import { transformReplyNumber } from './generic-transformers';
export const FIRST_KEY_INDEX = 1;
export function transformArguments(key: string, offset: number, value: string): Array<string> {
return ['SETRANGE', key, offset.toString(), value];
}
export const transformReply = transformReplyNumber;

View File

@@ -0,0 +1,26 @@
import { strict as assert } from 'assert';
import { TestRedisServers, itWithClient, itWithCluster, TestRedisClusters } from '../test-utils';
import { transformArguments } from './STRLEN';
describe('STRLEN', () => {
it('transformArguments', () => {
assert.deepEqual(
transformArguments('key'),
['STRLEN', 'key']
);
});
itWithClient(TestRedisServers.OPEN, 'client.strLen', async client => {
assert.equal(
await client.strLen('key'),
0
);
});
itWithCluster(TestRedisClusters.OPEN, 'cluster.strLen', async cluster => {
assert.equal(
await cluster.strLen('key'),
0
);
});
});

11
lib/commands/STRLEN.ts Normal file
View File

@@ -0,0 +1,11 @@
import { transformReplyNumber } from './generic-transformers';
export const FIRST_KEY_INDEX = 1;
export const IS_READ_ONLY = true;
export function transformArguments(key: string): Array<string> {
return ['STRLEN', key];
}
export const transformReply = transformReplyNumber;

View File

@@ -24,6 +24,7 @@ import * as EXISTS from './EXISTS';
import * as EXPIRE from './EXPIRE'; import * as EXPIRE from './EXPIRE';
import * as EXPIREAT from './EXPIREAT'; import * as EXPIREAT from './EXPIREAT';
import * as FLUSHALL from './FLUSHALL'; import * as FLUSHALL from './FLUSHALL';
import * as FLUSHDB from './FLUSHDB';
import * as GET from './GET'; import * as GET from './GET';
import * as HDEL from './HDEL'; import * as HDEL from './HDEL';
import * as HEXISTS from './HEXISTS'; import * as HEXISTS from './HEXISTS';
@@ -45,10 +46,12 @@ import * as INCR from './INCR';
import * as INCRBY from './INCRBY'; import * as INCRBY from './INCRBY';
import * as INCRBYFLOAT from './INCRBYFLOAT'; import * as INCRBYFLOAT from './INCRBYFLOAT';
import * as KEYS from './KEYS'; import * as KEYS from './KEYS';
import * as LASTSAVE from './LASTSAVE';
import * as LINDEX from './LINDEX'; import * as LINDEX from './LINDEX';
import * as LINSERT from './LINSERT'; import * as LINSERT from './LINSERT';
import * as LLEN from './LLEN'; import * as LLEN from './LLEN';
import * as LMOVE from './LMOVE'; import * as LMOVE from './LMOVE';
import * as LOLWUT from './LOLWUT';
import * as LPOP from './LPOP'; import * as LPOP from './LPOP';
import * as LPOP_COUNT from './LPOP_COUNT'; import * as LPOP_COUNT from './LPOP_COUNT';
import * as LPUSH from './LPUSH'; import * as LPUSH from './LPUSH';
@@ -82,6 +85,7 @@ import * as SCARD from './SCARD';
import * as SDIFF from './SDIFF'; import * as SDIFF from './SDIFF';
import * as SDIFFSTORE from './SDIFFSTORE'; import * as SDIFFSTORE from './SDIFFSTORE';
import * as SET from './SET'; import * as SET from './SET';
import * as SETRANGE from './SETRANGE';
import * as SINTER from './SINTER'; import * as SINTER from './SINTER';
import * as SINTERSTORE from './SINTERSTORE'; import * as SINTERSTORE from './SINTERSTORE';
import * as SISMEMBER from './SISMEMBER'; import * as SISMEMBER from './SISMEMBER';
@@ -94,6 +98,7 @@ import * as SRANDMEMBER_COUNT from './SRANDMEMBER_COUNT';
import * as SRANDMEMBER from './SRANDMEMBER'; import * as SRANDMEMBER from './SRANDMEMBER';
import * as SREM from './SREM'; import * as SREM from './SREM';
import * as SSCAN from './SSCAN'; import * as SSCAN from './SSCAN';
import * as STRLEN from './STRLEN';
import * as SUNION from './SUNION'; import * as SUNION from './SUNION';
import * as SUNIONSTORE from './SUNIONSTORE'; import * as SUNIONSTORE from './SUNIONSTORE';
import * as TOUCH from './TOUCH'; import * as TOUCH from './TOUCH';
@@ -211,6 +216,8 @@ export default {
expireAt: EXPIREAT, expireAt: EXPIREAT,
FLUSHALL, FLUSHALL,
flushAll: FLUSHALL, flushAll: FLUSHALL,
FLUSHDB,
flushDb: FLUSHDB,
GET, GET,
get: GET, get: GET,
HDEL, HDEL,
@@ -253,6 +260,8 @@ export default {
incrByFloat: INCRBYFLOAT, incrByFloat: INCRBYFLOAT,
KEYS, KEYS,
keys: KEYS, keys: KEYS,
LASTSAVE,
lastSave: LASTSAVE,
LINDEX, LINDEX,
lIndex: LINDEX, lIndex: LINDEX,
LINSERT, LINSERT,
@@ -261,6 +270,7 @@ export default {
lLen: LLEN, lLen: LLEN,
LMOVE, LMOVE,
lMove: LMOVE, lMove: LMOVE,
LOLWUT,
LPOP_COUNT, LPOP_COUNT,
lPopCount: LPOP_COUNT, lPopCount: LPOP_COUNT,
LPOP, LPOP,
@@ -331,6 +341,8 @@ export default {
sInterStore: SINTERSTORE, sInterStore: SINTERSTORE,
SET, SET,
set: SET, set: SET,
SETRANGE,
setRange: SETRANGE,
SISMEMBER, SISMEMBER,
sIsMember: SISMEMBER, sIsMember: SISMEMBER,
SMEMBERS, SMEMBERS,
@@ -351,6 +363,8 @@ export default {
sRem: SREM, sRem: SREM,
SSCAN, SSCAN,
sScan: SSCAN, sScan: SSCAN,
STRLEN,
strLen: STRLEN,
SUNION, SUNION,
sUnion: SUNION, sUnion: SUNION,
SUNIONSTORE, SUNIONSTORE,

View File

@@ -50,9 +50,9 @@ async function spawnPasswordServer(): Promise<void> {
} }
async function spawnOpenCluster(): Promise<void> { async function spawnOpenCluster(): Promise<void> {
TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN] = [{ TEST_REDIS_CLUSTERES[TestRedisClusters.OPEN] = (await spawnRedisCluster(TestRedisClusters.OPEN, 3)).map(port => ({
port: (await spawnRedisCluster(TestRedisClusters.OPEN, 3))[0] port
}]; }));
} }
export function itWithClient(type: TestRedisServers, title: string, fn: (client: RedisClientType<RedisModules, RedisLuaScripts>) => Promise<void>): void { export function itWithClient(type: TestRedisServers, title: string, fn: (client: RedisClientType<RedisModules, RedisLuaScripts>) => Promise<void>): void {