Commit 90891a7d authored by Moti Cohen's avatar Moti Cohen Committed by Oran Agra
Browse files

Fixed Sentinel support for hostnames (#10146)

Sentinel tries to resolve instances hostname to IP only during registration.
It might be that the instance is unavailable during that time, such as
leader crashed and failover took place. Yet, promoted replica must support:

 - Register leader, even if it fails to resolve its hostname during failover
 - Try later to resolve it, if instance is disconnected. Note that
   this condition also support ip-change of an instance.

(cherry picked from commit 79f089bd)
parent 4a59230d
...@@ -593,10 +593,10 @@ void sentinelIsRunning(void) { ...@@ -593,10 +593,10 @@ void sentinelIsRunning(void) {
/* Create a sentinelAddr object and return it on success. /* Create a sentinelAddr object and return it on success.
* On error NULL is returned and errno is set to: * On error NULL is returned and errno is set to:
* ENOENT: Can't resolve the hostname. * ENOENT: Can't resolve the hostname, unless accept_unresolved is non-zero.
* EINVAL: Invalid port number. * EINVAL: Invalid port number.
*/ */
sentinelAddr *createSentinelAddr(char *hostname, int port) { sentinelAddr *createSentinelAddr(char *hostname, int port, int is_accept_unresolved) {
char ip[NET_IP_STR_LEN]; char ip[NET_IP_STR_LEN];
sentinelAddr *sa; sentinelAddr *sa;
...@@ -606,8 +606,14 @@ sentinelAddr *createSentinelAddr(char *hostname, int port) { ...@@ -606,8 +606,14 @@ sentinelAddr *createSentinelAddr(char *hostname, int port) {
} }
if (anetResolve(NULL,hostname,ip,sizeof(ip), if (anetResolve(NULL,hostname,ip,sizeof(ip),
sentinel.resolve_hostnames ? ANET_NONE : ANET_IP_ONLY) == ANET_ERR) { sentinel.resolve_hostnames ? ANET_NONE : ANET_IP_ONLY) == ANET_ERR) {
errno = ENOENT; serverLog(LL_WARNING, "Failed to resolve hostname '%s'", hostname);
return NULL; if (sentinel.resolve_hostnames && is_accept_unresolved) {
ip[0] = '\0';
}
else {
errno = ENOENT;
return NULL;
}
} }
sa = zmalloc(sizeof(*sa)); sa = zmalloc(sizeof(*sa));
sa->hostname = sdsnew(hostname); sa->hostname = sdsnew(hostname);
...@@ -639,6 +645,15 @@ int sentinelAddrIsEqual(sentinelAddr *a, sentinelAddr *b) { ...@@ -639,6 +645,15 @@ int sentinelAddrIsEqual(sentinelAddr *a, sentinelAddr *b) {
return a->port == b->port && !strcasecmp(a->ip,b->ip); return a->port == b->port && !strcasecmp(a->ip,b->ip);
} }
/* Return non-zero if the two addresses are equal, either by address
* or by hostname if they could not have been resolved.
*/
int sentinelAddrOrHostnameEqual(sentinelAddr *a, sentinelAddr *b) {
return a->port == b->port &&
(!strcmp(a->ip, b->ip) ||
!strcasecmp(a->hostname, b->hostname));
}
/* Return non-zero if a hostname matches an address. */ /* Return non-zero if a hostname matches an address. */
int sentinelAddrEqualsHostname(sentinelAddr *a, char *hostname) { int sentinelAddrEqualsHostname(sentinelAddr *a, char *hostname) {
char ip[NET_IP_STR_LEN]; char ip[NET_IP_STR_LEN];
...@@ -1296,7 +1311,7 @@ sentinelRedisInstance *createSentinelRedisInstance(char *name, int flags, char * ...@@ -1296,7 +1311,7 @@ sentinelRedisInstance *createSentinelRedisInstance(char *name, int flags, char *
serverAssert((flags & SRI_MASTER) || master != NULL); serverAssert((flags & SRI_MASTER) || master != NULL);
/* Check address validity. */ /* Check address validity. */
addr = createSentinelAddr(hostname,port); addr = createSentinelAddr(hostname,port,1);
if (addr == NULL) return NULL; if (addr == NULL) return NULL;
/* For slaves use ip/host:port as name. */ /* For slaves use ip/host:port as name. */
...@@ -1424,7 +1439,7 @@ sentinelRedisInstance *sentinelRedisInstanceLookupSlave( ...@@ -1424,7 +1439,7 @@ sentinelRedisInstance *sentinelRedisInstanceLookupSlave(
* If that is the case, depending on configuration we either resolve * If that is the case, depending on configuration we either resolve
* it and use the IP addres or fail. * it and use the IP addres or fail.
*/ */
addr = createSentinelAddr(slave_addr, port); addr = createSentinelAddr(slave_addr, port, 0);
if (!addr) return NULL; if (!addr) return NULL;
key = announceSentinelAddrAndPort(addr); key = announceSentinelAddrAndPort(addr);
releaseSentinelAddr(addr); releaseSentinelAddr(addr);
...@@ -1487,8 +1502,10 @@ sentinelRedisInstance *getSentinelRedisInstanceByAddrAndRunID(dict *instances, c ...@@ -1487,8 +1502,10 @@ sentinelRedisInstance *getSentinelRedisInstanceByAddrAndRunID(dict *instances, c
serverAssert(addr || runid); /* User must pass at least one search param. */ serverAssert(addr || runid); /* User must pass at least one search param. */
if (addr != NULL) { if (addr != NULL) {
/* Resolve addr, we use the IP as a key even if a hostname is used */ /* Try to resolve addr. If hostnames are used, we're accepting an ri_addr
ri_addr = createSentinelAddr(addr, port); * that contains an hostname only and can still be matched based on that.
*/
ri_addr = createSentinelAddr(addr,port,1);
if (!ri_addr) return NULL; if (!ri_addr) return NULL;
} }
di = dictGetIterator(instances); di = dictGetIterator(instances);
...@@ -1497,8 +1514,7 @@ sentinelRedisInstance *getSentinelRedisInstanceByAddrAndRunID(dict *instances, c ...@@ -1497,8 +1514,7 @@ sentinelRedisInstance *getSentinelRedisInstanceByAddrAndRunID(dict *instances, c
if (runid && !ri->runid) continue; if (runid && !ri->runid) continue;
if ((runid == NULL || strcmp(ri->runid, runid) == 0) && if ((runid == NULL || strcmp(ri->runid, runid) == 0) &&
(addr == NULL || (strcmp(ri->addr->ip, ri_addr->ip) == 0 && (addr == NULL || sentinelAddrOrHostnameEqual(ri->addr, ri_addr)))
ri->addr->port == port)))
{ {
instance = ri; instance = ri;
break; break;
...@@ -1629,7 +1645,7 @@ int sentinelResetMasterAndChangeAddress(sentinelRedisInstance *master, char *hos ...@@ -1629,7 +1645,7 @@ int sentinelResetMasterAndChangeAddress(sentinelRedisInstance *master, char *hos
dictIterator *di; dictIterator *di;
dictEntry *de; dictEntry *de;
newaddr = createSentinelAddr(hostname,port); newaddr = createSentinelAddr(hostname,port,0);
if (newaddr == NULL) return C_ERR; if (newaddr == NULL) return C_ERR;
/* There can be only 0 or 1 slave that has the newaddr. /* There can be only 0 or 1 slave that has the newaddr.
...@@ -2414,6 +2430,7 @@ static int instanceLinkNegotiateTLS(redisAsyncContext *context) { ...@@ -2414,6 +2430,7 @@ static int instanceLinkNegotiateTLS(redisAsyncContext *context) {
* is disconnected. Note that link->disconnected is true even if just * is disconnected. Note that link->disconnected is true even if just
* one of the two links (commands and pub/sub) is missing. */ * one of the two links (commands and pub/sub) is missing. */
void sentinelReconnectInstance(sentinelRedisInstance *ri) { void sentinelReconnectInstance(sentinelRedisInstance *ri) {
if (ri->link->disconnected == 0) return; if (ri->link->disconnected == 0) return;
if (ri->addr->port == 0) return; /* port == 0 means invalid address. */ if (ri->addr->port == 0) return; /* port == 0 means invalid address. */
instanceLink *link = ri->link; instanceLink *link = ri->link;
...@@ -2424,7 +2441,21 @@ void sentinelReconnectInstance(sentinelRedisInstance *ri) { ...@@ -2424,7 +2441,21 @@ void sentinelReconnectInstance(sentinelRedisInstance *ri) {
/* Commands connection. */ /* Commands connection. */
if (link->cc == NULL) { if (link->cc == NULL) {
/* It might be that the instance is disconnected because it wasn't available earlier when the instance
* allocated, say during failover, and therefore we failed to resolve its ip.
* Another scenario is that the instance restarted with new ip, and we should resolve its new ip based on
* its hostname */
if (sentinel.resolve_hostnames) {
sentinelAddr *tryResolveAddr = createSentinelAddr(ri->addr->hostname, ri->addr->port, 0);
if (tryResolveAddr != NULL) {
releaseSentinelAddr(ri->addr);
ri->addr = tryResolveAddr;
}
}
link->cc = redisAsyncConnectBind(ri->addr->ip,ri->addr->port,NET_FIRST_BIND_ADDR); link->cc = redisAsyncConnectBind(ri->addr->ip,ri->addr->port,NET_FIRST_BIND_ADDR);
if (link->cc && !link->cc->err) anetCloexec(link->cc->c.fd); if (link->cc && !link->cc->err) anetCloexec(link->cc->c.fd);
if (!link->cc) { if (!link->cc) {
sentinelEvent(LL_DEBUG,"-cmd-link-reconnection",ri,"%@ #Failed to establish connection"); sentinelEvent(LL_DEBUG,"-cmd-link-reconnection",ri,"%@ #Failed to establish connection");
...@@ -4704,6 +4735,7 @@ sentinelRedisInstance *sentinelSelectSlave(sentinelRedisInstance *master) { ...@@ -4704,6 +4735,7 @@ sentinelRedisInstance *sentinelSelectSlave(sentinelRedisInstance *master) {
max_master_down_time += master->down_after_period * 10; max_master_down_time += master->down_after_period * 10;
di = dictGetIterator(master->slaves); di = dictGetIterator(master->slaves);
while((de = dictNext(di)) != NULL) { while((de = dictNext(di)) != NULL) {
sentinelRedisInstance *slave = dictGetVal(de); sentinelRedisInstance *slave = dictGetVal(de);
mstime_t info_validity_time; mstime_t info_validity_time;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment