Skip to content

Commit

Permalink
Dual stack and client-specific IPs in cluster (#736)
Browse files Browse the repository at this point in the history
New configs:

* `cluster-announce-client-ipv4`
* `cluster-announce-client-ipv6`

New module API function:

* `ValkeyModule_GetClusterNodeInfoForClient`, takes a client id and is
otherwise just like its non-ForClient cousin.

If configured, one of these IP addresses are reported to each client in
CLUSTER SLOTS, CLUSTER SHARDS, CLUSTER NODES and redirects, replacing
the IP (`custer-announce-ip` or the auto-detected IP) of each node.
Which one is reported to the client depends on whether the client is
connected over IPv4 or IPv6.

Benefits:

* This allows clients using IPv4 to get the IPv4 addresses of all
cluster nodes and IPv6 clients to get the IPv6 clients.
* This allows the IPs visible to clients to be different to the IPs used
between the cluster nodes due to NAT'ing.

The information is propagated in the cluster bus using new Ping
extensions. (Old nodes without this feature ignore unknown Ping
extensions.)

This adds another dimension to CLUSTER SLOTS reply. It now depends on
the client's use of TLS, the IP address family and RESP version.
Refactoring: The cached connection type definition is moved from
connection.h (it actually has nothing to do with the connection
abstraction) to server.h and is changed to a bitmap, with one bit for
each of TLS, IPv6 and RESP3.

Fixes #337

---------

Signed-off-by: Viktor Söderqvist <[email protected]>
  • Loading branch information
zuiderkwast authored Jul 10, 2024
1 parent 6a5a11f commit a323dce
Show file tree
Hide file tree
Showing 14 changed files with 497 additions and 130 deletions.
27 changes: 14 additions & 13 deletions src/cluster.c
Original file line number Diff line number Diff line change
Expand Up @@ -747,7 +747,7 @@ int verifyClusterNodeId(const char *name, int length) {
}

int isValidAuxChar(int c) {
return isalnum(c) || (strchr("!#$%&()*+:;<>?@[]^{|}~", c) == NULL);
return isalnum(c) || (strchr("!#$%&()*+.:;<>?@[]^{|}~", c) == NULL);
}

int isValidAuxString(char *s, unsigned int length) {
Expand Down Expand Up @@ -1194,7 +1194,7 @@ void clusterRedirectClient(client *c, clusterNode *n, int hashslot, int error_co
int port = clusterNodeClientPort(n, shouldReturnTlsInfo());
addReplyErrorSds(c,
sdscatprintf(sdsempty(), "-%s %d %s:%d", (error_code == CLUSTER_REDIR_ASK) ? "ASK" : "MOVED",
hashslot, clusterNodePreferredEndpoint(n), port));
hashslot, clusterNodePreferredEndpoint(n, c), port));
} else {
serverPanic("getNodeByQuery() unknown error.");
}
Expand Down Expand Up @@ -1267,7 +1267,7 @@ void addNodeToNodeReply(client *c, clusterNode *node) {
char *hostname = clusterNodeHostname(node);
addReplyArrayLen(c, 4);
if (server.cluster_preferred_endpoint_type == CLUSTER_ENDPOINT_TYPE_IP) {
addReplyBulkCString(c, clusterNodeIp(node));
addReplyBulkCString(c, clusterNodeIp(node, c));
} else if (server.cluster_preferred_endpoint_type == CLUSTER_ENDPOINT_TYPE_HOSTNAME) {
if (hostname != NULL && hostname[0] != '\0') {
addReplyBulkCString(c, hostname);
Expand Down Expand Up @@ -1300,7 +1300,7 @@ void addNodeToNodeReply(client *c, clusterNode *node) {

if (server.cluster_preferred_endpoint_type != CLUSTER_ENDPOINT_TYPE_IP) {
addReplyBulkCString(c, "ip");
addReplyBulkCString(c, clusterNodeIp(node));
addReplyBulkCString(c, clusterNodeIp(node, c));
length--;
}
if (server.cluster_preferred_endpoint_type != CLUSTER_ENDPOINT_TYPE_HOSTNAME && hostname != NULL &&
Expand Down Expand Up @@ -1353,12 +1353,10 @@ void addNodeReplyForClusterSlot(client *c, clusterNode *node, int start_slot, in
}

void clearCachedClusterSlotsResponse(void) {
for (connTypeForCaching conn_type = CACHE_CONN_TCP; conn_type < CACHE_CONN_TYPE_MAX; conn_type++) {
for (int resp = 0; resp <= 3; resp++) {
if (server.cached_cluster_slot_info[conn_type][resp]) {
sdsfree(server.cached_cluster_slot_info[conn_type][resp]);
server.cached_cluster_slot_info[conn_type][resp] = NULL;
}
for (int conn_type = 0; conn_type < CACHE_CONN_TYPE_MAX; conn_type++) {
if (server.cached_cluster_slot_info[conn_type]) {
sdsfree(server.cached_cluster_slot_info[conn_type]);
server.cached_cluster_slot_info[conn_type] = NULL;
}
}
}
Expand Down Expand Up @@ -1415,14 +1413,17 @@ void clusterCommandSlots(client *c) {
* 3) node ID
* ... continued until done
*/
connTypeForCaching conn_type = shouldReturnTlsInfo();
int conn_type = 0;
if (connIsTLS(c->conn)) conn_type |= CACHE_CONN_TYPE_TLS;
if (isClientConnIpV6(c)) conn_type |= CACHE_CONN_TYPE_IPv6;
if (c->resp == 3) conn_type |= CACHE_CONN_TYPE_RESP3;

if (detectAndUpdateCachedNodeHealth()) clearCachedClusterSlotsResponse();

sds cached_reply = server.cached_cluster_slot_info[conn_type][c->resp];
sds cached_reply = server.cached_cluster_slot_info[conn_type];
if (!cached_reply) {
cached_reply = generateClusterSlotResponse(c->resp);
server.cached_cluster_slot_info[conn_type][c->resp] = cached_reply;
server.cached_cluster_slot_info[conn_type] = cached_reply;
} else {
debugServerAssertWithInfo(c, NULL, verifyCachedClusterSlotsResponse(cached_reply, c->resp) == 1);
}
Expand Down
6 changes: 4 additions & 2 deletions src/cluster.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ int clusterSendModuleMessageToTarget(const char *target,

void clusterUpdateMyselfFlags(void);
void clusterUpdateMyselfIp(void);
void clusterUpdateMyselfClientIpV4(void);
void clusterUpdateMyselfClientIpV6(void);
void clusterUpdateMyselfHostname(void);
void clusterUpdateMyselfAnnouncedPorts(void);
void clusterUpdateMyselfHumanNodename(void);
Expand Down Expand Up @@ -85,7 +87,7 @@ int handleDebugClusterCommand(client *c);
int clusterNodePending(clusterNode *node);
int clusterNodeIsPrimary(clusterNode *n);
char **getClusterNodesList(size_t *numnodes);
char *clusterNodeIp(clusterNode *node);
char *clusterNodeIp(clusterNode *node, client *c);
int clusterNodeIsReplica(clusterNode *node);
clusterNode *clusterNodeGetPrimary(clusterNode *node);
char *clusterNodeGetName(clusterNode *node);
Expand All @@ -100,7 +102,7 @@ clusterNode *getImportingSlotSource(int slot);
clusterNode *getNodeBySlot(int slot);
int clusterNodeClientPort(clusterNode *n, int use_tls);
char *clusterNodeHostname(clusterNode *node);
const char *clusterNodePreferredEndpoint(clusterNode *n);
const char *clusterNodePreferredEndpoint(clusterNode *n, client *c);
long long clusterNodeReplOffset(clusterNode *node);
clusterNode *clusterLookupNode(const char *name, int length);
int detectAndUpdateCachedNodeHealth(void);
Expand Down
Loading

0 comments on commit a323dce

Please sign in to comment.