You've already forked node-redis
mirror of
https://github.com/redis/node-redis.git
synced 2025-08-07 13:22:56 +03:00
CLUSERT [INFO|NODES|REPLICAS]
This commit is contained in:
@@ -3,53 +3,18 @@ import testUtils, { GLOBAL } from '../test-utils';
|
||||
import { transformArguments, transformReply } from './CLUSTER_INFO';
|
||||
|
||||
describe('CLUSTER INFO', () => {
|
||||
it('transformArguments', () => {
|
||||
assert.deepEqual(
|
||||
transformArguments(),
|
||||
['CLUSTER', 'INFO']
|
||||
);
|
||||
});
|
||||
it('transformArguments', () => {
|
||||
assert.deepEqual(
|
||||
transformArguments(),
|
||||
['CLUSTER', 'INFO']
|
||||
);
|
||||
});
|
||||
|
||||
it('transformReply', () => {
|
||||
assert.deepEqual(
|
||||
transformReply([
|
||||
'cluster_state:ok',
|
||||
'cluster_slots_assigned:16384',
|
||||
'cluster_slots_ok:16384',
|
||||
'cluster_slots_pfail:0',
|
||||
'cluster_slots_fail:0',
|
||||
'cluster_known_nodes:6',
|
||||
'cluster_size:3',
|
||||
'cluster_current_epoch:6',
|
||||
'cluster_my_epoch:2',
|
||||
'cluster_stats_messages_sent:1483972',
|
||||
'cluster_stats_messages_received:1483968'
|
||||
].join('\r\n')),
|
||||
{
|
||||
state: 'ok',
|
||||
slots: {
|
||||
assigned: 16384,
|
||||
ok: 16384,
|
||||
pfail: 0,
|
||||
fail: 0
|
||||
},
|
||||
knownNodes: 6,
|
||||
size: 3,
|
||||
currentEpoch: 6,
|
||||
myEpoch: 2,
|
||||
stats: {
|
||||
messagesSent: 1483972,
|
||||
messagesReceived: 1483968
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
testUtils.testWithCluster('clusterNode.clusterInfo', async cluster => {
|
||||
const client = await cluster.nodeClient(cluster.masters[0]);
|
||||
assert.notEqual(
|
||||
await client.clusterInfo(),
|
||||
null
|
||||
);
|
||||
}, GLOBAL.CLUSTERS.OPEN);
|
||||
testUtils.testWithCluster('clusterNode.clusterInfo', async cluster => {
|
||||
const client = await cluster.nodeClient(cluster.masters[0]);
|
||||
assert.equal(
|
||||
typeof await client.clusterInfo(),
|
||||
'string'
|
||||
);
|
||||
}, GLOBAL.CLUSTERS.OPEN);
|
||||
});
|
||||
|
@@ -1,47 +1,10 @@
|
||||
export function transformArguments(): Array<string> {
|
||||
import { VerbatimStringReply, Command } from '@redis/client/dist/lib/RESP/types';
|
||||
|
||||
export default {
|
||||
FIRST_KEY_INDEX: undefined,
|
||||
IS_READ_ONLY: true,
|
||||
transformArguments() {
|
||||
return ['CLUSTER', 'INFO'];
|
||||
}
|
||||
|
||||
interface ClusterInfoReply {
|
||||
state: string;
|
||||
slots: {
|
||||
assigned: number;
|
||||
ok: number;
|
||||
pfail: number;
|
||||
fail: number;
|
||||
};
|
||||
knownNodes: number;
|
||||
size: number;
|
||||
currentEpoch: number;
|
||||
myEpoch: number;
|
||||
stats: {
|
||||
messagesSent: number;
|
||||
messagesReceived: number;
|
||||
};
|
||||
}
|
||||
|
||||
export function transformReply(reply: string): ClusterInfoReply {
|
||||
const lines = reply.split('\r\n');
|
||||
|
||||
return {
|
||||
state: extractLineValue(lines[0]),
|
||||
slots: {
|
||||
assigned: Number(extractLineValue(lines[1])),
|
||||
ok: Number(extractLineValue(lines[2])),
|
||||
pfail: Number(extractLineValue(lines[3])),
|
||||
fail: Number(extractLineValue(lines[4]))
|
||||
},
|
||||
knownNodes: Number(extractLineValue(lines[5])),
|
||||
size: Number(extractLineValue(lines[6])),
|
||||
currentEpoch: Number(extractLineValue(lines[7])),
|
||||
myEpoch: Number(extractLineValue(lines[8])),
|
||||
stats: {
|
||||
messagesSent: Number(extractLineValue(lines[9])),
|
||||
messagesReceived: Number(extractLineValue(lines[10]))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function extractLineValue(line: string): string {
|
||||
return line.substring(line.indexOf(':') + 1);
|
||||
}
|
||||
},
|
||||
transformReply: undefined as unknown as () => VerbatimStringReply
|
||||
} as const satisfies Command;
|
||||
|
@@ -1,145 +1,20 @@
|
||||
import { strict as assert } from 'assert';
|
||||
import { RedisClusterNodeLinkStates, transformArguments, transformReply } from './CLUSTER_NODES';
|
||||
import testUtils, { GLOBAL } from '../test-utils';
|
||||
import CLUSTER_NODES from './CLUSTER_NODES';
|
||||
|
||||
describe('CLUSTER NODES', () => {
|
||||
it('transformArguments', () => {
|
||||
assert.deepEqual(
|
||||
transformArguments(),
|
||||
['CLUSTER', 'NODES']
|
||||
);
|
||||
});
|
||||
it('transformArguments', () => {
|
||||
assert.deepEqual(
|
||||
CLUSTER_NODES.transformArguments(),
|
||||
['CLUSTER', 'NODES']
|
||||
);
|
||||
});
|
||||
|
||||
describe('transformReply', () => {
|
||||
it('simple', () => {
|
||||
assert.deepEqual(
|
||||
transformReply([
|
||||
'master 127.0.0.1:30001@31001 myself,master - 0 0 1 connected 0-16384',
|
||||
'slave 127.0.0.1:30002@31002 slave master 0 0 1 connected',
|
||||
''
|
||||
].join('\n')),
|
||||
[{
|
||||
id: 'master',
|
||||
address: '127.0.0.1:30001@31001',
|
||||
host: '127.0.0.1',
|
||||
port: 30001,
|
||||
cport: 31001,
|
||||
flags: ['myself', 'master'],
|
||||
pingSent: 0,
|
||||
pongRecv: 0,
|
||||
configEpoch: 1,
|
||||
linkState: RedisClusterNodeLinkStates.CONNECTED,
|
||||
slots: [{
|
||||
from: 0,
|
||||
to: 16384
|
||||
}],
|
||||
replicas: [{
|
||||
id: 'slave',
|
||||
address: '127.0.0.1:30002@31002',
|
||||
host: '127.0.0.1',
|
||||
port: 30002,
|
||||
cport: 31002,
|
||||
flags: ['slave'],
|
||||
pingSent: 0,
|
||||
pongRecv: 0,
|
||||
configEpoch: 1,
|
||||
linkState: RedisClusterNodeLinkStates.CONNECTED
|
||||
}]
|
||||
}]
|
||||
);
|
||||
});
|
||||
|
||||
it('should support addresses without cport', () => {
|
||||
assert.deepEqual(
|
||||
transformReply(
|
||||
'id 127.0.0.1:30001 master - 0 0 0 connected 0-16384\n'
|
||||
),
|
||||
[{
|
||||
id: 'id',
|
||||
address: '127.0.0.1:30001',
|
||||
host: '127.0.0.1',
|
||||
port: 30001,
|
||||
cport: null,
|
||||
flags: ['master'],
|
||||
pingSent: 0,
|
||||
pongRecv: 0,
|
||||
configEpoch: 0,
|
||||
linkState: RedisClusterNodeLinkStates.CONNECTED,
|
||||
slots: [{
|
||||
from: 0,
|
||||
to: 16384
|
||||
}],
|
||||
replicas: []
|
||||
}]
|
||||
);
|
||||
});
|
||||
|
||||
it('should support ipv6 addresses', () => {
|
||||
assert.deepEqual(
|
||||
transformReply(
|
||||
'id 2a02:6b8:c21:330d:0:1589:ebbe:b1a0:6379@16379 master - 0 0 0 connected 0-549\n'
|
||||
),
|
||||
[{
|
||||
id: 'id',
|
||||
address: '2a02:6b8:c21:330d:0:1589:ebbe:b1a0:6379@16379',
|
||||
host: '2a02:6b8:c21:330d:0:1589:ebbe:b1a0',
|
||||
port: 6379,
|
||||
cport: 16379,
|
||||
flags: ['master'],
|
||||
pingSent: 0,
|
||||
pongRecv: 0,
|
||||
configEpoch: 0,
|
||||
linkState: RedisClusterNodeLinkStates.CONNECTED,
|
||||
slots: [{
|
||||
from: 0,
|
||||
to: 549
|
||||
}],
|
||||
replicas: []
|
||||
}]
|
||||
);
|
||||
});
|
||||
|
||||
it.skip('with importing slots', () => {
|
||||
assert.deepEqual(
|
||||
transformReply(
|
||||
'id 127.0.0.1:30001@31001 master - 0 0 0 connected 0-<-16384\n'
|
||||
),
|
||||
[{
|
||||
id: 'id',
|
||||
address: '127.0.0.1:30001@31001',
|
||||
host: '127.0.0.1',
|
||||
port: 30001,
|
||||
cport: 31001,
|
||||
flags: ['master'],
|
||||
pingSent: 0,
|
||||
pongRecv: 0,
|
||||
configEpoch: 0,
|
||||
linkState: RedisClusterNodeLinkStates.CONNECTED,
|
||||
slots: [], // TODO
|
||||
replicas: []
|
||||
}]
|
||||
);
|
||||
});
|
||||
|
||||
it.skip('with migrating slots', () => {
|
||||
assert.deepEqual(
|
||||
transformReply(
|
||||
'id 127.0.0.1:30001@31001 master - 0 0 0 connected 0->-16384\n'
|
||||
),
|
||||
[{
|
||||
id: 'id',
|
||||
address: '127.0.0.1:30001@31001',
|
||||
host: '127.0.0.1',
|
||||
port: 30001,
|
||||
cport: 31001,
|
||||
flags: ['master'],
|
||||
pingSent: 0,
|
||||
pongRecv: 0,
|
||||
configEpoch: 0,
|
||||
linkState: RedisClusterNodeLinkStates.CONNECTED,
|
||||
slots: [], // TODO
|
||||
replicas: []
|
||||
}]
|
||||
);
|
||||
});
|
||||
});
|
||||
testUtils.testWithCluster('clusterNode.clusterNodes', async cluster => {
|
||||
const client = await cluster.nodeClient(cluster.masters[0]);
|
||||
assert.equal(
|
||||
typeof await client.clusterNodes(),
|
||||
'string'
|
||||
);
|
||||
}, GLOBAL.CLUSTERS.OPEN);
|
||||
});
|
||||
|
@@ -1,105 +1,10 @@
|
||||
export function transformArguments(): Array<string> {
|
||||
import { VerbatimStringReply, Command } from '@redis/client/dist/lib/RESP/types';
|
||||
|
||||
export default {
|
||||
FIRST_KEY_INDEX: undefined,
|
||||
IS_READ_ONLY: true,
|
||||
transformArguments() {
|
||||
return ['CLUSTER', 'NODES'];
|
||||
}
|
||||
|
||||
export enum RedisClusterNodeLinkStates {
|
||||
CONNECTED = 'connected',
|
||||
DISCONNECTED = 'disconnected'
|
||||
}
|
||||
|
||||
interface RedisClusterNodeAddress {
|
||||
host: string;
|
||||
port: number;
|
||||
cport: number | null;
|
||||
}
|
||||
|
||||
export interface RedisClusterReplicaNode extends RedisClusterNodeAddress {
|
||||
id: string;
|
||||
address: string;
|
||||
flags: Array<string>;
|
||||
pingSent: number;
|
||||
pongRecv: number;
|
||||
configEpoch: number;
|
||||
linkState: RedisClusterNodeLinkStates;
|
||||
}
|
||||
|
||||
export interface RedisClusterMasterNode extends RedisClusterReplicaNode {
|
||||
slots: Array<{
|
||||
from: number;
|
||||
to: number;
|
||||
}>;
|
||||
replicas: Array<RedisClusterReplicaNode>;
|
||||
}
|
||||
|
||||
export function transformReply(reply: string): Array<RedisClusterMasterNode> {
|
||||
const lines = reply.split('\n');
|
||||
lines.pop(); // last line is empty
|
||||
|
||||
const mastersMap = new Map<string, RedisClusterMasterNode>(),
|
||||
replicasMap = new Map<string, Array<RedisClusterReplicaNode>>();
|
||||
|
||||
for (const line of lines) {
|
||||
const [id, address, flags, masterId, pingSent, pongRecv, configEpoch, linkState, ...slots] = line.split(' '),
|
||||
node = {
|
||||
id,
|
||||
address,
|
||||
...transformNodeAddress(address),
|
||||
flags: flags.split(','),
|
||||
pingSent: Number(pingSent),
|
||||
pongRecv: Number(pongRecv),
|
||||
configEpoch: Number(configEpoch),
|
||||
linkState: (linkState as RedisClusterNodeLinkStates)
|
||||
};
|
||||
|
||||
if (masterId === '-') {
|
||||
let replicas = replicasMap.get(id);
|
||||
if (!replicas) {
|
||||
replicas = [];
|
||||
replicasMap.set(id, replicas);
|
||||
}
|
||||
|
||||
mastersMap.set(id, {
|
||||
...node,
|
||||
slots: slots.map(slot => {
|
||||
// TODO: importing & exporting (https://redis.io/commands/cluster-nodes#special-slot-entries)
|
||||
const [fromString, toString] = slot.split('-', 2),
|
||||
from = Number(fromString);
|
||||
return {
|
||||
from,
|
||||
to: toString ? Number(toString) : from
|
||||
};
|
||||
}),
|
||||
replicas
|
||||
});
|
||||
} else {
|
||||
const replicas = replicasMap.get(masterId);
|
||||
if (!replicas) {
|
||||
replicasMap.set(masterId, [node]);
|
||||
} else {
|
||||
replicas.push(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [...mastersMap.values()];
|
||||
}
|
||||
|
||||
function transformNodeAddress(address: string): RedisClusterNodeAddress {
|
||||
const indexOfColon = address.lastIndexOf(':'),
|
||||
indexOfAt = address.indexOf('@', indexOfColon),
|
||||
host = address.substring(0, indexOfColon);
|
||||
|
||||
if (indexOfAt === -1) {
|
||||
return {
|
||||
host,
|
||||
port: Number(address.substring(indexOfColon + 1)),
|
||||
cport: null
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
host: address.substring(0, indexOfColon),
|
||||
port: Number(address.substring(indexOfColon + 1, indexOfAt)),
|
||||
cport: Number(address.substring(indexOfAt + 1))
|
||||
};
|
||||
}
|
||||
},
|
||||
transformReply: undefined as unknown as () => VerbatimStringReply
|
||||
} as const satisfies Command;
|
||||
|
@@ -1,11 +1,20 @@
|
||||
import { strict as assert } from 'assert';
|
||||
import { transformArguments } from './CLUSTER_REPLICAS';
|
||||
import testUtils, { GLOBAL } from '../test-utils';
|
||||
import CLUSTER_REPLICAS from './CLUSTER_REPLICAS';
|
||||
|
||||
describe('CLUSTER REPLICAS', () => {
|
||||
it('transformArguments', () => {
|
||||
assert.deepEqual(
|
||||
transformArguments('0'),
|
||||
['CLUSTER', 'REPLICAS', '0']
|
||||
);
|
||||
});
|
||||
it('transformArguments', () => {
|
||||
assert.deepEqual(
|
||||
CLUSTER_REPLICAS.transformArguments('0'),
|
||||
['CLUSTER', 'REPLICAS', '0']
|
||||
);
|
||||
});
|
||||
|
||||
testUtils.testWithCluster('clusterNode.clusterReplicas', async cluster => {
|
||||
const client = await cluster.nodeClient(cluster.masters[0]);
|
||||
assert.equal(
|
||||
typeof await client.clusterReplicas(cluster.masters[0].id),
|
||||
'string'
|
||||
);
|
||||
}, GLOBAL.CLUSTERS.OPEN);
|
||||
});
|
||||
|
@@ -1,5 +1,10 @@
|
||||
export function transformArguments(nodeId: string): Array<string> {
|
||||
return ['CLUSTER', 'REPLICAS', nodeId];
|
||||
}
|
||||
import { RedisArgument, VerbatimStringReply, Command } from '@redis/client/dist/lib/RESP/types';
|
||||
|
||||
export { transformReply } from './CLUSTER_NODES';
|
||||
export default {
|
||||
FIRST_KEY_INDEX: undefined,
|
||||
IS_READ_ONLY: true,
|
||||
transformArguments(nodeId: RedisArgument) {
|
||||
return ['CLUSTER', 'REPLICAS', nodeId];
|
||||
},
|
||||
transformReply: undefined as unknown as () => VerbatimStringReply
|
||||
} as const satisfies Command;
|
||||
|
@@ -54,13 +54,13 @@ import CLUSTER_FAILOVER from './CLUSTER_FAILOVER';
|
||||
import CLUSTER_FLUSHSLOTS from './CLUSTER_FLUSHSLOTS';
|
||||
import CLUSTER_FORGET from './CLUSTER_FORGET';
|
||||
import CLUSTER_GETKEYSINSLOT from './CLUSTER_GETKEYSINSLOT';
|
||||
// import CLUSTER_INFO from './CLUSTER_INFO';
|
||||
import CLUSTER_INFO from './CLUSTER_INFO';
|
||||
import CLUSTER_KEYSLOT from './CLUSTER_KEYSLOT';
|
||||
import CLUSTER_LINKS from './CLUSTER_LINKS';
|
||||
import CLUSTER_MEET from './CLUSTER_MEET';
|
||||
import CLUSTER_MYID from './CLUSTER_MYID';
|
||||
// import CLUSTER_NODES from './CLUSTER_NODES';
|
||||
// import CLUSTER_REPLICAS from './CLUSTER_REPLICAS';
|
||||
import CLUSTER_NODES from './CLUSTER_NODES';
|
||||
import CLUSTER_REPLICAS from './CLUSTER_REPLICAS';
|
||||
import CLUSTER_REPLICATE from './CLUSTER_REPLICATE';
|
||||
import CLUSTER_RESET from './CLUSTER_RESET';
|
||||
import CLUSTER_SAVECONFIG from './CLUSTER_SAVECONFIG';
|
||||
@@ -437,8 +437,8 @@ export default {
|
||||
clusterForget: CLUSTER_FORGET,
|
||||
CLUSTER_GETKEYSINSLOT,
|
||||
clusterGetKeysInSlot: CLUSTER_GETKEYSINSLOT,
|
||||
// CLUSTER_INFO,
|
||||
// clusterInfo: CLUSTER_INFO,
|
||||
CLUSTER_INFO,
|
||||
clusterInfo: CLUSTER_INFO,
|
||||
CLUSTER_KEYSLOT,
|
||||
clusterKeySlot: CLUSTER_KEYSLOT,
|
||||
CLUSTER_LINKS,
|
||||
@@ -447,10 +447,10 @@ export default {
|
||||
clusterMeet: CLUSTER_MEET,
|
||||
CLUSTER_MYID,
|
||||
clusterMyId: CLUSTER_MYID,
|
||||
// CLUSTER_NODES,
|
||||
// clusterNodes: CLUSTER_NODES,
|
||||
// CLUSTER_REPLICAS,
|
||||
// clusterReplicas: CLUSTER_REPLICAS,
|
||||
CLUSTER_NODES,
|
||||
clusterNodes: CLUSTER_NODES,
|
||||
CLUSTER_REPLICAS,
|
||||
clusterReplicas: CLUSTER_REPLICAS,
|
||||
CLUSTER_REPLICATE,
|
||||
clusterReplicate: CLUSTER_REPLICATE,
|
||||
CLUSTER_RESET,
|
||||
|
Reference in New Issue
Block a user