/* Remove usual Redis commands from the command table, then just add * the SENTINEL command. */ dictEmpty(server.commands,NULL); for (j = 0; j < sizeof(sentinelcmds)/sizeof(sentinelcmds[0]); j++) { int retval; structredisCommand *cmd = sentinelcmds+j;
voidsentinelIsRunning(void){ int j; // 配置和权限校验 if (server.configfile == NULL) { serverLog(LL_WARNING, "Sentinel started without a config file. Exiting..."); exit(1); } elseif (access(server.configfile,W_OK) == -1) { serverLog(LL_WARNING, "Sentinel config file %s is not writable: %s. Exiting...", server.configfile,strerror(errno)); exit(1); } /* If this Sentinel has yet no ID set in the configuration file, we * pick a random one and persist the config on disk. From now on this * will be this Sentinel ID across restarts. */ // 如果自身的myid未设置则生成一个随机的myid for (j = 0; j < CONFIG_RUN_ID_SIZE; j++) if (sentinel.myid[j] != 0) break;
if (j == CONFIG_RUN_ID_SIZE) { /* Pick ID and persist the config. */ getRandomHexChars(sentinel.myid,CONFIG_RUN_ID_SIZE); sentinelFlushConfig(); }
/* Log its ID to make debugging of issues simpler. */ serverLog(LL_WARNING,"Sentinel ID is %s", sentinel.myid); // 开启监控每个配置的master节点 /* We want to generate a +monitor event for every configured master * at startup. */ sentinelGenerateInitialMonitorEvents(); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/* This function is called only at startup and is used to generate a * +monitor event for every configured master. The same events are also * generated when a master to monitor is added at runtime via the * SENTINEL MONITOR command. */ voidsentinelGenerateInitialMonitorEvents(void){ dictIterator *di; dictEntry *de;
di = dictGetIterator(sentinel.masters); while((de = dictNext(di)) != NULL) { sentinelRedisInstance *ri = dictGetVal(de); sentinelEvent(LL_WARNING,"+monitor",ri,"%@ quorum %d",ri->quorum); } dictReleaseIterator(di); }
/* We continuously change the frequency of the Redis "timer interrupt" * in order to desynchronize every Sentinel from every other. * This non-determinism avoids that Sentinels started at the same time * exactly continue to stay synchronized asking to be voted at the * same time again and again (resulting in nobody likely winning the * election because of split brain voting). */ server.hz = CONFIG_DEFAULT_HZ + rand() % CONFIG_DEFAULT_HZ; }
/* There are a number of things we need to perform against every master. */ // 生成被管理的master列表的迭代器 di = dictGetIterator(instances); while((de = dictNext(di)) != NULL) { // 迭代所有实例 sentinelRedisInstance *ri = dictGetVal(de); // xxx sentinelHandleRedisInstance(ri); if (ri->flags & SRI_MASTER) { sentinelHandleDictOfRedisInstances(ri->slaves); sentinelHandleDictOfRedisInstances(ri->sentinels); if (ri->failover_state == SENTINEL_FAILOVER_STATE_UPDATE_CONFIG) { switch_to_promoted = ri; } } } if (switch_to_promoted) sentinelFailoverSwitchToPromotedSlave(switch_to_promoted); dictReleaseIterator(di); }
/* ============== ACTING HALF ============= */ /* We don't proceed with the acting half if we are in TILT mode. * TILT happens when we find something odd with the time, like a * sudden change in the clock. */ if (sentinel.tilt) { if (mstime()-sentinel.tilt_start_time < SENTINEL_TILT_PERIOD) return; sentinel.tilt = 0; sentinelEvent(LL_WARNING,"-tilt",NULL,"#tilt mode exited"); }
/* Every kind of instance */ // 节点存活校验(主观性判断) sentinelCheckSubjectivelyDown(ri);
/* Masters and slaves */ if (ri->flags & (SRI_MASTER|SRI_SLAVE)) { /* Nothing so far. */ }
/* Check if we are in need for a reconnection of one of the * links, because we are detecting low activity. * * 1) Check if the command link seems connected, was connected not less * than SENTINEL_MIN_LINK_RECONNECT_PERIOD, but still we have a * pending ping for more than half the timeout. */ if (ri->link->cc && (mstime() - ri->link->cc_conn_time) >P SENTINEL_MIN_LINK_RECONNECT_PERIOD && ri->link->act_ping_time != 0 && /* There is a pending ping... */ /* The pending ping is delayed, and we did not receive * error replies as well. */ (mstime() - ri->link->act_ping_time) > (ri->down_after_period/2) && (mstime() - ri->link->last_pong_time) > (ri->down_after_period/2)) { instanceLinkCloseConnection(ri->link,ri->link->cc); }
/* 2) Check if the pubsub link seems connected, was connected not less * than SENTINEL_MIN_LINK_RECONNECT_PERIOD, but still we have no * activity in the Pub/Sub channel for more than * SENTINEL_PUBLISH_PERIOD * 3. */ if (ri->link->pc && (mstime() - ri->link->pc_conn_time) > SENTINEL_MIN_LINK_RECONNECT_PERIOD && (mstime() - ri->link->pc_last_activity) > (SENTINEL_PUBLISH_PERIOD*3)) { instanceLinkCloseConnection(ri->link,ri->link->pc); }
/* Update the SDOWN flag. We believe the instance is SDOWN if: * * 1) It is not replying. * 2) We believe it is a master, it reports to be a slave for enough time * to meet the down_after_period, plus enough time to get two times * INFO report from the instance. */ if (elapsed > ri->down_after_period || (ri->flags & SRI_MASTER && ri->role_reported == SRI_SLAVE && mstime() - ri->role_reported_time > (ri->down_after_period+SENTINEL_INFO_PERIOD*2))) { /* Is subjectively down */ if ((ri->flags & SRI_S_DOWN) == 0) { sentinelEvent(LL_WARNING,"+sdown",ri,"%@"); ri->s_down_since_time = mstime(); ri->flags |= SRI_S_DOWN; } } else { /* Is subjectively up */ if (ri->flags & SRI_S_DOWN) { sentinelEvent(LL_WARNING,"-sdown",ri,"%@"); ri->flags &= ~(SRI_S_DOWN|SRI_SCRIPT_KILL_SENT); } } }
voidsentinelCheckObjectivelyDown(sentinelRedisInstance *master){ dictIterator *di; dictEntry *de; unsignedint quorum = 0, odown = 0; // master被标记为主观下线状态 if (master->flags & SRI_S_DOWN) { /* Is down for enough sentinels? */ // 被多少个sentinel主观下线 quorum = 1; /* the current sentinel. */ /* Count all the other sentinels. */ di = dictGetIterator(master->sentinels); // 遍历所有sentinel实例,判断是否认为该master主观下线 while((de = dictNext(di)) != NULL) { sentinelRedisInstance *ri = dictGetVal(de);
intsentinelStartFailoverIfNeeded(sentinelRedisInstance *master){ /* We can't failover if the master is not in O_DOWN state. */ if (!(master->flags & SRI_O_DOWN)) return0;
/* Failover already in progress? */ if (master->flags & SRI_FAILOVER_IN_PROGRESS) return0;
/* Last failover attempt started too little time ago? */ if (mstime() - master->failover_start_time < master->failover_timeout*2) { if (master->failover_delay_logged != master->failover_start_time) { time_t clock = (master->failover_start_time + master->failover_timeout*2) / 1000; char ctimebuf[26];
ctime_r(&clock,ctimebuf); ctimebuf[24] = '\0'; /* Remove newline. */ master->failover_delay_logged = master->failover_start_time; serverLog(LL_WARNING, "Next failover delay: I will not start a failover before %s", ctimebuf); } return0; }
SENTINEL is-master-down-by-addr ip port current_epoch runid:ip:主观下线的服务id,port:主观下线的服务端口,current_epoch:sentinel的纪元,runid:*表示检测服务下线状态,如果是sentinel 运行id,表示用来选举领头sentinel
/* If the master state from other sentinel is too old, we clear it. */ // 最后一次查询时间超过 SENTINEL_ASK_PERIOD*5,则清除该sentinel的leader属性 if (elapsed > SENTINEL_ASK_PERIOD*5) { ri->flags &= ~SRI_MASTER_DOWN; sdsfree(ri->leader); ri->leader = NULL; }
/* Only ask if master is down to other sentinels if: * * 1) We believe it is down, or there is a failover in progress. * 2) Sentinel is connected. * 3) We did not receive the info within SENTINEL_ASK_PERIOD ms. */ if ((master->flags & SRI_S_DOWN) == 0) continue; if (ri->link->disconnected) continue; // master有响应,且最后一次响应时间要在SENTINEL_ASK_PERIOD内 if (!(flags & SENTINEL_ASK_FORCED) && mstime() - ri->last_master_down_reply_time < SENTINEL_ASK_PERIOD) continue;1
if (!reply || !link) return; link->pending_commands--; r = reply;
/* Ignore every error or unexpected reply. * Note that if the command returns an error for any reason we'll * end clearing the SRI_MASTER_DOWN flag for timeout anyway. */ if (r->type == REDIS_REPLY_ARRAY && r->elements == 3 && r->element[0]->type == REDIS_REPLY_INTEGER && r->element[1]->type == REDIS_REPLY_STRING && r->element[2]->type == REDIS_REPLY_INTEGER) { ri->last_master_down_reply_time = mstime(); if (r->element[0]->integer == 1) { ri->flags |= SRI_MASTER_DOWN; } else { ri->flags &= ~SRI_MASTER_DOWN; } if (strcmp(r->element[1]->str,"*")) { /* If the runid in the reply is not "*" the Sentinel actually * replied with a vote. */ sdsfree(ri->leader); if ((longlong)ri->leader_epoch != r->element[2]->integer) serverLog(LL_WARNING, "%s voted for %s %llu", ri->name, r->element[1]->str, (unsignedlonglong) r->element[2]->integer); ri->leader = sdsnew(r->element[1]->str); ri->leader_epoch = r->element[2]->integer; } } }
serverAssert(master->flags & (SRI_O_DOWN|SRI_FAILOVER_IN_PROGRESS)); counters = dictCreate(&leaderVotesDictType,NULL); // 监听master的总sentinel数 voters = dictSize(master->sentinels)+1; /* All the other sentinels and me.*/
/* Check what's the winner. For the winner to win, it needs two conditions: * 1) Absolute majority between voters (50% + 1). * 2) And anyway at least master->quorum votes. */ di = dictGetIterator(counters); // 计算最大票数 while((de = dictNext(di)) != NULL) { uint64_t votes = dictGetUnsignedIntegerVal(de);
/* Count this Sentinel vote: * if this Sentinel did not voted yet, either vote for the most * common voted sentinel, or for itself if no vote exists at all. */ // 如果有最大的票则投最大票对应的节点,不然就投自己 if (winner) myvote = sentinelVoteLeader(master,epoch,winner,&leader_epoch); else myvote = sentinelVoteLeader(master,epoch,sentinel.myid,&leader_epoch); // 加上自己的投票再次计算投票数 if (myvote && leader_epoch == epoch) { uint64_t votes = sentinelLeaderIncr(counters,myvote);
if (slave->flags & (SRI_S_DOWN|SRI_O_DOWN)) continue; if (slave->link->disconnected) continue; if (mstime() - slave->link->last_avail_time > SENTINEL_PING_PERIOD*5) continue; if (slave->slave_priority == 0) continue;
/* If the master is in SDOWN state we get INFO for slaves every second. * Otherwise we get it with the usual period so we need to account for * a larger delay. */ if (master->flags & SRI_S_DOWN) info_validity_time = SENTINEL_PING_PERIOD*5; else info_validity_time = SENTINEL_INFO_PERIOD*3; if (mstime() - slave->info_refresh > info_validity_time) continue; if (slave->master_link_down_time > max_master_down_time) continue; instance[instances++] = slave; } dictReleaseIterator(di); if (instances) { qsort(instance,instances,sizeof(sentinelRedisInstance*), compareSlavesForPromotion); selected = instance[0]; } zfree(instance); return selected; }
if ((*sa)->slave_priority != (*sb)->slave_priority) return (*sa)->slave_priority - (*sb)->slave_priority;
/* If priority is the same, select the slave with greater replication * offset (processed more data from the master). */ if ((*sa)->slave_repl_offset > (*sb)->slave_repl_offset) { return-1; /* a < b */ } elseif ((*sa)->slave_repl_offset < (*sb)->slave_repl_offset) { return1; /* a > b */ }
/* If the replication offset is the same select the slave with that has * the lexicographically smaller runid. Note that we try to handle runid * == NULL as there are old Redis versions that don't publish runid in * INFO. A NULL runid is considered bigger than any other runid. */ sa_runid = (*sa)->runid; sb_runid = (*sb)->runid; if (sa_runid == NULL && sb_runid == NULL) return0; elseif (sa_runid == NULL) return1; /* a > b */ elseif (sb_runid == NULL) return-1; /* a < b */ return strcasecmp(sa_runid, sb_runid); }
voidsentinelFailoverWaitStart(sentinelRedisInstance *ri){ char *leader; int isleader;
/* Check if we are the leader for the failover epoch. */ // 选举sentinel主节点 leader = sentinelGetLeader(ri, ri->failover_epoch);、 // 判断是不是自己是leader isleader = leader && strcasecmp(leader,sentinel.myid) == 0; sdsfree(leader);
/* If I'm not the leader, and it is not a forced failover via * SENTINEL FAILOVER, then I can't continue with the failover. */ // 当前节点不是leader,且节点标记不为SRI_FORCE_FAILOVER if (!isleader && !(ri->flags & SRI_FORCE_FAILOVER)) { int election_timeout = SENTINEL_ELECTION_TIMEOUT;
/* The election timeout is the MIN between SENTINEL_ELECTION_TIMEOUT * and the configured failover timeout. */ // 选举超时时间上限 if (election_timeout > ri->failover_timeout) election_timeout = ri->failover_timeout; /* Abort the failover if I'm not the leader after some time. */ // 如果选举超时,则中断故障恢复 if (mstime() - ri->failover_start_time > election_timeout) { sentinelEvent(LL_WARNING,"-failover-abort-not-elected",ri,"%@"); sentinelAbortFailover(ri); } return; } // 发送选举的结果 sentinelEvent(LL_WARNING,"+elected-leader",ri,"%@"); // 异常,程序退出exit(99) if (sentinel.simfailure_flags & SENTINEL_SIMFAILURE_CRASH_AFTER_ELECTION) sentinelSimFailureCrash(); // 故障恢复状态改为SENTINEL_FAILOVER_STATE_SELECT_SLAVE ri->failover_state = SENTINEL_FAILOVER_STATE_SELECT_SLAVE; ri->failover_state_change_time = mstime(); // 发送选取从节点的事件 sentinelEvent(LL_WARNING,"+failover-state-select-slave",ri,"%@"); }
/* We don't handle the timeout in this state as the function aborts * the failover or go forward in the next state. */ if (slave == NULL) { sentinelEvent(LL_WARNING,"-failover-abort-no-good-slave",ri,"%@"); sentinelAbortFailover(ri); } else { sentinelEvent(LL_WARNING,"+selected-slave",slave,"%@"); slave->flags |= SRI_PROMOTED; ri->promoted_slave = slave; ri->failover_state = SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE; ri->failover_state_change_time = mstime(); sentinelEvent(LL_NOTICE,"+failover-state-send-slaveof-noone", slave, "%@"); } }
voidsentinelFailoverSendSlaveOfNoOne(sentinelRedisInstance *ri){ int retval;
/* We can't send the command to the promoted slave if it is now * disconnected. Retry again and again with this state until the timeout * is reached, then abort the failover. */ if (ri->promoted_slave->link->disconnected) { if (mstime() - ri->failover_state_change_time > ri->failover_timeout) { sentinelEvent(LL_WARNING,"-failover-abort-slave-timeout",ri,"%@"); sentinelAbortFailover(ri); } return; }
/* Send SLAVEOF NO ONE command to turn the slave into a master. * We actually register a generic callback for this command as we don't * really care about the reply. We check if it worked indirectly observing * if INFO returns a different role (master instead of slave). */ retval = sentinelSendSlaveOf(ri->promoted_slave,NULL,0); if (retval != C_OK) return; sentinelEvent(LL_NOTICE, "+failover-state-wait-promotion", ri->promoted_slave,"%@"); ri->failover_state = SENTINEL_FAILOVER_STATE_WAIT_PROMOTION; ri->failover_state_change_time = mstime(); }
sentinelSendSlaveOf
故障恢复时向选中slave节点发送SLAVEOF NO ONE使从节点结束主从复制变为主节点。
SLAVEOF host port 命令,可以将当前服务器转变为指定服务器的从属服务器(slave server)。 SLAVEOF NO ONE 将使得这个从属服务器关闭复制功能,并从从属服务器转变回主服务器
SLAVEOF NO ONE: sentinelSendSlaveOf(ri->promoted_slave,NULL,0);
intsentinelSendSlaveOf(sentinelRedisInstance *ri, char *host, int port){ char portstr[32]; int retval;
ll2string(portstr,sizeof(portstr),port);
/* If host is NULL we send SLAVEOF NO ONE that will turn the instance * into a master. */ // 没有指定master就代表将该节点结束主从复制,变成master节点 if (host == NULL) { host = "NO"; memcpy(portstr,"ONE",4); }
/* In order to send SLAVEOF in a safe way, we send a transaction performing * the following tasks: * 1) Reconfigure the instance according to the specified host/port params. * 2) Rewrite the configuration. * 3) Disconnect all clients (but this one sending the commnad) in order * to trigger the ask-master-on-reconnection protocol for connected * clients. * * Note that we don't check the replies returned by commands, since we * will observe instead the effects in the next INFO output. */ //MULTI命令 标记一个事务块的开始。 retval = redisAsyncCommand(ri->link->cc, sentinelDiscardReplyCallback, ri, "%s", sentinelInstanceMapCommand(ri,"MULTI")); if (retval == C_ERR) return retval; ri->link->pending_commands++;
/* CLIENT KILL TYPE <type> is only supported starting from Redis 2.8.12, * however sending it to an instance not understanding this command is not * an issue because CLIENT is variadic command, so Redis will not * recognized as a syntax error, and the transaction will not fail (but * only the unsupported command will fail). */ retval = redisAsyncCommand(ri->link->cc, sentinelDiscardReplyCallback, ri, "%s KILL TYPE normal", sentinelInstanceMapCommand(ri,"CLIENT")); if (retval == C_ERR) return retval; ri->link->pending_commands++;
/* We actually wait for promotion indirectly checking with INFO when the * slave turns into a master. */ voidsentinelFailoverWaitPromotion(sentinelRedisInstance *ri){ /* Just handle the timeout. Switching to the next state is handled * by the function parsing the INFO command of the promoted slave. */ if (mstime() - ri->failover_state_change_time > ri->failover_timeout) { sentinelEvent(LL_WARNING,"-failover-abort-slave-timeout",ri,"%@"); sentinelAbortFailover(ri); } }
/* Process the INFO output from masters. */ voidsentinelRefreshInstanceInfo(sentinelRedisInstance *ri, constchar *info){ sds *lines; int numlines, j; int role = 0;
/* cache full INFO output for instance */ sdsfree(ri->info); ri->info = sdsnew(info);
/* The following fields must be reset to a given value in the case they * are not found at all in the INFO output. */ ri->master_link_down_time = 0;
/* Process line by line. */ // 解析返回信息 lines = sdssplitlen(info,strlen(info),"\r\n",2,&numlines); for (j = 0; j < numlines; j++) { // ... 省略解析INFO返回的内容 } // 记录INFO信息刷新的时间 ri->info_refresh = mstime(); sdsfreesplitres(lines,numlines);
/* ---------------------------- Acting half ----------------------------- * Some things will not happen if sentinel.tilt is true, but some will * still be processed. */
/* Remember when the role changed. */ // 记录角色(主从)变更 if (role != ri->role_reported) { ri->role_reported_time = mstime(); ri->role_reported = role; if (role == SRI_SLAVE) ri->slave_conf_change_time = mstime(); /* Log the event with +role-change if the new role is coherent or * with -role-change if there is a mismatch with the current config. */ sentinelEvent(LL_VERBOSE, ((ri->flags & (SRI_MASTER|SRI_SLAVE)) == role) ? "+role-change" : "-role-change", ri, "%@ new reported role is %s", role == SRI_MASTER ? "master" : "slave", ri->flags & SRI_MASTER ? "master" : "slave"); }
/* None of the following conditions are processed when in tilt mode, so * return asap. */ // sentinel处于tilt模式则结束 if (sentinel.tilt) return;
/* Handle master -> slave role switch. */ // master变成slave了, 这里不处理 if ((ri->flags & SRI_MASTER) && role == SRI_SLAVE) { /* Nothing to do, but masters claiming to be slaves are * considered to be unreachable by Sentinel, so eventually * a failover will be triggered. */ }
/* Handle slave -> master role switch. */ // slave节点变成master了 if ((ri->flags & SRI_SLAVE) && role == SRI_MASTER) { /* If this is a promoted slave we can change state to the * failover state machine. */ // 是否处于slave升为主节点的状态 if ((ri->flags & SRI_PROMOTED) && (ri->master->flags & SRI_FAILOVER_IN_PROGRESS) && (ri->master->failover_state == SENTINEL_FAILOVER_STATE_WAIT_PROMOTION)) { /* Now that we are sure the slave was reconfigured as a master * set the master configuration epoch to the epoch we won the * election to perform this failover. This will force the other * Sentinels to update their config (assuming there is not * a newer one already available). */ // 更新config_epoch 和状态 ri->master->config_epoch = ri->master->failover_epoch; ri->master->failover_state = SENTINEL_FAILOVER_STATE_RECONF_SLAVES; ri->master->failover_state_change_time = mstime(); sentinelFlushConfig(); sentinelEvent(LL_WARNING,"+promoted-slave",ri,"%@"); // 如果是slave升级为master的过程中失败了 if (sentinel.simfailure_flags & SENTINEL_SIMFAILURE_CRASH_AFTER_PROMOTION) sentinelSimFailureCrash(); sentinelEvent(LL_WARNING,"+failover-state-reconf-slaves", ri->master,"%@"); sentinelCallClientReconfScript(ri->master,SENTINEL_LEADER, "start",ri->master->addr,ri->addr); // 修改last_pub_time时间,强制进行"Hello" sentinelForceHelloUpdateForMaster(ri->master); } else { /* A slave turned into a master. We want to force our view and * reconfigure as slave. Wait some time after the change before * going forward, to receive new configs if any. */ mstime_t wait_time = SENTINEL_PUBLISH_PERIOD*4; // 发送SlaveOf命令将变为slave if (!(ri->flags & SRI_PROMOTED) && sentinelMasterLooksSane(ri->master) && sentinelRedisInstanceNoDownFor(ri,wait_time) && mstime() - ri->role_reported_time > wait_time) { int retval = sentinelSendSlaveOf(ri, ri->master->addr->ip, ri->master->addr->port); if (retval == C_OK) sentinelEvent(LL_NOTICE,"+convert-to-slave",ri,"%@"); } } }
/* Handle slaves replicating to a different master address. */ // 从节点同步的主节点变更,通知从节点同步信息 if ((ri->flags & SRI_SLAVE) && role == SRI_SLAVE && (ri->slave_master_port != ri->master->addr->port || strcasecmp(ri->slave_master_host,ri->master->addr->ip))) { mstime_t wait_time = ri->master->failover_timeout;
/* Make sure the master is sane before reconfiguring this instance * into a slave. */ if (sentinelMasterLooksSane(ri->master) && sentinelRedisInstanceNoDownFor(ri,wait_time) && mstime() - ri->slave_conf_change_time > wait_time) { int retval = sentinelSendSlaveOf(ri, ri->master->addr->ip, ri->master->addr->port); if (retval == C_OK) sentinelEvent(LL_NOTICE,"+fix-slave-config",ri,"%@"); } }
/* Detect if the slave that is in the process of being reconfigured * changed state. */ // if ((ri->flags & SRI_SLAVE) && role == SRI_SLAVE && (ri->flags & (SRI_RECONF_SENT|SRI_RECONF_INPROG))) { /* SRI_RECONF_SENT -> SRI_RECONF_INPROG. */ // 发送slave-reconf-inprog,执行slaveof + SYNC过程,如过slave收到“+slave-reconf-sent”之后将会执行slaveof操作。 if ((ri->flags & SRI_RECONF_SENT) && ri->slave_master_host && strcmp(ri->slave_master_host, ri->master->promoted_slave->addr->ip) == 0 && ri->slave_master_port == ri->master->promoted_slave->addr->port) { ri->flags &= ~SRI_RECONF_SENT; ri->flags |= SRI_RECONF_INPROG; sentinelEvent(LL_NOTICE,"+slave-reconf-inprog",ri,"%@"); }
/* Send SLAVE OF <new master address> to all the remaining slaves that * still don't appear to have the configuration updated. */ voidsentinelFailoverReconfNextSlave(sentinelRedisInstance *master){ dictIterator *di; dictEntry *de; int in_progress = 0;
if (slave->flags & (SRI_RECONF_SENT|SRI_RECONF_INPROG)) in_progress++; } dictReleaseIterator(di);
di = dictGetIterator(master->slaves); while(in_progress < master->parallel_syncs && (de = dictNext(di)) != NULL) { sentinelRedisInstance *slave = dictGetVal(de); int retval;
/* Skip the promoted slave, and already configured slaves. */ if (slave->flags & (SRI_PROMOTED|SRI_RECONF_DONE)) continue;
/* If too much time elapsed without the slave moving forward to * the next state, consider it reconfigured even if it is not. * Sentinels will detect the slave as misconfigured and fix its * configuration later. */ if ((slave->flags & SRI_RECONF_SENT) && (mstime() - slave->slave_reconf_sent_time) > SENTINEL_SLAVE_RECONF_TIMEOUT) { sentinelEvent(LL_NOTICE,"-slave-reconf-sent-timeout",slave,"%@"); slave->flags &= ~SRI_RECONF_SENT; slave->flags |= SRI_RECONF_DONE; }
/* Nothing to do for instances that are disconnected or already * in RECONF_SENT state. */ if (slave->flags & (SRI_RECONF_SENT|SRI_RECONF_INPROG)) continue; if (slave->link->disconnected) continue;
/* We can't consider failover finished if the promoted slave is * not reachable. */ if (master->promoted_slave == NULL || master->promoted_slave->flags & SRI_S_DOWN) return;
/* The failover terminates once all the reachable slaves are properly * configured. */ di = dictGetIterator(master->slaves); while((de = dictNext(di)) != NULL) { sentinelRedisInstance *slave = dictGetVal(de);
if (slave->flags & (SRI_PROMOTED|SRI_RECONF_DONE)) continue; if (slave->flags & SRI_S_DOWN) continue; not_reconfigured++; } dictReleaseIterator(di);
/* Force end of failover on timeout. */ if (elapsed > master->failover_timeout) { not_reconfigured = 0; timeout = 1; sentinelEvent(LL_WARNING,"+failover-end-for-timeout",master,"%@"); }
/* If I'm the leader it is a good idea to send a best effort SLAVEOF * command to all the slaves still not reconfigured to replicate with * the new master. */ if (timeout) { dictIterator *di; dictEntry *de;
di = dictGetIterator(master->slaves); while((de = dictNext(di)) != NULL) { sentinelRedisInstance *slave = dictGetVal(de); int retval;
if (slave->flags & (SRI_RECONF_DONE|SRI_RECONF_SENT)) continue; if (slave->link->disconnected) continue;