Optimize performance of clusterGenNodesDescription for large clusters (#8182)

Optimize the performance of clusterGenNodesDescription by only checking slot ownership of each slot once, instead of checking each slot for each node.
This commit is contained in:
Wang Yuan 2021-01-14 04:36:03 +08:00 committed by GitHub
parent f5577fdbd8
commit 9cb9f98d2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 134 additions and 20 deletions

View File

@ -779,6 +779,7 @@ clusterNode *createClusterNode(char *nodename, int flags) {
node->configEpoch = 0;
node->flags = flags;
memset(node->slots,0,sizeof(node->slots));
node->slots_info = NULL;
node->numslots = 0;
node->numslaves = 0;
node->slaves = NULL;
@ -4144,8 +4145,8 @@ sds clusterGenNodeDescription(clusterNode *node) {
sds ci;
/* Node coordinates */
ci = sdscatprintf(sdsempty(),"%.40s %s:%d@%d ",
node->name,
ci = sdscatlen(sdsempty(),node->name,CLUSTER_NAMELEN);
ci = sdscatfmt(ci," %s:%i@%i ",
node->ip,
node->port,
node->cport);
@ -4154,24 +4155,29 @@ sds clusterGenNodeDescription(clusterNode *node) {
ci = representClusterNodeFlags(ci, node->flags);
/* Slave of... or just "-" */
ci = sdscatlen(ci," ",1);
if (node->slaveof)
ci = sdscatprintf(ci," %.40s ",node->slaveof->name);
ci = sdscatlen(ci,node->slaveof->name,CLUSTER_NAMELEN);
else
ci = sdscatlen(ci," - ",3);
ci = sdscatlen(ci,"-",1);
unsigned long long nodeEpoch = node->configEpoch;
if (nodeIsSlave(node) && node->slaveof) {
nodeEpoch = node->slaveof->configEpoch;
}
/* Latency from the POV of this node, config epoch, link status */
ci = sdscatprintf(ci,"%lld %lld %llu %s",
ci = sdscatfmt(ci," %I %I %U %s",
(long long) node->ping_sent,
(long long) node->pong_received,
nodeEpoch,
(node->link || node->flags & CLUSTER_NODE_MYSELF) ?
"connected" : "disconnected");
/* Slots served by this instance */
/* Slots served by this instance. If we already have slots info,
* append it diretly, otherwise, generate slots only if it has. */
if (node->slots_info) {
ci = sdscatsds(ci, node->slots_info);
} else if (node->numslots > 0) {
start = -1;
for (j = 0; j < CLUSTER_SLOTS; j++) {
int bit;
@ -4183,13 +4189,14 @@ sds clusterGenNodeDescription(clusterNode *node) {
if (bit && j == CLUSTER_SLOTS-1) j++;
if (start == j-1) {
ci = sdscatprintf(ci," %d",start);
ci = sdscatfmt(ci," %i",start);
} else {
ci = sdscatprintf(ci," %d-%d",start,j-1);
ci = sdscatfmt(ci," %i-%i",start,j-1);
}
start = -1;
}
}
}
/* Just for MYSELF node we also dump info about slots that
* we are migrating to other instances or importing from other
@ -4208,6 +4215,41 @@ sds clusterGenNodeDescription(clusterNode *node) {
return ci;
}
/* Generate the slot topology for all nodes and store the string representation
* in the slots_info struct on the node. This is used to improve the efficiency
* of clusterGenNodesDescription() because it removes looping of the slot space
* for generating the slot info for each node individually. */
void clusterGenNodesSlotsInfo(int filter) {
clusterNode *n = NULL;
int start = -1;
for (int i = 0; i <= CLUSTER_SLOTS; i++) {
/* Find start node and slot id. */
if (n == NULL) {
if (i == CLUSTER_SLOTS) break;
n = server.cluster->slots[i];
start = i;
continue;
}
/* Generate slots info when occur different node with start
* or end of slot. */
if (i == CLUSTER_SLOTS || n != server.cluster->slots[i]) {
if (!(n->flags & filter)) {
if (n->slots_info == NULL) n->slots_info = sdsempty();
if (start == i-1) {
n->slots_info = sdscatfmt(n->slots_info," %i",start);
} else {
n->slots_info = sdscatfmt(n->slots_info," %i-%i",start,i-1);
}
}
if (i == CLUSTER_SLOTS) break;
n = server.cluster->slots[i];
start = i;
}
}
}
/* Generate a csv-alike representation of the nodes we are aware of,
* including the "myself" node, and return an SDS string containing the
* representation (it is up to the caller to free it).
@ -4225,6 +4267,9 @@ sds clusterGenNodesDescription(int filter) {
dictIterator *di;
dictEntry *de;
/* Generate all nodes slots info firstly. */
clusterGenNodesSlotsInfo(filter);
di = dictGetSafeIterator(server.cluster->nodes);
while((de = dictNext(di)) != NULL) {
clusterNode *node = dictGetVal(de);
@ -4234,6 +4279,12 @@ sds clusterGenNodesDescription(int filter) {
ci = sdscatsds(ci,ni);
sdsfree(ni);
ci = sdscatlen(ci,"\n",1);
/* Release slots info. */
if (node->slots_info) {
sdsfree(node->slots_info);
node->slots_info = NULL;
}
}
dictReleaseIterator(di);
return ci;

View File

@ -118,6 +118,7 @@ typedef struct clusterNode {
int flags; /* CLUSTER_NODE_... */
uint64_t configEpoch; /* Last configEpoch observed for this node */
unsigned char slots[CLUSTER_SLOTS/8]; /* slots handled by this node */
sds slots_info; /* Slots info represented by string. */
int numslots; /* Number of slots handled by this node */
int numslaves; /* Number of slave nodes, if this is a master */
struct clusterNode **slaves; /* pointers to slave nodes */

View File

@ -0,0 +1,62 @@
# Optimize CLUSTER NODES command by generating all nodes slot topology firstly
source "../tests/includes/init-tests.tcl"
proc cluster_allocate_with_continuous_slots {n} {
set slot 16383
set avg [expr ($slot+1) / $n]
while {$slot >= 0} {
set node [expr $slot/$avg >= $n ? $n-1 : $slot/$avg]
lappend slots_$node $slot
incr slot -1
}
for {set j 0} {$j < $n} {incr j} {
R $j cluster addslots {*}[set slots_${j}]
}
}
proc cluster_create_with_continuous_slots {masters slaves} {
cluster_allocate_with_continuous_slots $masters
if {$slaves} {
cluster_allocate_slaves $masters $slaves
}
assert_cluster_state ok
}
test "Create a 2 nodes cluster" {
cluster_create_with_continuous_slots 2 2
}
test "Cluster should start ok" {
assert_cluster_state ok
}
set master1 [Rn 0]
set master2 [Rn 1]
test "Continuous slots distribution" {
assert_match "* 0-8191*" [$master1 CLUSTER NODES]
assert_match "* 8192-16383*" [$master2 CLUSTER NODES]
$master1 CLUSTER DELSLOTS 4096
assert_match "* 0-4095 4097-8191*" [$master1 CLUSTER NODES]
$master2 CLUSTER DELSLOTS 12288
assert_match "* 8192-12287 12289-16383*" [$master2 CLUSTER NODES]
}
test "Discontinuous slots distribution" {
# Remove middle slots
$master1 CLUSTER DELSLOTS 4092 4094
assert_match "* 0-4091 4093 4095 4097-8191*" [$master1 CLUSTER NODES]
$master2 CLUSTER DELSLOTS 12284 12286
assert_match "* 8192-12283 12285 12287 12289-16383*" [$master2 CLUSTER NODES]
# Remove head slots
$master1 CLUSTER DELSLOTS 0 2
assert_match "* 1 3-4091 4093 4095 4097-8191*" [$master1 CLUSTER NODES]
# Remove tail slots
$master2 CLUSTER DELSLOTS 16380 16382 16383
assert_match "* 8192-12283 12285 12287 12289-16379 16381*" [$master2 CLUSTER NODES]
}