1 /* Redis Sentinel implementation 2 * 3 * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * * Redistributions of source code must retain the above copyright notice, 10 * this list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * * Neither the name of Redis nor the names of its contributors may be used 15 * to endorse or promote products derived from this software without 16 * specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "redis.h" 32 #include "hiredis.h" 33 #include "async.h" 34 35 #include <ctype.h> 36 #include <arpa/inet.h> 37 #include <sys/socket.h> 38 #include <sys/wait.h> 39 #include <fcntl.h> 40 41 extern char **environ; 42 43 #define REDIS_SENTINEL_PORT 26379 44 45 /* ======================== Sentinel global state =========================== */ 46 47 /* Address object, used to describe an ip:port pair. */ 48 typedef struct sentinelAddr { 49 char *ip; 50 int port; 51 } sentinelAddr; 52 53 /* A Sentinel Redis Instance object is monitoring. */ 54 #define SRI_MASTER (1<<0) 55 #define SRI_SLAVE (1<<1) 56 #define SRI_SENTINEL (1<<2) 57 #define SRI_DISCONNECTED (1<<3) 58 #define SRI_S_DOWN (1<<4) /* Subjectively down (no quorum). */ 59 #define SRI_O_DOWN (1<<5) /* Objectively down (confirmed by others). */ 60 #define SRI_MASTER_DOWN (1<<6) /* A Sentinel with this flag set thinks that 61 its master is down. */ 62 #define SRI_FAILOVER_IN_PROGRESS (1<<7) /* Failover is in progress for 63 this master. */ 64 #define SRI_PROMOTED (1<<8) /* Slave selected for promotion. */ 65 #define SRI_RECONF_SENT (1<<9) /* SLAVEOF <newmaster> sent. */ 66 #define SRI_RECONF_INPROG (1<<10) /* Slave synchronization in progress. */ 67 #define SRI_RECONF_DONE (1<<11) /* Slave synchronized with new master. */ 68 #define SRI_FORCE_FAILOVER (1<<12) /* Force failover with master up. */ 69 #define SRI_SCRIPT_KILL_SENT (1<<13) /* SCRIPT KILL already sent on -BUSY */ 70 71 /* Note: times are in milliseconds. */ 72 #define SENTINEL_INFO_PERIOD 10000 73 #define SENTINEL_PING_PERIOD 1000 74 #define SENTINEL_ASK_PERIOD 1000 75 #define SENTINEL_PUBLISH_PERIOD 2000 76 #define SENTINEL_DEFAULT_DOWN_AFTER 30000 77 #define SENTINEL_HELLO_CHANNEL "__sentinel__:hello" 78 #define SENTINEL_TILT_TRIGGER 2000 79 #define SENTINEL_TILT_PERIOD (SENTINEL_PING_PERIOD*30) 80 #define SENTINEL_DEFAULT_SLAVE_PRIORITY 100 81 #define SENTINEL_SLAVE_RECONF_TIMEOUT 10000 82 #define SENTINEL_DEFAULT_PARALLEL_SYNCS 1 83 #define SENTINEL_MIN_LINK_RECONNECT_PERIOD 15000 84 #define SENTINEL_DEFAULT_FAILOVER_TIMEOUT (60*3*1000) 85 #define SENTINEL_MAX_PENDING_COMMANDS 100 86 #define SENTINEL_ELECTION_TIMEOUT 10000 87 #define SENTINEL_MAX_DESYNC 1000 88 89 /* Failover machine different states. */ 90 #define SENTINEL_FAILOVER_STATE_NONE 0 /* No failover in progress. */ 91 #define SENTINEL_FAILOVER_STATE_WAIT_START 1 /* Wait for failover_start_time*/ 92 #define SENTINEL_FAILOVER_STATE_SELECT_SLAVE 2 /* Select slave to promote */ 93 #define SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE 3 /* Slave -> Master */ 94 #define SENTINEL_FAILOVER_STATE_WAIT_PROMOTION 4 /* Wait slave to change role */ 95 #define SENTINEL_FAILOVER_STATE_RECONF_SLAVES 5 /* SLAVEOF newmaster */ 96 #define SENTINEL_FAILOVER_STATE_UPDATE_CONFIG 6 /* Monitor promoted slave. */ 97 98 #define SENTINEL_MASTER_LINK_STATUS_UP 0 99 #define SENTINEL_MASTER_LINK_STATUS_DOWN 1 100 101 /* Generic flags that can be used with different functions. 102 * They use higher bits to avoid colliding with the function specific 103 * flags. */ 104 #define SENTINEL_NO_FLAGS 0 105 #define SENTINEL_GENERATE_EVENT (1<<16) 106 #define SENTINEL_LEADER (1<<17) 107 #define SENTINEL_OBSERVER (1<<18) 108 109 /* Script execution flags and limits. */ 110 #define SENTINEL_SCRIPT_NONE 0 111 #define SENTINEL_SCRIPT_RUNNING 1 112 #define SENTINEL_SCRIPT_MAX_QUEUE 256 113 #define SENTINEL_SCRIPT_MAX_RUNNING 16 114 #define SENTINEL_SCRIPT_MAX_RUNTIME 60000 /* 60 seconds max exec time. */ 115 #define SENTINEL_SCRIPT_MAX_RETRY 10 116 #define SENTINEL_SCRIPT_RETRY_DELAY 30000 /* 30 seconds between retries. */ 117 118 typedef struct sentinelRedisInstance { 119 int flags; /* See SRI_... defines */ 120 char *name; /* Master name from the point of view of this sentinel. */ 121 char *runid; /* run ID of this instance. */ 122 uint64_t config_epoch; /* Configuration epoch. */ 123 sentinelAddr *addr; /* Master host. */ 124 redisAsyncContext *cc; /* Hiredis context for commands. */ 125 redisAsyncContext *pc; /* Hiredis context for Pub / Sub. */ 126 int pending_commands; /* Number of commands sent waiting for a reply. */ 127 mstime_t cc_conn_time; /* cc connection time. */ 128 mstime_t pc_conn_time; /* pc connection time. */ 129 mstime_t pc_last_activity; /* Last time we received any message. */ 130 mstime_t last_avail_time; /* Last time the instance replied to ping with 131 a reply we consider valid. */ 132 mstime_t last_ping_time; /* Last time a pending ping was sent in the 133 context of the current command connection 134 with the instance. 0 if still not sent or 135 if pong already received. */ 136 mstime_t last_pong_time; /* Last time the instance replied to ping, 137 whatever the reply was. That's used to check 138 if the link is idle and must be reconnected. */ 139 mstime_t last_pub_time; /* Last time we sent hello via Pub/Sub. */ 140 mstime_t last_hello_time; /* Only used if SRI_SENTINEL is set. Last time 141 we received a hello from this Sentinel 142 via Pub/Sub. */ 143 mstime_t last_master_down_reply_time; /* Time of last reply to 144 SENTINEL is-master-down command. */ 145 mstime_t s_down_since_time; /* Subjectively down since time. */ 146 mstime_t o_down_since_time; /* Objectively down since time. */ 147 mstime_t down_after_period; /* Consider it down after that period. */ 148 mstime_t info_refresh; /* Time at which we received INFO output from it. */ 149 150 /* Role and the first time we observed it. 151 * This is useful in order to delay replacing what the instance reports 152 * with our own configuration. We need to always wait some time in order 153 * to give a chance to the leader to report the new configuration before 154 * we do silly things. */ 155 int role_reported; 156 mstime_t role_reported_time; 157 mstime_t slave_conf_change_time; /* Last time slave master addr changed. */ 158 159 /* Master specific. */ 160 dict *sentinels; /* Other sentinels monitoring the same master. */ 161 dict *slaves; /* Slaves for this master instance. */ 162 int quorum; /* Number of sentinels that need to agree on failure. */ 163 int parallel_syncs; /* How many slaves to reconfigure at same time. */ 164 char *auth_pass; /* Password to use for AUTH against master & slaves. */ 165 166 /* Slave specific. */ 167 mstime_t master_link_down_time; /* Slave replication link down time. */ 168 int slave_priority; /* Slave priority according to its INFO output. */ 169 mstime_t slave_reconf_sent_time; /* Time at which we sent SLAVE OF <new> */ 170 struct sentinelRedisInstance *master; /* Master instance if it's slave. */ 171 char *slave_master_host; /* Master host as reported by INFO */ 172 int slave_master_port; /* Master port as reported by INFO */ 173 int slave_master_link_status; /* Master link status as reported by INFO */ 174 unsigned long long slave_repl_offset; /* Slave replication offset. */ 175 /* Failover */ 176 char *leader; /* If this is a master instance, this is the runid of 177 the Sentinel that should perform the failover. If 178 this is a Sentinel, this is the runid of the Sentinel 179 that this Sentinel voted as leader. */ 180 uint64_t leader_epoch; /* Epoch of the 'leader' field. */ 181 uint64_t failover_epoch; /* Epoch of the currently started failover. */ 182 int failover_state; /* See SENTINEL_FAILOVER_STATE_* defines. */ 183 mstime_t failover_state_change_time; 184 mstime_t failover_start_time; /* Last failover attempt start time. */ 185 mstime_t failover_timeout; /* Max time to refresh failover state. */ 186 mstime_t failover_delay_logged; /* For what failover_start_time value we 187 logged the failover delay. */ 188 struct sentinelRedisInstance *promoted_slave; /* Promoted slave instance. */ 189 /* Scripts executed to notify admin or reconfigure clients: when they 190 * are set to NULL no script is executed. */ 191 char *notification_script; 192 char *client_reconfig_script; 193 } sentinelRedisInstance; 194 195 /* Main state. */ 196 struct sentinelState { 197 uint64_t current_epoch; /* Current epoch. */ 198 dict *masters; /* Dictionary of master sentinelRedisInstances. 199 Key is the instance name, value is the 200 sentinelRedisInstance structure pointer. */ 201 int tilt; /* Are we in TILT mode? */ 202 int running_scripts; /* Number of scripts in execution right now. */ 203 mstime_t tilt_start_time; /* When TITL started. */ 204 mstime_t previous_time; /* Last time we ran the time handler. */ 205 list *scripts_queue; /* Queue of user scripts to execute. */ 206 } sentinel; 207 208 /* A script execution job. */ 209 typedef struct sentinelScriptJob { 210 int flags; /* Script job flags: SENTINEL_SCRIPT_* */ 211 int retry_num; /* Number of times we tried to execute it. */ 212 char **argv; /* Arguments to call the script. */ 213 mstime_t start_time; /* Script execution time if the script is running, 214 otherwise 0 if we are allowed to retry the 215 execution at any time. If the script is not 216 running and it's not 0, it means: do not run 217 before the specified time. */ 218 pid_t pid; /* Script execution pid. */ 219 } sentinelScriptJob; 220 221 /* ======================= hiredis ae.c adapters ============================= 222 * Note: this implementation is taken from hiredis/adapters/ae.h, however 223 * we have our modified copy for Sentinel in order to use our allocator 224 * and to have full control over how the adapter works. */ 225 226 typedef struct redisAeEvents { 227 redisAsyncContext *context; 228 aeEventLoop *loop; 229 int fd; 230 int reading, writing; 231 } redisAeEvents; 232 233 static void redisAeReadEvent(aeEventLoop *el, int fd, void *privdata, int mask) { 234 ((void)el); ((void)fd); ((void)mask); 235 236 redisAeEvents *e = (redisAeEvents*)privdata; 237 redisAsyncHandleRead(e->context); 238 } 239 240 static void redisAeWriteEvent(aeEventLoop *el, int fd, void *privdata, int mask) { 241 ((void)el); ((void)fd); ((void)mask); 242 243 redisAeEvents *e = (redisAeEvents*)privdata; 244 redisAsyncHandleWrite(e->context); 245 } 246 247 static void redisAeAddRead(void *privdata) { 248 redisAeEvents *e = (redisAeEvents*)privdata; 249 aeEventLoop *loop = e->loop; 250 if (!e->reading) { 251 e->reading = 1; 252 aeCreateFileEvent(loop,e->fd,AE_READABLE,redisAeReadEvent,e); 253 } 254 } 255 256 static void redisAeDelRead(void *privdata) { 257 redisAeEvents *e = (redisAeEvents*)privdata; 258 aeEventLoop *loop = e->loop; 259 if (e->reading) { 260 e->reading = 0; 261 aeDeleteFileEvent(loop,e->fd,AE_READABLE); 262 } 263 } 264 265 static void redisAeAddWrite(void *privdata) { 266 redisAeEvents *e = (redisAeEvents*)privdata; 267 aeEventLoop *loop = e->loop; 268 if (!e->writing) { 269 e->writing = 1; 270 aeCreateFileEvent(loop,e->fd,AE_WRITABLE,redisAeWriteEvent,e); 271 } 272 } 273 274 static void redisAeDelWrite(void *privdata) { 275 redisAeEvents *e = (redisAeEvents*)privdata; 276 aeEventLoop *loop = e->loop; 277 if (e->writing) { 278 e->writing = 0; 279 aeDeleteFileEvent(loop,e->fd,AE_WRITABLE); 280 } 281 } 282 283 static void redisAeCleanup(void *privdata) { 284 redisAeEvents *e = (redisAeEvents*)privdata; 285 redisAeDelRead(privdata); 286 redisAeDelWrite(privdata); 287 zfree(e); 288 } 289 290 static int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) { 291 redisContext *c = &(ac->c); 292 redisAeEvents *e; 293 294 /* Nothing should be attached when something is already attached */ 295 if (ac->ev.data != NULL) 296 return REDIS_ERR; 297 298 /* Create container for context and r/w events */ 299 e = (redisAeEvents*)zmalloc(sizeof(*e)); 300 e->context = ac; 301 e->loop = loop; 302 e->fd = c->fd; 303 e->reading = e->writing = 0; 304 305 /* Register functions to start/stop listening for events */ 306 ac->ev.addRead = redisAeAddRead; 307 ac->ev.delRead = redisAeDelRead; 308 ac->ev.addWrite = redisAeAddWrite; 309 ac->ev.delWrite = redisAeDelWrite; 310 ac->ev.cleanup = redisAeCleanup; 311 ac->ev.data = e; 312 313 return REDIS_OK; 314 } 315 316 /* ============================= Prototypes ================================= */ 317 318 void sentinelLinkEstablishedCallback(const redisAsyncContext *c, int status); 319 void sentinelDisconnectCallback(const redisAsyncContext *c, int status); 320 void sentinelReceiveHelloMessages(redisAsyncContext *c, void *reply, void *privdata); 321 sentinelRedisInstance *sentinelGetMasterByName(char *name); 322 char *sentinelGetSubjectiveLeader(sentinelRedisInstance *master); 323 char *sentinelGetObjectiveLeader(sentinelRedisInstance *master); 324 int yesnotoi(char *s); 325 void sentinelDisconnectInstanceFromContext(const redisAsyncContext *c); 326 void sentinelKillLink(sentinelRedisInstance *ri, redisAsyncContext *c); 327 const char *sentinelRedisInstanceTypeStr(sentinelRedisInstance *ri); 328 void sentinelAbortFailover(sentinelRedisInstance *ri); 329 void sentinelEvent(int level, char *type, sentinelRedisInstance *ri, const char *fmt, ...); 330 sentinelRedisInstance *sentinelSelectSlave(sentinelRedisInstance *master); 331 void sentinelScheduleScriptExecution(char *path, ...); 332 void sentinelStartFailover(sentinelRedisInstance *master); 333 void sentinelDiscardReplyCallback(redisAsyncContext *c, void *reply, void *privdata); 334 int sentinelSendSlaveOf(sentinelRedisInstance *ri, char *host, int port); 335 char *sentinelVoteLeader(sentinelRedisInstance *master, uint64_t req_epoch, char *req_runid, uint64_t *leader_epoch); 336 void sentinelFlushConfig(void); 337 void sentinelGenerateInitialMonitorEvents(void); 338 int sentinelSendPing(sentinelRedisInstance *ri); 339 int sentinelForceHelloUpdateForMaster(sentinelRedisInstance *master); 340 341 /* ========================= Dictionary types =============================== */ 342 343 unsigned int dictSdsHash(const void *key); 344 int dictSdsKeyCompare(void *privdata, const void *key1, const void *key2); 345 void releaseSentinelRedisInstance(sentinelRedisInstance *ri); 346 347 void dictInstancesValDestructor (void *privdata, void *obj) { 348 releaseSentinelRedisInstance(obj); 349 } 350 351 /* Instance name (sds) -> instance (sentinelRedisInstance pointer) 352 * 353 * also used for: sentinelRedisInstance->sentinels dictionary that maps 354 * sentinels ip:port to last seen time in Pub/Sub hello message. */ 355 dictType instancesDictType = { 356 dictSdsHash, /* hash function */ 357 NULL, /* key dup */ 358 NULL, /* val dup */ 359 dictSdsKeyCompare, /* key compare */ 360 NULL, /* key destructor */ 361 dictInstancesValDestructor /* val destructor */ 362 }; 363 364 /* Instance runid (sds) -> votes (long casted to void*) 365 * 366 * This is useful into sentinelGetObjectiveLeader() function in order to 367 * count the votes and understand who is the leader. */ 368 dictType leaderVotesDictType = { 369 dictSdsHash, /* hash function */ 370 NULL, /* key dup */ 371 NULL, /* val dup */ 372 dictSdsKeyCompare, /* key compare */ 373 NULL, /* key destructor */ 374 NULL /* val destructor */ 375 }; 376 377 /* =========================== Initialization =============================== */ 378 379 void sentinelCommand(redisClient *c); 380 void sentinelInfoCommand(redisClient *c); 381 void sentinelSetCommand(redisClient *c); 382 void sentinelPublishCommand(redisClient *c); 383 void sentinelRoleCommand(redisClient *c); 384 385 struct redisCommand sentinelcmds[] = { 386 {"ping",pingCommand,1,"",0,NULL,0,0,0,0,0}, 387 {"sentinel",sentinelCommand,-2,"",0,NULL,0,0,0,0,0}, 388 {"subscribe",subscribeCommand,-2,"",0,NULL,0,0,0,0,0}, 389 {"unsubscribe",unsubscribeCommand,-1,"",0,NULL,0,0,0,0,0}, 390 {"psubscribe",psubscribeCommand,-2,"",0,NULL,0,0,0,0,0}, 391 {"punsubscribe",punsubscribeCommand,-1,"",0,NULL,0,0,0,0,0}, 392 {"publish",sentinelPublishCommand,3,"",0,NULL,0,0,0,0,0}, 393 {"info",sentinelInfoCommand,-1,"",0,NULL,0,0,0,0,0}, 394 {"role",sentinelRoleCommand,1,"l",0,NULL,0,0,0,0,0}, 395 {"shutdown",shutdownCommand,-1,"",0,NULL,0,0,0,0,0} 396 }; 397 398 /* This function overwrites a few normal Redis config default with Sentinel 399 * specific defaults. */ 400 void initSentinelConfig(void) { 401 server.port = REDIS_SENTINEL_PORT; 402 } 403 404 /* Perform the Sentinel mode initialization. */ 405 void initSentinel(void) { 406 int j; 407 408 /* Remove usual Redis commands from the command table, then just add 409 * the SENTINEL command. */ 410 dictEmpty(server.commands,NULL); 411 for (j = 0; j < sizeof(sentinelcmds)/sizeof(sentinelcmds[0]); j++) { 412 int retval; 413 struct redisCommand *cmd = sentinelcmds+j; 414 415 retval = dictAdd(server.commands, sdsnew(cmd->name), cmd); 416 redisAssert(retval == DICT_OK); 417 } 418 419 /* Initialize various data structures. */ 420 sentinel.current_epoch = 0; 421 sentinel.masters = dictCreate(&instancesDictType,NULL); 422 sentinel.tilt = 0; 423 sentinel.tilt_start_time = 0; 424 sentinel.previous_time = mstime(); 425 sentinel.running_scripts = 0; 426 sentinel.scripts_queue = listCreate(); 427 } 428 429 /* This function gets called when the server is in Sentinel mode, started, 430 * loaded the configuration, and is ready for normal operations. */ 431 void sentinelIsRunning(void) { 432 redisLog(REDIS_WARNING,"Sentinel runid is %s", server.runid); 433 434 if (server.configfile == NULL) { 435 redisLog(REDIS_WARNING, 436 "Sentinel started without a config file. Exiting..."); 437 exit(1); 438 } else if (access(server.configfile,W_OK) == -1) { 439 redisLog(REDIS_WARNING, 440 "Sentinel config file %s is not writable: %s. Exiting...", 441 server.configfile,strerror(errno)); 442 exit(1); 443 } 444 445 /* We want to generate a +monitor event for every configured master 446 * at startup. */ 447 sentinelGenerateInitialMonitorEvents(); 448 } 449 450 /* ============================== sentinelAddr ============================== */ 451 452 /* Create a sentinelAddr object and return it on success. 453 * On error NULL is returned and errno is set to: 454 * ENOENT: Can't resolve the hostname. 455 * EINVAL: Invalid port number. 456 */ 457 sentinelAddr *createSentinelAddr(char *hostname, int port) { 458 char buf[32]; 459 sentinelAddr *sa; 460 461 if (port <= 0 || port > 65535) { 462 errno = EINVAL; 463 return NULL; 464 } 465 if (anetResolve(NULL,hostname,buf,sizeof(buf)) == ANET_ERR) { 466 errno = ENOENT; 467 return NULL; 468 } 469 sa = zmalloc(sizeof(*sa)); 470 sa->ip = sdsnew(buf); 471 sa->port = port; 472 return sa; 473 } 474 475 /* Return a duplicate of the source address. */ 476 sentinelAddr *dupSentinelAddr(sentinelAddr *src) { 477 sentinelAddr *sa; 478 479 sa = zmalloc(sizeof(*sa)); 480 sa->ip = sdsnew(src->ip); 481 sa->port = src->port; 482 return sa; 483 } 484 485 /* Free a Sentinel address. Can't fail. */ 486 void releaseSentinelAddr(sentinelAddr *sa) { 487 sdsfree(sa->ip); 488 zfree(sa); 489 } 490 491 /* Return non-zero if two addresses are equal. */ 492 int sentinelAddrIsEqual(sentinelAddr *a, sentinelAddr *b) { 493 return a->port == b->port && !strcasecmp(a->ip,b->ip); 494 } 495 496 /* =========================== Events notification ========================== */ 497 498 /* Send an event to log, pub/sub, user notification script. 499 * 500 * 'level' is the log level for logging. Only REDIS_WARNING events will trigger 501 * the execution of the user notification script. 502 * 503 * 'type' is the message type, also used as a pub/sub channel name. 504 * 505 * 'ri', is the redis instance target of this event if applicable, and is 506 * used to obtain the path of the notification script to execute. 507 * 508 * The remaining arguments are printf-alike. 509 * If the format specifier starts with the two characters "%@" then ri is 510 * not NULL, and the message is prefixed with an instance identifier in the 511 * following format: 512 * 513 * <instance type> <instance name> <ip> <port> 514 * 515 * If the instance type is not master, than the additional string is 516 * added to specify the originating master: 517 * 518 * @ <master name> <master ip> <master port> 519 * 520 * Any other specifier after "%@" is processed by printf itself. 521 */ 522 void sentinelEvent(int level, char *type, sentinelRedisInstance *ri, 523 const char *fmt, ...) { 524 va_list ap; 525 char msg[REDIS_MAX_LOGMSG_LEN]; 526 robj *channel, *payload; 527 528 /* Handle %@ */ 529 if (fmt[0] == '%' && fmt[1] == '@') { 530 sentinelRedisInstance *master = (ri->flags & SRI_MASTER) ? 531 NULL : ri->master; 532 533 if (master) { 534 snprintf(msg, sizeof(msg), "%s %s %s %d @ %s %s %d", 535 sentinelRedisInstanceTypeStr(ri), 536 ri->name, ri->addr->ip, ri->addr->port, 537 master->name, master->addr->ip, master->addr->port); 538 } else { 539 snprintf(msg, sizeof(msg), "%s %s %s %d", 540 sentinelRedisInstanceTypeStr(ri), 541 ri->name, ri->addr->ip, ri->addr->port); 542 } 543 fmt += 2; 544 } else { 545 msg[0] = '\0'; 546 } 547 548 /* Use vsprintf for the rest of the formatting if any. */ 549 if (fmt[0] != '\0') { 550 va_start(ap, fmt); 551 vsnprintf(msg+strlen(msg), sizeof(msg)-strlen(msg), fmt, ap); 552 va_end(ap); 553 } 554 555 /* Log the message if the log level allows it to be logged. */ 556 if (level >= server.verbosity) 557 redisLog(level,"%s %s",type,msg); 558 559 /* Publish the message via Pub/Sub if it's not a debugging one. */ 560 if (level != REDIS_DEBUG) { 561 channel = createStringObject(type,strlen(type)); 562 payload = createStringObject(msg,strlen(msg)); 563 pubsubPublishMessage(channel,payload); 564 decrRefCount(channel); 565 decrRefCount(payload); 566 } 567 568 /* Call the notification script if applicable. */ 569 if (level == REDIS_WARNING && ri != NULL) { 570 sentinelRedisInstance *master = (ri->flags & SRI_MASTER) ? 571 ri : ri->master; 572 if (master->notification_script) { 573 sentinelScheduleScriptExecution(master->notification_script, 574 type,msg,NULL); 575 } 576 } 577 } 578 579 /* This function is called only at startup and is used to generate a 580 * +monitor event for every configured master. The same events are also 581 * generated when a master to monitor is added at runtime via the 582 * SENTINEL MONITOR command. */ 583 void sentinelGenerateInitialMonitorEvents(void) { 584 dictIterator *di; 585 dictEntry *de; 586 587 di = dictGetIterator(sentinel.masters); 588 while((de = dictNext(di)) != NULL) { 589 sentinelRedisInstance *ri = dictGetVal(de); 590 sentinelEvent(REDIS_WARNING,"+monitor",ri,"%@ quorum %d",ri->quorum); 591 } 592 dictReleaseIterator(di); 593 } 594 595 /* ============================ script execution ============================ */ 596 597 /* Release a script job structure and all the associated data. */ 598 void sentinelReleaseScriptJob(sentinelScriptJob *sj) { 599 int j = 0; 600 601 while(sj->argv[j]) sdsfree(sj->argv[j++]); 602 zfree(sj->argv); 603 zfree(sj); 604 } 605 606 #define SENTINEL_SCRIPT_MAX_ARGS 16 607 void sentinelScheduleScriptExecution(char *path, ...) { 608 va_list ap; 609 char *argv[SENTINEL_SCRIPT_MAX_ARGS+1]; 610 int argc = 1; 611 sentinelScriptJob *sj; 612 613 va_start(ap, path); 614 while(argc < SENTINEL_SCRIPT_MAX_ARGS) { 615 argv[argc] = va_arg(ap,char*); 616 if (!argv[argc]) break; 617 argv[argc] = sdsnew(argv[argc]); /* Copy the string. */ 618 argc++; 619 } 620 va_end(ap); 621 argv[0] = sdsnew(path); 622 623 sj = zmalloc(sizeof(*sj)); 624 sj->flags = SENTINEL_SCRIPT_NONE; 625 sj->retry_num = 0; 626 sj->argv = zmalloc(sizeof(char*)*(argc+1)); 627 sj->start_time = 0; 628 sj->pid = 0; 629 memcpy(sj->argv,argv,sizeof(char*)*(argc+1)); 630 631 listAddNodeTail(sentinel.scripts_queue,sj); 632 633 /* Remove the oldest non running script if we already hit the limit. */ 634 if (listLength(sentinel.scripts_queue) > SENTINEL_SCRIPT_MAX_QUEUE) { 635 listNode *ln; 636 listIter li; 637 638 listRewind(sentinel.scripts_queue,&li); 639 while ((ln = listNext(&li)) != NULL) { 640 sj = ln->value; 641 642 if (sj->flags & SENTINEL_SCRIPT_RUNNING) continue; 643 /* The first node is the oldest as we add on tail. */ 644 listDelNode(sentinel.scripts_queue,ln); 645 sentinelReleaseScriptJob(sj); 646 break; 647 } 648 redisAssert(listLength(sentinel.scripts_queue) <= 649 SENTINEL_SCRIPT_MAX_QUEUE); 650 } 651 } 652 653 /* Lookup a script in the scripts queue via pid, and returns the list node 654 * (so that we can easily remove it from the queue if needed). */ 655 listNode *sentinelGetScriptListNodeByPid(pid_t pid) { 656 listNode *ln; 657 listIter li; 658 659 listRewind(sentinel.scripts_queue,&li); 660 while ((ln = listNext(&li)) != NULL) { 661 sentinelScriptJob *sj = ln->value; 662 663 if ((sj->flags & SENTINEL_SCRIPT_RUNNING) && sj->pid == pid) 664 return ln; 665 } 666 return NULL; 667 } 668 669 /* Run pending scripts if we are not already at max number of running 670 * scripts. */ 671 void sentinelRunPendingScripts(void) { 672 listNode *ln; 673 listIter li; 674 mstime_t now = mstime(); 675 676 /* Find jobs that are not running and run them, from the top to the 677 * tail of the queue, so we run older jobs first. */ 678 listRewind(sentinel.scripts_queue,&li); 679 while (sentinel.running_scripts < SENTINEL_SCRIPT_MAX_RUNNING && 680 (ln = listNext(&li)) != NULL) 681 { 682 sentinelScriptJob *sj = ln->value; 683 pid_t pid; 684 685 /* Skip if already running. */ 686 if (sj->flags & SENTINEL_SCRIPT_RUNNING) continue; 687 688 /* Skip if it's a retry, but not enough time has elapsed. */ 689 if (sj->start_time && sj->start_time > now) continue; 690 691 sj->flags |= SENTINEL_SCRIPT_RUNNING; 692 sj->start_time = mstime(); 693 sj->retry_num++; 694 pid = fork(); 695 696 if (pid == -1) { 697 /* Parent (fork error). 698 * We report fork errors as signal 99, in order to unify the 699 * reporting with other kind of errors. */ 700 sentinelEvent(REDIS_WARNING,"-script-error",NULL, 701 "%s %d %d", sj->argv[0], 99, 0); 702 sj->flags &= ~SENTINEL_SCRIPT_RUNNING; 703 sj->pid = 0; 704 } else if (pid == 0) { 705 /* Child */ 706 execve(sj->argv[0],sj->argv,environ); 707 /* If we are here an error occurred. */ 708 _exit(2); /* Don't retry execution. */ 709 } else { 710 sentinel.running_scripts++; 711 sj->pid = pid; 712 sentinelEvent(REDIS_DEBUG,"+script-child",NULL,"%ld",(long)pid); 713 } 714 } 715 } 716 717 /* How much to delay the execution of a script that we need to retry after 718 * an error? 719 * 720 * We double the retry delay for every further retry we do. So for instance 721 * if RETRY_DELAY is set to 30 seconds and the max number of retries is 10 722 * starting from the second attempt to execute the script the delays are: 723 * 30 sec, 60 sec, 2 min, 4 min, 8 min, 16 min, 32 min, 64 min, 128 min. */ 724 mstime_t sentinelScriptRetryDelay(int retry_num) { 725 mstime_t delay = SENTINEL_SCRIPT_RETRY_DELAY; 726 727 while (retry_num-- > 1) delay *= 2; 728 return delay; 729 } 730 731 /* Check for scripts that terminated, and remove them from the queue if the 732 * script terminated successfully. If instead the script was terminated by 733 * a signal, or returned exit code "1", it is scheduled to run again if 734 * the max number of retries did not already elapsed. */ 735 void sentinelCollectTerminatedScripts(void) { 736 int statloc; 737 pid_t pid; 738 739 while ((pid = wait3(&statloc,WNOHANG,NULL)) > 0) { 740 int exitcode = WEXITSTATUS(statloc); 741 int bysignal = 0; 742 listNode *ln; 743 sentinelScriptJob *sj; 744 745 if (WIFSIGNALED(statloc)) bysignal = WTERMSIG(statloc); 746 sentinelEvent(REDIS_DEBUG,"-script-child",NULL,"%ld %d %d", 747 (long)pid, exitcode, bysignal); 748 749 ln = sentinelGetScriptListNodeByPid(pid); 750 if (ln == NULL) { 751 redisLog(REDIS_WARNING,"wait3() returned a pid (%ld) we can't find in our scripts execution queue!", (long)pid); 752 continue; 753 } 754 sj = ln->value; 755 756 /* If the script was terminated by a signal or returns an 757 * exit code of "1" (that means: please retry), we reschedule it 758 * if the max number of retries is not already reached. */ 759 if ((bysignal || exitcode == 1) && 760 sj->retry_num != SENTINEL_SCRIPT_MAX_RETRY) 761 { 762 sj->flags &= ~SENTINEL_SCRIPT_RUNNING; 763 sj->pid = 0; 764 sj->start_time = mstime() + 765 sentinelScriptRetryDelay(sj->retry_num); 766 } else { 767 /* Otherwise let's remove the script, but log the event if the 768 * execution did not terminated in the best of the ways. */ 769 if (bysignal || exitcode != 0) { 770 sentinelEvent(REDIS_WARNING,"-script-error",NULL, 771 "%s %d %d", sj->argv[0], bysignal, exitcode); 772 } 773 listDelNode(sentinel.scripts_queue,ln); 774 sentinelReleaseScriptJob(sj); 775 sentinel.running_scripts--; 776 } 777 } 778 } 779 780 /* Kill scripts in timeout, they'll be collected by the 781 * sentinelCollectTerminatedScripts() function. */ 782 void sentinelKillTimedoutScripts(void) { 783 listNode *ln; 784 listIter li; 785 mstime_t now = mstime(); 786 787 listRewind(sentinel.scripts_queue,&li); 788 while ((ln = listNext(&li)) != NULL) { 789 sentinelScriptJob *sj = ln->value; 790 791 if (sj->flags & SENTINEL_SCRIPT_RUNNING && 792 (now - sj->start_time) > SENTINEL_SCRIPT_MAX_RUNTIME) 793 { 794 sentinelEvent(REDIS_WARNING,"-script-timeout",NULL,"%s %ld", 795 sj->argv[0], (long)sj->pid); 796 kill(sj->pid,SIGKILL); 797 } 798 } 799 } 800 801 /* Implements SENTINEL PENDING-SCRIPTS command. */ 802 void sentinelPendingScriptsCommand(redisClient *c) { 803 listNode *ln; 804 listIter li; 805 806 addReplyMultiBulkLen(c,listLength(sentinel.scripts_queue)); 807 listRewind(sentinel.scripts_queue,&li); 808 while ((ln = listNext(&li)) != NULL) { 809 sentinelScriptJob *sj = ln->value; 810 int j = 0; 811 812 addReplyMultiBulkLen(c,10); 813 814 addReplyBulkCString(c,"argv"); 815 while (sj->argv[j]) j++; 816 addReplyMultiBulkLen(c,j); 817 j = 0; 818 while (sj->argv[j]) addReplyBulkCString(c,sj->argv[j++]); 819 820 addReplyBulkCString(c,"flags"); 821 addReplyBulkCString(c, 822 (sj->flags & SENTINEL_SCRIPT_RUNNING) ? "running" : "scheduled"); 823 824 addReplyBulkCString(c,"pid"); 825 addReplyBulkLongLong(c,sj->pid); 826 827 if (sj->flags & SENTINEL_SCRIPT_RUNNING) { 828 addReplyBulkCString(c,"run-time"); 829 addReplyBulkLongLong(c,mstime() - sj->start_time); 830 } else { 831 mstime_t delay = sj->start_time ? (sj->start_time-mstime()) : 0; 832 if (delay < 0) delay = 0; 833 addReplyBulkCString(c,"run-delay"); 834 addReplyBulkLongLong(c,delay); 835 } 836 837 addReplyBulkCString(c,"retry-num"); 838 addReplyBulkLongLong(c,sj->retry_num); 839 } 840 } 841 842 /* This function calls, if any, the client reconfiguration script with the 843 * following parameters: 844 * 845 * <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port> 846 * 847 * It is called every time a failover is performed. 848 * 849 * <state> is currently always "failover". 850 * <role> is either "leader" or "observer". 851 * 852 * from/to fields are respectively master -> promoted slave addresses for 853 * "start" and "end". */ 854 void sentinelCallClientReconfScript(sentinelRedisInstance *master, int role, char *state, sentinelAddr *from, sentinelAddr *to) { 855 char fromport[32], toport[32]; 856 857 if (master->client_reconfig_script == NULL) return; 858 ll2string(fromport,sizeof(fromport),from->port); 859 ll2string(toport,sizeof(toport),to->port); 860 sentinelScheduleScriptExecution(master->client_reconfig_script, 861 master->name, 862 (role == SENTINEL_LEADER) ? "leader" : "observer", 863 state, from->ip, fromport, to->ip, toport, NULL); 864 } 865 866 /* ========================== sentinelRedisInstance ========================= */ 867 868 /* Create a redis instance, the following fields must be populated by the 869 * caller if needed: 870 * runid: set to NULL but will be populated once INFO output is received. 871 * info_refresh: is set to 0 to mean that we never received INFO so far. 872 * 873 * If SRI_MASTER is set into initial flags the instance is added to 874 * sentinel.masters table. 875 * 876 * if SRI_SLAVE or SRI_SENTINEL is set then 'master' must be not NULL and the 877 * instance is added into master->slaves or master->sentinels table. 878 * 879 * If the instance is a slave or sentinel, the name parameter is ignored and 880 * is created automatically as hostname:port. 881 * 882 * The function fails if hostname can't be resolved or port is out of range. 883 * When this happens NULL is returned and errno is set accordingly to the 884 * createSentinelAddr() function. 885 * 886 * The function may also fail and return NULL with errno set to EBUSY if 887 * a master or slave with the same name already exists. */ 888 sentinelRedisInstance *createSentinelRedisInstance(char *name, int flags, char *hostname, int port, int quorum, sentinelRedisInstance *master) { 889 sentinelRedisInstance *ri; 890 sentinelAddr *addr; 891 dict *table = NULL; 892 char slavename[128], *sdsname; 893 894 redisAssert(flags & (SRI_MASTER|SRI_SLAVE|SRI_SENTINEL)); 895 redisAssert((flags & SRI_MASTER) || master != NULL); 896 897 /* Check address validity. */ 898 addr = createSentinelAddr(hostname,port); 899 if (addr == NULL) return NULL; 900 901 /* For slaves and sentinel we use ip:port as name. */ 902 if (flags & (SRI_SLAVE|SRI_SENTINEL)) { 903 snprintf(slavename,sizeof(slavename), 904 strchr(hostname,':') ? "[%s]:%d" : "%s:%d", 905 hostname,port); 906 name = slavename; 907 } 908 909 /* Make sure the entry is not duplicated. This may happen when the same 910 * name for a master is used multiple times inside the configuration or 911 * if we try to add multiple times a slave or sentinel with same ip/port 912 * to a master. */ 913 if (flags & SRI_MASTER) table = sentinel.masters; 914 else if (flags & SRI_SLAVE) table = master->slaves; 915 else if (flags & SRI_SENTINEL) table = master->sentinels; 916 sdsname = sdsnew(name); 917 if (dictFind(table,sdsname)) { 918 sdsfree(sdsname); 919 errno = EBUSY; 920 return NULL; 921 } 922 923 /* Create the instance object. */ 924 ri = zmalloc(sizeof(*ri)); 925 /* Note that all the instances are started in the disconnected state, 926 * the event loop will take care of connecting them. */ 927 ri->flags = flags | SRI_DISCONNECTED; 928 ri->name = sdsname; 929 ri->runid = NULL; 930 ri->config_epoch = 0; 931 ri->addr = addr; 932 ri->cc = NULL; 933 ri->pc = NULL; 934 ri->pending_commands = 0; 935 ri->cc_conn_time = 0; 936 ri->pc_conn_time = 0; 937 ri->pc_last_activity = 0; 938 /* We set the last_ping_time to "now" even if we actually don't have yet 939 * a connection with the node, nor we sent a ping. 940 * This is useful to detect a timeout in case we'll not be able to connect 941 * with the node at all. */ 942 ri->last_ping_time = mstime(); 943 ri->last_avail_time = mstime(); 944 ri->last_pong_time = mstime(); 945 ri->last_pub_time = mstime(); 946 ri->last_hello_time = mstime(); 947 ri->last_master_down_reply_time = mstime(); 948 ri->s_down_since_time = 0; 949 ri->o_down_since_time = 0; 950 ri->down_after_period = master ? master->down_after_period : 951 SENTINEL_DEFAULT_DOWN_AFTER; 952 ri->master_link_down_time = 0; 953 ri->auth_pass = NULL; 954 ri->slave_priority = SENTINEL_DEFAULT_SLAVE_PRIORITY; 955 ri->slave_reconf_sent_time = 0; 956 ri->slave_master_host = NULL; 957 ri->slave_master_port = 0; 958 ri->slave_master_link_status = SENTINEL_MASTER_LINK_STATUS_DOWN; 959 ri->slave_repl_offset = 0; 960 ri->sentinels = dictCreate(&instancesDictType,NULL); 961 ri->quorum = quorum; 962 ri->parallel_syncs = SENTINEL_DEFAULT_PARALLEL_SYNCS; 963 ri->master = master; 964 ri->slaves = dictCreate(&instancesDictType,NULL); 965 ri->info_refresh = 0; 966 967 /* Failover state. */ 968 ri->leader = NULL; 969 ri->leader_epoch = 0; 970 ri->failover_epoch = 0; 971 ri->failover_state = SENTINEL_FAILOVER_STATE_NONE; 972 ri->failover_state_change_time = 0; 973 ri->failover_start_time = 0; 974 ri->failover_timeout = SENTINEL_DEFAULT_FAILOVER_TIMEOUT; 975 ri->failover_delay_logged = 0; 976 ri->promoted_slave = NULL; 977 ri->notification_script = NULL; 978 ri->client_reconfig_script = NULL; 979 980 /* Role */ 981 ri->role_reported = ri->flags & (SRI_MASTER|SRI_SLAVE); 982 ri->role_reported_time = mstime(); 983 ri->slave_conf_change_time = mstime(); 984 985 /* Add into the right table. */ 986 dictAdd(table, ri->name, ri); 987 return ri; 988 } 989 990 /* Release this instance and all its slaves, sentinels, hiredis connections. 991 * This function does not take care of unlinking the instance from the main 992 * masters table (if it is a master) or from its master sentinels/slaves table 993 * if it is a slave or sentinel. */ 994 void releaseSentinelRedisInstance(sentinelRedisInstance *ri) { 995 /* Release all its slaves or sentinels if any. */ 996 dictRelease(ri->sentinels); 997 dictRelease(ri->slaves); 998 999 /* Release hiredis connections. */ 1000 if (ri->cc) sentinelKillLink(ri,ri->cc); 1001 if (ri->pc) sentinelKillLink(ri,ri->pc); 1002 1003 /* Free other resources. */ 1004 sdsfree(ri->name); 1005 sdsfree(ri->runid); 1006 sdsfree(ri->notification_script); 1007 sdsfree(ri->client_reconfig_script); 1008 sdsfree(ri->slave_master_host); 1009 sdsfree(ri->leader); 1010 sdsfree(ri->auth_pass); 1011 releaseSentinelAddr(ri->addr); 1012 1013 /* Clear state into the master if needed. */ 1014 if ((ri->flags & SRI_SLAVE) && (ri->flags & SRI_PROMOTED) && ri->master) 1015 ri->master->promoted_slave = NULL; 1016 1017 zfree(ri); 1018 } 1019 1020 /* Lookup a slave in a master Redis instance, by ip and port. */ 1021 sentinelRedisInstance *sentinelRedisInstanceLookupSlave( 1022 sentinelRedisInstance *ri, char *ip, int port) 1023 { 1024 sds key; 1025 sentinelRedisInstance *slave; 1026 1027 redisAssert(ri->flags & SRI_MASTER); 1028 key = sdscatprintf(sdsempty(), 1029 strchr(ip,':') ? "[%s]:%d" : "%s:%d", 1030 ip,port); 1031 slave = dictFetchValue(ri->slaves,key); 1032 sdsfree(key); 1033 return slave; 1034 } 1035 1036 /* Return the name of the type of the instance as a string. */ 1037 const char *sentinelRedisInstanceTypeStr(sentinelRedisInstance *ri) { 1038 if (ri->flags & SRI_MASTER) return "master"; 1039 else if (ri->flags & SRI_SLAVE) return "slave"; 1040 else if (ri->flags & SRI_SENTINEL) return "sentinel"; 1041 else return "unknown"; 1042 } 1043 1044 /* This function removes all the instances found in the dictionary of 1045 * sentinels in the specified 'master', having either: 1046 * 1047 * 1) The same ip/port as specified. 1048 * 2) The same runid. 1049 * 1050 * "1" and "2" don't need to verify at the same time, just one is enough. 1051 * If "runid" is NULL it is not checked. 1052 * Similarly if "ip" is NULL it is not checked. 1053 * 1054 * This function is useful because every time we add a new Sentinel into 1055 * a master's Sentinels dictionary, we want to be very sure about not 1056 * having duplicated instances for any reason. This is important because 1057 * other sentinels are needed to reach ODOWN quorum, and later to get 1058 * voted for a given configuration epoch in order to perform the failover. 1059 * 1060 * The function returns the number of Sentinels removed. */ 1061 int removeMatchingSentinelsFromMaster(sentinelRedisInstance *master, char *ip, int port, char *runid) { 1062 dictIterator *di; 1063 dictEntry *de; 1064 int removed = 0; 1065 1066 di = dictGetSafeIterator(master->sentinels); 1067 while((de = dictNext(di)) != NULL) { 1068 sentinelRedisInstance *ri = dictGetVal(de); 1069 1070 if ((ri->runid && runid && strcmp(ri->runid,runid) == 0) || 1071 (ip && strcmp(ri->addr->ip,ip) == 0 && port == ri->addr->port)) 1072 { 1073 dictDelete(master->sentinels,ri->name); 1074 removed++; 1075 } 1076 } 1077 dictReleaseIterator(di); 1078 return removed; 1079 } 1080 1081 /* Search an instance with the same runid, ip and port into a dictionary 1082 * of instances. Return NULL if not found, otherwise return the instance 1083 * pointer. 1084 * 1085 * runid or ip can be NULL. In such a case the search is performed only 1086 * by the non-NULL field. */ 1087 sentinelRedisInstance *getSentinelRedisInstanceByAddrAndRunID(dict *instances, char *ip, int port, char *runid) { 1088 dictIterator *di; 1089 dictEntry *de; 1090 sentinelRedisInstance *instance = NULL; 1091 1092 redisAssert(ip || runid); /* User must pass at least one search param. */ 1093 di = dictGetIterator(instances); 1094 while((de = dictNext(di)) != NULL) { 1095 sentinelRedisInstance *ri = dictGetVal(de); 1096 1097 if (runid && !ri->runid) continue; 1098 if ((runid == NULL || strcmp(ri->runid, runid) == 0) && 1099 (ip == NULL || (strcmp(ri->addr->ip, ip) == 0 && 1100 ri->addr->port == port))) 1101 { 1102 instance = ri; 1103 break; 1104 } 1105 } 1106 dictReleaseIterator(di); 1107 return instance; 1108 } 1109 1110 /* Master lookup by name */ 1111 sentinelRedisInstance *sentinelGetMasterByName(char *name) { 1112 sentinelRedisInstance *ri; 1113 sds sdsname = sdsnew(name); 1114 1115 ri = dictFetchValue(sentinel.masters,sdsname); 1116 sdsfree(sdsname); 1117 return ri; 1118 } 1119 1120 /* Add the specified flags to all the instances in the specified dictionary. */ 1121 void sentinelAddFlagsToDictOfRedisInstances(dict *instances, int flags) { 1122 dictIterator *di; 1123 dictEntry *de; 1124 1125 di = dictGetIterator(instances); 1126 while((de = dictNext(di)) != NULL) { 1127 sentinelRedisInstance *ri = dictGetVal(de); 1128 ri->flags |= flags; 1129 } 1130 dictReleaseIterator(di); 1131 } 1132 1133 /* Remove the specified flags to all the instances in the specified 1134 * dictionary. */ 1135 void sentinelDelFlagsToDictOfRedisInstances(dict *instances, int flags) { 1136 dictIterator *di; 1137 dictEntry *de; 1138 1139 di = dictGetIterator(instances); 1140 while((de = dictNext(di)) != NULL) { 1141 sentinelRedisInstance *ri = dictGetVal(de); 1142 ri->flags &= ~flags; 1143 } 1144 dictReleaseIterator(di); 1145 } 1146 1147 /* Reset the state of a monitored master: 1148 * 1) Remove all slaves. 1149 * 2) Remove all sentinels. 1150 * 3) Remove most of the flags resulting from runtime operations. 1151 * 4) Reset timers to their default value. 1152 * 5) In the process of doing this undo the failover if in progress. 1153 * 6) Disconnect the connections with the master (will reconnect automatically). 1154 */ 1155 1156 #define SENTINEL_RESET_NO_SENTINELS (1<<0) 1157 void sentinelResetMaster(sentinelRedisInstance *ri, int flags) { 1158 redisAssert(ri->flags & SRI_MASTER); 1159 dictRelease(ri->slaves); 1160 ri->slaves = dictCreate(&instancesDictType,NULL); 1161 if (!(flags & SENTINEL_RESET_NO_SENTINELS)) { 1162 dictRelease(ri->sentinels); 1163 ri->sentinels = dictCreate(&instancesDictType,NULL); 1164 } 1165 if (ri->cc) sentinelKillLink(ri,ri->cc); 1166 if (ri->pc) sentinelKillLink(ri,ri->pc); 1167 ri->flags &= SRI_MASTER|SRI_DISCONNECTED; 1168 if (ri->leader) { 1169 sdsfree(ri->leader); 1170 ri->leader = NULL; 1171 } 1172 ri->failover_state = SENTINEL_FAILOVER_STATE_NONE; 1173 ri->failover_state_change_time = 0; 1174 ri->failover_start_time = 0; 1175 ri->promoted_slave = NULL; 1176 sdsfree(ri->runid); 1177 sdsfree(ri->slave_master_host); 1178 ri->runid = NULL; 1179 ri->slave_master_host = NULL; 1180 ri->last_ping_time = mstime(); 1181 ri->last_avail_time = mstime(); 1182 ri->last_pong_time = mstime(); 1183 ri->role_reported_time = mstime(); 1184 ri->role_reported = SRI_MASTER; 1185 if (flags & SENTINEL_GENERATE_EVENT) 1186 sentinelEvent(REDIS_WARNING,"+reset-master",ri,"%@"); 1187 } 1188 1189 /* Call sentinelResetMaster() on every master with a name matching the specified 1190 * pattern. */ 1191 int sentinelResetMastersByPattern(char *pattern, int flags) { 1192 dictIterator *di; 1193 dictEntry *de; 1194 int reset = 0; 1195 1196 di = dictGetIterator(sentinel.masters); 1197 while((de = dictNext(di)) != NULL) { 1198 sentinelRedisInstance *ri = dictGetVal(de); 1199 1200 if (ri->name) { 1201 if (stringmatch(pattern,ri->name,0)) { 1202 sentinelResetMaster(ri,flags); 1203 reset++; 1204 } 1205 } 1206 } 1207 dictReleaseIterator(di); 1208 return reset; 1209 } 1210 1211 /* Reset the specified master with sentinelResetMaster(), and also change 1212 * the ip:port address, but take the name of the instance unmodified. 1213 * 1214 * This is used to handle the +switch-master event. 1215 * 1216 * The function returns REDIS_ERR if the address can't be resolved for some 1217 * reason. Otherwise REDIS_OK is returned. */ 1218 int sentinelResetMasterAndChangeAddress(sentinelRedisInstance *master, char *ip, int port) { 1219 sentinelAddr *oldaddr, *newaddr; 1220 sentinelAddr **slaves = NULL; 1221 int numslaves = 0, j; 1222 dictIterator *di; 1223 dictEntry *de; 1224 1225 newaddr = createSentinelAddr(ip,port); 1226 if (newaddr == NULL) return REDIS_ERR; 1227 1228 /* Make a list of slaves to add back after the reset. 1229 * Don't include the one having the address we are switching to. */ 1230 di = dictGetIterator(master->slaves); 1231 while((de = dictNext(di)) != NULL) { 1232 sentinelRedisInstance *slave = dictGetVal(de); 1233 1234 if (sentinelAddrIsEqual(slave->addr,newaddr)) continue; 1235 slaves = zrealloc(slaves,sizeof(sentinelAddr*)*(numslaves+1)); 1236 slaves[numslaves++] = createSentinelAddr(slave->addr->ip, 1237 slave->addr->port); 1238 } 1239 dictReleaseIterator(di); 1240 1241 /* If we are switching to a different address, include the old address 1242 * as a slave as well, so that we'll be able to sense / reconfigure 1243 * the old master. */ 1244 if (!sentinelAddrIsEqual(newaddr,master->addr)) { 1245 slaves = zrealloc(slaves,sizeof(sentinelAddr*)*(numslaves+1)); 1246 slaves[numslaves++] = createSentinelAddr(master->addr->ip, 1247 master->addr->port); 1248 } 1249 1250 /* Reset and switch address. */ 1251 sentinelResetMaster(master,SENTINEL_RESET_NO_SENTINELS); 1252 oldaddr = master->addr; 1253 master->addr = newaddr; 1254 master->o_down_since_time = 0; 1255 master->s_down_since_time = 0; 1256 1257 /* Add slaves back. */ 1258 for (j = 0; j < numslaves; j++) { 1259 sentinelRedisInstance *slave; 1260 1261 slave = createSentinelRedisInstance(NULL,SRI_SLAVE,slaves[j]->ip, 1262 slaves[j]->port, master->quorum, master); 1263 releaseSentinelAddr(slaves[j]); 1264 if (slave) { 1265 sentinelEvent(REDIS_NOTICE,"+slave",slave,"%@"); 1266 sentinelFlushConfig(); 1267 } 1268 } 1269 zfree(slaves); 1270 1271 /* Release the old address at the end so we are safe even if the function 1272 * gets the master->addr->ip and master->addr->port as arguments. */ 1273 releaseSentinelAddr(oldaddr); 1274 sentinelFlushConfig(); 1275 return REDIS_OK; 1276 } 1277 1278 /* Return non-zero if there was no SDOWN or ODOWN error associated to this 1279 * instance in the latest 'ms' milliseconds. */ 1280 int sentinelRedisInstanceNoDownFor(sentinelRedisInstance *ri, mstime_t ms) { 1281 mstime_t most_recent; 1282 1283 most_recent = ri->s_down_since_time; 1284 if (ri->o_down_since_time > most_recent) 1285 most_recent = ri->o_down_since_time; 1286 return most_recent == 0 || (mstime() - most_recent) > ms; 1287 } 1288 1289 /* Return the current master address, that is, its address or the address 1290 * of the promoted slave if already operational. */ 1291 sentinelAddr *sentinelGetCurrentMasterAddress(sentinelRedisInstance *master) { 1292 /* If we are failing over the master, and the state is already 1293 * SENTINEL_FAILOVER_STATE_RECONF_SLAVES or greater, it means that we 1294 * already have the new configuration epoch in the master, and the 1295 * slave acknowledged the configuration switch. Advertise the new 1296 * address. */ 1297 if ((master->flags & SRI_FAILOVER_IN_PROGRESS) && 1298 master->promoted_slave && 1299 master->failover_state >= SENTINEL_FAILOVER_STATE_RECONF_SLAVES) 1300 { 1301 return master->promoted_slave->addr; 1302 } else { 1303 return master->addr; 1304 } 1305 } 1306 1307 /* This function sets the down_after_period field value in 'master' to all 1308 * the slaves and sentinel instances connected to this master. */ 1309 void sentinelPropagateDownAfterPeriod(sentinelRedisInstance *master) { 1310 dictIterator *di; 1311 dictEntry *de; 1312 int j; 1313 dict *d[] = {master->slaves, master->sentinels, NULL}; 1314 1315 for (j = 0; d[j]; j++) { 1316 di = dictGetIterator(d[j]); 1317 while((de = dictNext(di)) != NULL) { 1318 sentinelRedisInstance *ri = dictGetVal(de); 1319 ri->down_after_period = master->down_after_period; 1320 } 1321 dictReleaseIterator(di); 1322 } 1323 } 1324 1325 /* ============================ Config handling ============================= */ 1326 char *sentinelHandleConfiguration(char **argv, int argc) { 1327 sentinelRedisInstance *ri; 1328 1329 if (!strcasecmp(argv[0],"monitor") && argc == 5) { 1330 /* monitor <name> <host> <port> <quorum> */ 1331 int quorum = atoi(argv[4]); 1332 1333 if (quorum <= 0) return "Quorum must be 1 or greater."; 1334 if (createSentinelRedisInstance(argv[1],SRI_MASTER,argv[2], 1335 atoi(argv[3]),quorum,NULL) == NULL) 1336 { 1337 switch(errno) { 1338 case EBUSY: return "Duplicated master name."; 1339 case ENOENT: return "Can't resolve master instance hostname."; 1340 case EINVAL: return "Invalid port number"; 1341 } 1342 } 1343 } else if (!strcasecmp(argv[0],"down-after-milliseconds") && argc == 3) { 1344 /* down-after-milliseconds <name> <milliseconds> */ 1345 ri = sentinelGetMasterByName(argv[1]); 1346 if (!ri) return "No such master with specified name."; 1347 ri->down_after_period = atoi(argv[2]); 1348 if (ri->down_after_period <= 0) 1349 return "negative or zero time parameter."; 1350 sentinelPropagateDownAfterPeriod(ri); 1351 } else if (!strcasecmp(argv[0],"failover-timeout") && argc == 3) { 1352 /* failover-timeout <name> <milliseconds> */ 1353 ri = sentinelGetMasterByName(argv[1]); 1354 if (!ri) return "No such master with specified name."; 1355 ri->failover_timeout = atoi(argv[2]); 1356 if (ri->failover_timeout <= 0) 1357 return "negative or zero time parameter."; 1358 } else if (!strcasecmp(argv[0],"parallel-syncs") && argc == 3) { 1359 /* parallel-syncs <name> <milliseconds> */ 1360 ri = sentinelGetMasterByName(argv[1]); 1361 if (!ri) return "No such master with specified name."; 1362 ri->parallel_syncs = atoi(argv[2]); 1363 } else if (!strcasecmp(argv[0],"notification-script") && argc == 3) { 1364 /* notification-script <name> <path> */ 1365 ri = sentinelGetMasterByName(argv[1]); 1366 if (!ri) return "No such master with specified name."; 1367 if (access(argv[2],X_OK) == -1) 1368 return "Notification script seems non existing or non executable."; 1369 ri->notification_script = sdsnew(argv[2]); 1370 } else if (!strcasecmp(argv[0],"client-reconfig-script") && argc == 3) { 1371 /* client-reconfig-script <name> <path> */ 1372 ri = sentinelGetMasterByName(argv[1]); 1373 if (!ri) return "No such master with specified name."; 1374 if (access(argv[2],X_OK) == -1) 1375 return "Client reconfiguration script seems non existing or " 1376 "non executable."; 1377 ri->client_reconfig_script = sdsnew(argv[2]); 1378 } else if (!strcasecmp(argv[0],"auth-pass") && argc == 3) { 1379 /* auth-pass <name> <password> */ 1380 ri = sentinelGetMasterByName(argv[1]); 1381 if (!ri) return "No such master with specified name."; 1382 ri->auth_pass = sdsnew(argv[2]); 1383 } else if (!strcasecmp(argv[0],"current-epoch") && argc == 2) { 1384 /* current-epoch <epoch> */ 1385 unsigned long long current_epoch = strtoull(argv[1],NULL,10); 1386 if (current_epoch > sentinel.current_epoch) 1387 sentinel.current_epoch = current_epoch; 1388 } else if (!strcasecmp(argv[0],"config-epoch") && argc == 3) { 1389 /* config-epoch <name> <epoch> */ 1390 ri = sentinelGetMasterByName(argv[1]); 1391 if (!ri) return "No such master with specified name."; 1392 ri->config_epoch = strtoull(argv[2],NULL,10); 1393 /* The following update of current_epoch is not really useful as 1394 * now the current epoch is persisted on the config file, but 1395 * we leave this check here for redundancy. */ 1396 if (ri->config_epoch > sentinel.current_epoch) 1397 sentinel.current_epoch = ri->config_epoch; 1398 } else if (!strcasecmp(argv[0],"leader-epoch") && argc == 3) { 1399 /* leader-epoch <name> <epoch> */ 1400 ri = sentinelGetMasterByName(argv[1]); 1401 if (!ri) return "No such master with specified name."; 1402 ri->leader_epoch = strtoull(argv[2],NULL,10); 1403 } else if (!strcasecmp(argv[0],"known-slave") && argc == 4) { 1404 sentinelRedisInstance *slave; 1405 1406 /* known-slave <name> <ip> <port> */ 1407 ri = sentinelGetMasterByName(argv[1]); 1408 if (!ri) return "No such master with specified name."; 1409 if ((slave = createSentinelRedisInstance(NULL,SRI_SLAVE,argv[2], 1410 atoi(argv[3]), ri->quorum, ri)) == NULL) 1411 { 1412 return "Wrong hostname or port for slave."; 1413 } 1414 } else if (!strcasecmp(argv[0],"known-sentinel") && 1415 (argc == 4 || argc == 5)) { 1416 sentinelRedisInstance *si; 1417 1418 /* known-sentinel <name> <ip> <port> [runid] */ 1419 ri = sentinelGetMasterByName(argv[1]); 1420 if (!ri) return "No such master with specified name."; 1421 if ((si = createSentinelRedisInstance(NULL,SRI_SENTINEL,argv[2], 1422 atoi(argv[3]), ri->quorum, ri)) == NULL) 1423 { 1424 return "Wrong hostname or port for sentinel."; 1425 } 1426 if (argc == 5) si->runid = sdsnew(argv[4]); 1427 } else { 1428 return "Unrecognized sentinel configuration statement."; 1429 } 1430 return NULL; 1431 } 1432 1433 /* Implements CONFIG REWRITE for "sentinel" option. 1434 * This is used not just to rewrite the configuration given by the user 1435 * (the configured masters) but also in order to retain the state of 1436 * Sentinel across restarts: config epoch of masters, associated slaves 1437 * and sentinel instances, and so forth. */ 1438 void rewriteConfigSentinelOption(struct rewriteConfigState *state) { 1439 dictIterator *di, *di2; 1440 dictEntry *de; 1441 sds line; 1442 1443 /* For every master emit a "sentinel monitor" config entry. */ 1444 di = dictGetIterator(sentinel.masters); 1445 while((de = dictNext(di)) != NULL) { 1446 sentinelRedisInstance *master, *ri; 1447 sentinelAddr *master_addr; 1448 1449 /* sentinel monitor */ 1450 master = dictGetVal(de); 1451 master_addr = sentinelGetCurrentMasterAddress(master); 1452 line = sdscatprintf(sdsempty(),"sentinel monitor %s %s %d %d", 1453 master->name, master_addr->ip, master_addr->port, 1454 master->quorum); 1455 rewriteConfigRewriteLine(state,"sentinel",line,1); 1456 1457 /* sentinel down-after-milliseconds */ 1458 if (master->down_after_period != SENTINEL_DEFAULT_DOWN_AFTER) { 1459 line = sdscatprintf(sdsempty(), 1460 "sentinel down-after-milliseconds %s %ld", 1461 master->name, (long) master->down_after_period); 1462 rewriteConfigRewriteLine(state,"sentinel",line,1); 1463 } 1464 1465 /* sentinel failover-timeout */ 1466 if (master->failover_timeout != SENTINEL_DEFAULT_FAILOVER_TIMEOUT) { 1467 line = sdscatprintf(sdsempty(), 1468 "sentinel failover-timeout %s %ld", 1469 master->name, (long) master->failover_timeout); 1470 rewriteConfigRewriteLine(state,"sentinel",line,1); 1471 } 1472 1473 /* sentinel parallel-syncs */ 1474 if (master->parallel_syncs != SENTINEL_DEFAULT_PARALLEL_SYNCS) { 1475 line = sdscatprintf(sdsempty(), 1476 "sentinel parallel-syncs %s %d", 1477 master->name, master->parallel_syncs); 1478 rewriteConfigRewriteLine(state,"sentinel",line,1); 1479 } 1480 1481 /* sentinel notification-script */ 1482 if (master->notification_script) { 1483 line = sdscatprintf(sdsempty(), 1484 "sentinel notification-script %s %s", 1485 master->name, master->notification_script); 1486 rewriteConfigRewriteLine(state,"sentinel",line,1); 1487 } 1488 1489 /* sentinel client-reconfig-script */ 1490 if (master->client_reconfig_script) { 1491 line = sdscatprintf(sdsempty(), 1492 "sentinel client-reconfig-script %s %s", 1493 master->name, master->client_reconfig_script); 1494 rewriteConfigRewriteLine(state,"sentinel",line,1); 1495 } 1496 1497 /* sentinel auth-pass */ 1498 if (master->auth_pass) { 1499 line = sdscatprintf(sdsempty(), 1500 "sentinel auth-pass %s %s", 1501 master->name, master->auth_pass); 1502 rewriteConfigRewriteLine(state,"sentinel",line,1); 1503 } 1504 1505 /* sentinel config-epoch */ 1506 line = sdscatprintf(sdsempty(), 1507 "sentinel config-epoch %s %llu", 1508 master->name, (unsigned long long) master->config_epoch); 1509 rewriteConfigRewriteLine(state,"sentinel",line,1); 1510 1511 /* sentinel leader-epoch */ 1512 line = sdscatprintf(sdsempty(), 1513 "sentinel leader-epoch %s %llu", 1514 master->name, (unsigned long long) master->leader_epoch); 1515 rewriteConfigRewriteLine(state,"sentinel",line,1); 1516 1517 /* sentinel known-slave */ 1518 di2 = dictGetIterator(master->slaves); 1519 while((de = dictNext(di2)) != NULL) { 1520 sentinelAddr *slave_addr; 1521 1522 ri = dictGetVal(de); 1523 slave_addr = ri->addr; 1524 1525 /* If master_addr (obtained using sentinelGetCurrentMasterAddress() 1526 * so it may be the address of the promoted slave) is equal to this 1527 * slave's address, a failover is in progress and the slave was 1528 * already successfully promoted. So as the address of this slave 1529 * we use the old master address instead. */ 1530 if (sentinelAddrIsEqual(slave_addr,master_addr)) 1531 slave_addr = master->addr; 1532 line = sdscatprintf(sdsempty(), 1533 "sentinel known-slave %s %s %d", 1534 master->name, ri->addr->ip, ri->addr->port); 1535 rewriteConfigRewriteLine(state,"sentinel",line,1); 1536 } 1537 dictReleaseIterator(di2); 1538 1539 /* sentinel known-sentinel */ 1540 di2 = dictGetIterator(master->sentinels); 1541 while((de = dictNext(di2)) != NULL) { 1542 ri = dictGetVal(de); 1543 line = sdscatprintf(sdsempty(), 1544 "sentinel known-sentinel %s %s %d%s%s", 1545 master->name, ri->addr->ip, ri->addr->port, 1546 ri->runid ? " " : "", 1547 ri->runid ? ri->runid : ""); 1548 rewriteConfigRewriteLine(state,"sentinel",line,1); 1549 } 1550 dictReleaseIterator(di2); 1551 } 1552 1553 /* sentinel current-epoch is a global state valid for all the masters. */ 1554 line = sdscatprintf(sdsempty(), 1555 "sentinel current-epoch %llu", (unsigned long long) sentinel.current_epoch); 1556 rewriteConfigRewriteLine(state,"sentinel",line,1); 1557 1558 dictReleaseIterator(di); 1559 } 1560 1561 /* This function uses the config rewriting Redis engine in order to persist 1562 * the state of the Sentinel in the current configuration file. 1563 * 1564 * Before returning the function calls fsync() against the generated 1565 * configuration file to make sure changes are committed to disk. 1566 * 1567 * On failure the function logs a warning on the Redis log. */ 1568 void sentinelFlushConfig(void) { 1569 int fd = -1; 1570 int saved_hz = server.hz; 1571 int rewrite_status; 1572 1573 server.hz = REDIS_DEFAULT_HZ; 1574 rewrite_status = rewriteConfig(server.configfile); 1575 server.hz = saved_hz; 1576 1577 if (rewrite_status == -1) goto werr; 1578 if ((fd = open(server.configfile,O_RDONLY)) == -1) goto werr; 1579 if (fsync(fd) == -1) goto werr; 1580 if (close(fd) == EOF) goto werr; 1581 return; 1582 1583 werr: 1584 if (fd != -1) close(fd); 1585 redisLog(REDIS_WARNING,"WARNING: Sentinel was not able to save the new configuration on disk!!!: %s", strerror(errno)); 1586 } 1587 1588 /* ====================== hiredis connection handling ======================= */ 1589 1590 /* Completely disconnect a hiredis link from an instance. */ 1591 void sentinelKillLink(sentinelRedisInstance *ri, redisAsyncContext *c) { 1592 if (ri->cc == c) { 1593 ri->cc = NULL; 1594 ri->pending_commands = 0; 1595 } 1596 if (ri->pc == c) ri->pc = NULL; 1597 c->data = NULL; 1598 ri->flags |= SRI_DISCONNECTED; 1599 redisAsyncFree(c); 1600 } 1601 1602 /* This function takes a hiredis context that is in an error condition 1603 * and make sure to mark the instance as disconnected performing the 1604 * cleanup needed. 1605 * 1606 * Note: we don't free the hiredis context as hiredis will do it for us 1607 * for async connections. */ 1608 void sentinelDisconnectInstanceFromContext(const redisAsyncContext *c) { 1609 sentinelRedisInstance *ri = c->data; 1610 int pubsub; 1611 1612 if (ri == NULL) return; /* The instance no longer exists. */ 1613 1614 pubsub = (ri->pc == c); 1615 sentinelEvent(REDIS_DEBUG, pubsub ? "-pubsub-link" : "-cmd-link", ri, 1616 "%@ #%s", c->errstr); 1617 if (pubsub) 1618 ri->pc = NULL; 1619 else 1620 ri->cc = NULL; 1621 ri->flags |= SRI_DISCONNECTED; 1622 } 1623 1624 void sentinelLinkEstablishedCallback(const redisAsyncContext *c, int status) { 1625 if (status != REDIS_OK) { 1626 sentinelDisconnectInstanceFromContext(c); 1627 } else { 1628 sentinelRedisInstance *ri = c->data; 1629 int pubsub = (ri->pc == c); 1630 1631 sentinelEvent(REDIS_DEBUG, pubsub ? "+pubsub-link" : "+cmd-link", ri, 1632 "%@"); 1633 } 1634 } 1635 1636 void sentinelDisconnectCallback(const redisAsyncContext *c, int status) { 1637 sentinelDisconnectInstanceFromContext(c); 1638 } 1639 1640 /* Send the AUTH command with the specified master password if needed. 1641 * Note that for slaves the password set for the master is used. 1642 * 1643 * We don't check at all if the command was successfully transmitted 1644 * to the instance as if it fails Sentinel will detect the instance down, 1645 * will disconnect and reconnect the link and so forth. */ 1646 void sentinelSendAuthIfNeeded(sentinelRedisInstance *ri, redisAsyncContext *c) { 1647 char *auth_pass = (ri->flags & SRI_MASTER) ? ri->auth_pass : 1648 ri->master->auth_pass; 1649 1650 if (auth_pass) { 1651 if (redisAsyncCommand(c, sentinelDiscardReplyCallback, NULL, "AUTH %s", 1652 auth_pass) == REDIS_OK) ri->pending_commands++; 1653 } 1654 } 1655 1656 /* Use CLIENT SETNAME to name the connection in the Redis instance as 1657 * sentinel-<first_8_chars_of_runid>-<connection_type> 1658 * The connection type is "cmd" or "pubsub" as specified by 'type'. 1659 * 1660 * This makes it possible to list all the sentinel instances connected 1661 * to a Redis servewr with CLIENT LIST, grepping for a specific name format. */ 1662 void sentinelSetClientName(sentinelRedisInstance *ri, redisAsyncContext *c, char *type) { 1663 char name[64]; 1664 1665 snprintf(name,sizeof(name),"sentinel-%.8s-%s",server.runid,type); 1666 if (redisAsyncCommand(c, sentinelDiscardReplyCallback, NULL, 1667 "CLIENT SETNAME %s", name) == REDIS_OK) 1668 { 1669 ri->pending_commands++; 1670 } 1671 } 1672 1673 /* Create the async connections for the specified instance if the instance 1674 * is disconnected. Note that the SRI_DISCONNECTED flag is set even if just 1675 * one of the two links (commands and pub/sub) is missing. */ 1676 void sentinelReconnectInstance(sentinelRedisInstance *ri) { 1677 if (!(ri->flags & SRI_DISCONNECTED)) return; 1678 1679 /* Commands connection. */ 1680 if (ri->cc == NULL) { 1681 ri->cc = redisAsyncConnectBind(ri->addr->ip,ri->addr->port,REDIS_BIND_ADDR); 1682 if (ri->cc->err) { 1683 sentinelEvent(REDIS_DEBUG,"-cmd-link-reconnection",ri,"%@ #%s", 1684 ri->cc->errstr); 1685 sentinelKillLink(ri,ri->cc); 1686 } else { 1687 ri->cc_conn_time = mstime(); 1688 ri->cc->data = ri; 1689 redisAeAttach(server.el,ri->cc); 1690 redisAsyncSetConnectCallback(ri->cc, 1691 sentinelLinkEstablishedCallback); 1692 redisAsyncSetDisconnectCallback(ri->cc, 1693 sentinelDisconnectCallback); 1694 sentinelSendAuthIfNeeded(ri,ri->cc); 1695 sentinelSetClientName(ri,ri->cc,"cmd"); 1696 1697 /* Send a PING ASAP when reconnecting. */ 1698 sentinelSendPing(ri); 1699 } 1700 } 1701 /* Pub / Sub */ 1702 if ((ri->flags & (SRI_MASTER|SRI_SLAVE)) && ri->pc == NULL) { 1703 ri->pc = redisAsyncConnectBind(ri->addr->ip,ri->addr->port,REDIS_BIND_ADDR); 1704 if (ri->pc->err) { 1705 sentinelEvent(REDIS_DEBUG,"-pubsub-link-reconnection",ri,"%@ #%s", 1706 ri->pc->errstr); 1707 sentinelKillLink(ri,ri->pc); 1708 } else { 1709 int retval; 1710 1711 ri->pc_conn_time = mstime(); 1712 ri->pc->data = ri; 1713 redisAeAttach(server.el,ri->pc); 1714 redisAsyncSetConnectCallback(ri->pc, 1715 sentinelLinkEstablishedCallback); 1716 redisAsyncSetDisconnectCallback(ri->pc, 1717 sentinelDisconnectCallback); 1718 sentinelSendAuthIfNeeded(ri,ri->pc); 1719 sentinelSetClientName(ri,ri->pc,"pubsub"); 1720 /* Now we subscribe to the Sentinels "Hello" channel. */ 1721 retval = redisAsyncCommand(ri->pc, 1722 sentinelReceiveHelloMessages, NULL, "SUBSCRIBE %s", 1723 SENTINEL_HELLO_CHANNEL); 1724 if (retval != REDIS_OK) { 1725 /* If we can't subscribe, the Pub/Sub connection is useless 1726 * and we can simply disconnect it and try again. */ 1727 sentinelKillLink(ri,ri->pc); 1728 return; 1729 } 1730 } 1731 } 1732 /* Clear the DISCONNECTED flags only if we have both the connections 1733 * (or just the commands connection if this is a sentinel instance). */ 1734 if (ri->cc && (ri->flags & SRI_SENTINEL || ri->pc)) 1735 ri->flags &= ~SRI_DISCONNECTED; 1736 } 1737 1738 /* ======================== Redis instances pinging ======================== */ 1739 1740 /* Return true if master looks "sane", that is: 1741 * 1) It is actually a master in the current configuration. 1742 * 2) It reports itself as a master. 1743 * 3) It is not SDOWN or ODOWN. 1744 * 4) We obtained last INFO no more than two times the INFO period time ago. */ 1745 int sentinelMasterLooksSane(sentinelRedisInstance *master) { 1746 return 1747 master->flags & SRI_MASTER && 1748 master->role_reported == SRI_MASTER && 1749 (master->flags & (SRI_S_DOWN|SRI_O_DOWN)) == 0 && 1750 (mstime() - master->info_refresh) < SENTINEL_INFO_PERIOD*2; 1751 } 1752 1753 /* Process the INFO output from masters. */ 1754 void sentinelRefreshInstanceInfo(sentinelRedisInstance *ri, const char *info) { 1755 sds *lines; 1756 int numlines, j; 1757 int role = 0; 1758 1759 /* The following fields must be reset to a given value in the case they 1760 * are not found at all in the INFO output. */ 1761 ri->master_link_down_time = 0; 1762 1763 /* Process line by line. */ 1764 lines = sdssplitlen(info,strlen(info),"\r\n",2,&numlines); 1765 for (j = 0; j < numlines; j++) { 1766 sentinelRedisInstance *slave; 1767 sds l = lines[j]; 1768 1769 /* run_id:<40 hex chars>*/ 1770 if (sdslen(l) >= 47 && !memcmp(l,"run_id:",7)) { 1771 if (ri->runid == NULL) { 1772 ri->runid = sdsnewlen(l+7,40); 1773 } else { 1774 if (strncmp(ri->runid,l+7,40) != 0) { 1775 sentinelEvent(REDIS_NOTICE,"+reboot",ri,"%@"); 1776 sdsfree(ri->runid); 1777 ri->runid = sdsnewlen(l+7,40); 1778 } 1779 } 1780 } 1781 1782 /* old versions: slave0:<ip>,<port>,<state> 1783 * new versions: slave0:ip=127.0.0.1,port=9999,... */ 1784 if ((ri->flags & SRI_MASTER) && 1785 sdslen(l) >= 7 && 1786 !memcmp(l,"slave",5) && isdigit(l[5])) 1787 { 1788 char *ip, *port, *end; 1789 1790 if (strstr(l,"ip=") == NULL) { 1791 /* Old format. */ 1792 ip = strchr(l,':'); if (!ip) continue; 1793 ip++; /* Now ip points to start of ip address. */ 1794 port = strchr(ip,','); if (!port) continue; 1795 *port = '\0'; /* nul term for easy access. */ 1796 port++; /* Now port points to start of port number. */ 1797 end = strchr(port,','); if (!end) continue; 1798 *end = '\0'; /* nul term for easy access. */ 1799 } else { 1800 /* New format. */ 1801 ip = strstr(l,"ip="); if (!ip) continue; 1802 ip += 3; /* Now ip points to start of ip address. */ 1803 port = strstr(l,"port="); if (!port) continue; 1804 port += 5; /* Now port points to start of port number. */ 1805 /* Nul term both fields for easy access. */ 1806 end = strchr(ip,','); if (end) *end = '\0'; 1807 end = strchr(port,','); if (end) *end = '\0'; 1808 } 1809 1810 /* Check if we already have this slave into our table, 1811 * otherwise add it. */ 1812 if (sentinelRedisInstanceLookupSlave(ri,ip,atoi(port)) == NULL) { 1813 if ((slave = createSentinelRedisInstance(NULL,SRI_SLAVE,ip, 1814 atoi(port), ri->quorum, ri)) != NULL) 1815 { 1816 sentinelEvent(REDIS_NOTICE,"+slave",slave,"%@"); 1817 } 1818 } 1819 } 1820 1821 /* master_link_down_since_seconds:<seconds> */ 1822 if (sdslen(l) >= 32 && 1823 !memcmp(l,"master_link_down_since_seconds",30)) 1824 { 1825 ri->master_link_down_time = strtoll(l+31,NULL,10)*1000; 1826 } 1827 1828 /* role:<role> */ 1829 if (!memcmp(l,"role:master",11)) role = SRI_MASTER; 1830 else if (!memcmp(l,"role:slave",10)) role = SRI_SLAVE; 1831 1832 if (role == SRI_SLAVE) { 1833 /* master_host:<host> */ 1834 if (sdslen(l) >= 12 && !memcmp(l,"master_host:",12)) { 1835 if (ri->slave_master_host == NULL || 1836 strcasecmp(l+12,ri->slave_master_host)) 1837 { 1838 sdsfree(ri->slave_master_host); 1839 ri->slave_master_host = sdsnew(l+12); 1840 ri->slave_conf_change_time = mstime(); 1841 } 1842 } 1843 1844 /* master_port:<port> */ 1845 if (sdslen(l) >= 12 && !memcmp(l,"master_port:",12)) { 1846 int slave_master_port = atoi(l+12); 1847 1848 if (ri->slave_master_port != slave_master_port) { 1849 ri->slave_master_port = slave_master_port; 1850 ri->slave_conf_change_time = mstime(); 1851 } 1852 } 1853 1854 /* master_link_status:<status> */ 1855 if (sdslen(l) >= 19 && !memcmp(l,"master_link_status:",19)) { 1856 ri->slave_master_link_status = 1857 (strcasecmp(l+19,"up") == 0) ? 1858 SENTINEL_MASTER_LINK_STATUS_UP : 1859 SENTINEL_MASTER_LINK_STATUS_DOWN; 1860 } 1861 1862 /* slave_priority:<priority> */ 1863 if (sdslen(l) >= 15 && !memcmp(l,"slave_priority:",15)) 1864 ri->slave_priority = atoi(l+15); 1865 1866 /* slave_repl_offset:<offset> */ 1867 if (sdslen(l) >= 18 && !memcmp(l,"slave_repl_offset:",18)) 1868 ri->slave_repl_offset = strtoull(l+18,NULL,10); 1869 } 1870 } 1871 ri->info_refresh = mstime(); 1872 sdsfreesplitres(lines,numlines); 1873 1874 /* ---------------------------- Acting half ----------------------------- 1875 * Some things will not happen if sentinel.tilt is true, but some will 1876 * still be processed. */ 1877 1878 /* Remember when the role changed. */ 1879 if (role != ri->role_reported) { 1880 ri->role_reported_time = mstime(); 1881 ri->role_reported = role; 1882 if (role == SRI_SLAVE) ri->slave_conf_change_time = mstime(); 1883 /* Log the event with +role-change if the new role is coherent or 1884 * with -role-change if there is a mismatch with the current config. */ 1885 sentinelEvent(REDIS_VERBOSE, 1886 ((ri->flags & (SRI_MASTER|SRI_SLAVE)) == role) ? 1887 "+role-change" : "-role-change", 1888 ri, "%@ new reported role is %s", 1889 role == SRI_MASTER ? "master" : "slave", 1890 ri->flags & SRI_MASTER ? "master" : "slave"); 1891 } 1892 1893 /* None of the following conditions are processed when in tilt mode, so 1894 * return asap. */ 1895 if (sentinel.tilt) return; 1896 1897 /* Handle master -> slave role switch. */ 1898 if ((ri->flags & SRI_MASTER) && role == SRI_SLAVE) { 1899 /* Nothing to do, but masters claiming to be slaves are 1900 * considered to be unreachable by Sentinel, so eventually 1901 * a failover will be triggered. */ 1902 } 1903 1904 /* Handle slave -> master role switch. */ 1905 if ((ri->flags & SRI_SLAVE) && role == SRI_MASTER) { 1906 /* If this is a promoted slave we can change state to the 1907 * failover state machine. */ 1908 if ((ri->flags & SRI_PROMOTED) && 1909 (ri->master->flags & SRI_FAILOVER_IN_PROGRESS) && 1910 (ri->master->failover_state == 1911 SENTINEL_FAILOVER_STATE_WAIT_PROMOTION)) 1912 { 1913 /* Now that we are sure the slave was reconfigured as a master 1914 * set the master configuration epoch to the epoch we won the 1915 * election to perform this failover. This will force the other 1916 * Sentinels to update their config (assuming there is not 1917 * a newer one already available). */ 1918 ri->master->config_epoch = ri->master->failover_epoch; 1919 ri->master->failover_state = SENTINEL_FAILOVER_STATE_RECONF_SLAVES; 1920 ri->master->failover_state_change_time = mstime(); 1921 sentinelFlushConfig(); 1922 sentinelEvent(REDIS_WARNING,"+promoted-slave",ri,"%@"); 1923 sentinelEvent(REDIS_WARNING,"+failover-state-reconf-slaves", 1924 ri->master,"%@"); 1925 sentinelCallClientReconfScript(ri->master,SENTINEL_LEADER, 1926 "start",ri->master->addr,ri->addr); 1927 sentinelForceHelloUpdateForMaster(ri->master); 1928 } else { 1929 /* A slave turned into a master. We want to force our view and 1930 * reconfigure as slave. Wait some time after the change before 1931 * going forward, to receive new configs if any. */ 1932 mstime_t wait_time = SENTINEL_PUBLISH_PERIOD*4; 1933 1934 if (!(ri->flags & SRI_PROMOTED) && 1935 sentinelMasterLooksSane(ri->master) && 1936 sentinelRedisInstanceNoDownFor(ri,wait_time) && 1937 mstime() - ri->role_reported_time > wait_time) 1938 { 1939 int retval = sentinelSendSlaveOf(ri, 1940 ri->master->addr->ip, 1941 ri->master->addr->port); 1942 if (retval == REDIS_OK) 1943 sentinelEvent(REDIS_NOTICE,"+convert-to-slave",ri,"%@"); 1944 } 1945 } 1946 } 1947 1948 /* Handle slaves replicating to a different master address. */ 1949 if ((ri->flags & SRI_SLAVE) && 1950 role == SRI_SLAVE && 1951 (ri->slave_master_port != ri->master->addr->port || 1952 strcasecmp(ri->slave_master_host,ri->master->addr->ip))) 1953 { 1954 mstime_t wait_time = ri->master->failover_timeout; 1955 1956 /* Make sure the master is sane before reconfiguring this instance 1957 * into a slave. */ 1958 if (sentinelMasterLooksSane(ri->master) && 1959 sentinelRedisInstanceNoDownFor(ri,wait_time) && 1960 mstime() - ri->slave_conf_change_time > wait_time) 1961 { 1962 int retval = sentinelSendSlaveOf(ri, 1963 ri->master->addr->ip, 1964 ri->master->addr->port); 1965 if (retval == REDIS_OK) 1966 sentinelEvent(REDIS_NOTICE,"+fix-slave-config",ri,"%@"); 1967 } 1968 } 1969 1970 /* Detect if the slave that is in the process of being reconfigured 1971 * changed state. */ 1972 if ((ri->flags & SRI_SLAVE) && role == SRI_SLAVE && 1973 (ri->flags & (SRI_RECONF_SENT|SRI_RECONF_INPROG))) 1974 { 1975 /* SRI_RECONF_SENT -> SRI_RECONF_INPROG. */ 1976 if ((ri->flags & SRI_RECONF_SENT) && 1977 ri->slave_master_host && 1978 strcmp(ri->slave_master_host, 1979 ri->master->promoted_slave->addr->ip) == 0 && 1980 ri->slave_master_port == ri->master->promoted_slave->addr->port) 1981 { 1982 ri->flags &= ~SRI_RECONF_SENT; 1983 ri->flags |= SRI_RECONF_INPROG; 1984 sentinelEvent(REDIS_NOTICE,"+slave-reconf-inprog",ri,"%@"); 1985 } 1986 1987 /* SRI_RECONF_INPROG -> SRI_RECONF_DONE */ 1988 if ((ri->flags & SRI_RECONF_INPROG) && 1989 ri->slave_master_link_status == SENTINEL_MASTER_LINK_STATUS_UP) 1990 { 1991 ri->flags &= ~SRI_RECONF_INPROG; 1992 ri->flags |= SRI_RECONF_DONE; 1993 sentinelEvent(REDIS_NOTICE,"+slave-reconf-done",ri,"%@"); 1994 } 1995 } 1996 } 1997 1998 void sentinelInfoReplyCallback(redisAsyncContext *c, void *reply, void *privdata) { 1999 sentinelRedisInstance *ri = c->data; 2000 redisReply *r; 2001 2002 if (ri) ri->pending_commands--; 2003 if (!reply || !ri) return; 2004 r = reply; 2005 2006 if (r->type == REDIS_REPLY_STRING) { 2007 sentinelRefreshInstanceInfo(ri,r->str); 2008 } 2009 } 2010 2011 /* Just discard the reply. We use this when we are not monitoring the return 2012 * value of the command but its effects directly. */ 2013 void sentinelDiscardReplyCallback(redisAsyncContext *c, void *reply, void *privdata) { 2014 sentinelRedisInstance *ri = c->data; 2015 2016 if (ri) ri->pending_commands--; 2017 } 2018 2019 void sentinelPingReplyCallback(redisAsyncContext *c, void *reply, void *privdata) { 2020 sentinelRedisInstance *ri = c->data; 2021 redisReply *r; 2022 2023 if (ri) ri->pending_commands--; 2024 if (!reply || !ri) return; 2025 r = reply; 2026 2027 if (r->type == REDIS_REPLY_STATUS || 2028 r->type == REDIS_REPLY_ERROR) { 2029 /* Update the "instance available" field only if this is an 2030 * acceptable reply. */ 2031 if (strncmp(r->str,"PONG",4) == 0 || 2032 strncmp(r->str,"LOADING",7) == 0 || 2033 strncmp(r->str,"MASTERDOWN",10) == 0) 2034 { 2035 ri->last_avail_time = mstime(); 2036 ri->last_ping_time = 0; /* Flag the pong as received. */ 2037 } else { 2038 /* Send a SCRIPT KILL command if the instance appears to be 2039 * down because of a busy script. */ 2040 if (strncmp(r->str,"BUSY",4) == 0 && 2041 (ri->flags & SRI_S_DOWN) && 2042 !(ri->flags & SRI_SCRIPT_KILL_SENT)) 2043 { 2044 if (redisAsyncCommand(ri->cc, 2045 sentinelDiscardReplyCallback, NULL, 2046 "SCRIPT KILL") == REDIS_OK) 2047 ri->pending_commands++; 2048 ri->flags |= SRI_SCRIPT_KILL_SENT; 2049 } 2050 } 2051 } 2052 ri->last_pong_time = mstime(); 2053 } 2054 2055 /* This is called when we get the reply about the PUBLISH command we send 2056 * to the master to advertise this sentinel. */ 2057 void sentinelPublishReplyCallback(redisAsyncContext *c, void *reply, void *privdata) { 2058 sentinelRedisInstance *ri = c->data; 2059 redisReply *r; 2060 2061 if (ri) ri->pending_commands--; 2062 if (!reply || !ri) return; 2063 r = reply; 2064 2065 /* Only update pub_time if we actually published our message. Otherwise 2066 * we'll retry again in 100 milliseconds. */ 2067 if (r->type != REDIS_REPLY_ERROR) 2068 ri->last_pub_time = mstime(); 2069 } 2070 2071 /* Process an hello message received via Pub/Sub in master or slave instance, 2072 * or sent directly to this sentinel via the (fake) PUBLISH command of Sentinel. 2073 * 2074 * If the master name specified in the message is not known, the message is 2075 * discareded. */ 2076 void sentinelProcessHelloMessage(char *hello, int hello_len) { 2077 /* Format is composed of 8 tokens: 2078 * 0=ip,1=port,2=runid,3=current_epoch,4=master_name, 2079 * 5=master_ip,6=master_port,7=master_config_epoch. */ 2080 int numtokens, port, removed, master_port; 2081 uint64_t current_epoch, master_config_epoch; 2082 char **token = sdssplitlen(hello, hello_len, ",", 1, &numtokens); 2083 sentinelRedisInstance *si, *master; 2084 2085 if (numtokens == 8) { 2086 /* Obtain a reference to the master this hello message is about */ 2087 master = sentinelGetMasterByName(token[4]); 2088 if (!master) goto cleanup; /* Unknown master, skip the message. */ 2089 2090 /* First, try to see if we already have this sentinel. */ 2091 port = atoi(token[1]); 2092 master_port = atoi(token[6]); 2093 si = getSentinelRedisInstanceByAddrAndRunID( 2094 master->sentinels,token[0],port,token[2]); 2095 current_epoch = strtoull(token[3],NULL,10); 2096 master_config_epoch = strtoull(token[7],NULL,10); 2097 2098 if (!si) { 2099 /* If not, remove all the sentinels that have the same runid 2100 * OR the same ip/port, because it's either a restart or a 2101 * network topology change. */ 2102 removed = removeMatchingSentinelsFromMaster(master,token[0],port, 2103 token[2]); 2104 if (removed) { 2105 sentinelEvent(REDIS_NOTICE,"-dup-sentinel",master, 2106 "%@ #duplicate of %s:%d or %s", 2107 token[0],port,token[2]); 2108 } 2109 2110 /* Add the new sentinel. */ 2111 si = createSentinelRedisInstance(NULL,SRI_SENTINEL, 2112 token[0],port,master->quorum,master); 2113 if (si) { 2114 sentinelEvent(REDIS_NOTICE,"+sentinel",si,"%@"); 2115 /* The runid is NULL after a new instance creation and 2116 * for Sentinels we don't have a later chance to fill it, 2117 * so do it now. */ 2118 si->runid = sdsnew(token[2]); 2119 sentinelFlushConfig(); 2120 } 2121 } 2122 2123 /* Update local current_epoch if received current_epoch is greater.*/ 2124 if (current_epoch > sentinel.current_epoch) { 2125 sentinel.current_epoch = current_epoch; 2126 sentinelFlushConfig(); 2127 sentinelEvent(REDIS_WARNING,"+new-epoch",master,"%llu", 2128 (unsigned long long) sentinel.current_epoch); 2129 } 2130 2131 /* Update master info if received configuration is newer. */ 2132 if (master->config_epoch < master_config_epoch) { 2133 master->config_epoch = master_config_epoch; 2134 if (master_port != master->addr->port || 2135 strcmp(master->addr->ip, token[5])) 2136 { 2137 sentinelAddr *old_addr; 2138 2139 sentinelEvent(REDIS_WARNING,"+config-update-from",si,"%@"); 2140 sentinelEvent(REDIS_WARNING,"+switch-master", 2141 master,"%s %s %d %s %d", 2142 master->name, 2143 master->addr->ip, master->addr->port, 2144 token[5], master_port); 2145 2146 old_addr = dupSentinelAddr(master->addr); 2147 sentinelResetMasterAndChangeAddress(master, token[5], master_port); 2148 sentinelCallClientReconfScript(master, 2149 SENTINEL_OBSERVER,"start", 2150 old_addr,master->addr); 2151 releaseSentinelAddr(old_addr); 2152 } 2153 } 2154 2155 /* Update the state of the Sentinel. */ 2156 if (si) si->last_hello_time = mstime(); 2157 } 2158 2159 cleanup: 2160 sdsfreesplitres(token,numtokens); 2161 } 2162 2163 2164 /* This is our Pub/Sub callback for the Hello channel. It's useful in order 2165 * to discover other sentinels attached at the same master. */ 2166 void sentinelReceiveHelloMessages(redisAsyncContext *c, void *reply, void *privdata) { 2167 sentinelRedisInstance *ri = c->data; 2168 redisReply *r; 2169 2170 if (!reply || !ri) return; 2171 r = reply; 2172 2173 /* Update the last activity in the pubsub channel. Note that since we 2174 * receive our messages as well this timestamp can be used to detect 2175 * if the link is probably disconnected even if it seems otherwise. */ 2176 ri->pc_last_activity = mstime(); 2177 2178 /* Sanity check in the reply we expect, so that the code that follows 2179 * can avoid to check for details. */ 2180 if (r->type != REDIS_REPLY_ARRAY || 2181 r->elements != 3 || 2182 r->element[0]->type != REDIS_REPLY_STRING || 2183 r->element[1]->type != REDIS_REPLY_STRING || 2184 r->element[2]->type != REDIS_REPLY_STRING || 2185 strcmp(r->element[0]->str,"message") != 0) return; 2186 2187 /* We are not interested in meeting ourselves */ 2188 if (strstr(r->element[2]->str,server.runid) != NULL) return; 2189 2190 sentinelProcessHelloMessage(r->element[2]->str, r->element[2]->len); 2191 } 2192 2193 /* Send an "Hello" message via Pub/Sub to the specified 'ri' Redis 2194 * instance in order to broadcast the current configuraiton for this 2195 * master, and to advertise the existence of this Sentinel at the same time. 2196 * 2197 * The message has the following format: 2198 * 2199 * sentinel_ip,sentinel_port,sentinel_runid,current_epoch, 2200 * master_name,master_ip,master_port,master_config_epoch. 2201 * 2202 * Returns REDIS_OK if the PUBLISH was queued correctly, otherwise 2203 * REDIS_ERR is returned. */ 2204 int sentinelSendHello(sentinelRedisInstance *ri) { 2205 char ip[REDIS_IP_STR_LEN]; 2206 char payload[REDIS_IP_STR_LEN+1024]; 2207 int retval; 2208 sentinelRedisInstance *master = (ri->flags & SRI_MASTER) ? ri : ri->master; 2209 sentinelAddr *master_addr = sentinelGetCurrentMasterAddress(master); 2210 2211 /* Try to obtain our own IP address. */ 2212 if (anetSockName(ri->cc->c.fd,ip,sizeof(ip),NULL) == -1) return REDIS_ERR; 2213 if (ri->flags & SRI_DISCONNECTED) return REDIS_ERR; 2214 2215 /* Format and send the Hello message. */ 2216 snprintf(payload,sizeof(payload), 2217 "%s,%d,%s,%llu," /* Info about this sentinel. */ 2218 "%s,%s,%d,%llu", /* Info about current master. */ 2219 ip, server.port, server.runid, 2220 (unsigned long long) sentinel.current_epoch, 2221 /* --- */ 2222 master->name,master_addr->ip,master_addr->port, 2223 (unsigned long long) master->config_epoch); 2224 retval = redisAsyncCommand(ri->cc, 2225 sentinelPublishReplyCallback, NULL, "PUBLISH %s %s", 2226 SENTINEL_HELLO_CHANNEL,payload); 2227 if (retval != REDIS_OK) return REDIS_ERR; 2228 ri->pending_commands++; 2229 return REDIS_OK; 2230 } 2231 2232 /* Reset last_pub_time in all the instances in the specified dictionary 2233 * in order to force the delivery of an Hello update ASAP. */ 2234 void sentinelForceHelloUpdateDictOfRedisInstances(dict *instances) { 2235 dictIterator *di; 2236 dictEntry *de; 2237 2238 di = dictGetSafeIterator(instances); 2239 while((de = dictNext(di)) != NULL) { 2240 sentinelRedisInstance *ri = dictGetVal(de); 2241 if (ri->last_pub_time >= (SENTINEL_PUBLISH_PERIOD+1)) 2242 ri->last_pub_time -= (SENTINEL_PUBLISH_PERIOD+1); 2243 } 2244 dictReleaseIterator(di); 2245 } 2246 2247 /* This function forces the delivery of an "Hello" message (see 2248 * sentinelSendHello() top comment for further information) to all the Redis 2249 * and Sentinel instances related to the specified 'master'. 2250 * 2251 * It is technically not needed since we send an update to every instance 2252 * with a period of SENTINEL_PUBLISH_PERIOD milliseconds, however when a 2253 * Sentinel upgrades a configuration it is a good idea to deliever an update 2254 * to the other Sentinels ASAP. */ 2255 int sentinelForceHelloUpdateForMaster(sentinelRedisInstance *master) { 2256 if (!(master->flags & SRI_MASTER)) return REDIS_ERR; 2257 if (master->last_pub_time >= (SENTINEL_PUBLISH_PERIOD+1)) 2258 master->last_pub_time -= (SENTINEL_PUBLISH_PERIOD+1); 2259 sentinelForceHelloUpdateDictOfRedisInstances(master->sentinels); 2260 sentinelForceHelloUpdateDictOfRedisInstances(master->slaves); 2261 return REDIS_OK; 2262 } 2263 2264 /* Send a PING to the specified instance and refresh the last_ping_time 2265 * if it is zero (that is, if we received a pong for the previous ping). 2266 * 2267 * On error zero is returned, and we can't consider the PING command 2268 * queued in the connection. */ 2269 int sentinelSendPing(sentinelRedisInstance *ri) { 2270 int retval = redisAsyncCommand(ri->cc, 2271 sentinelPingReplyCallback, NULL, "PING"); 2272 if (retval == REDIS_OK) { 2273 ri->pending_commands++; 2274 /* We update the ping time only if we received the pong for 2275 * the previous ping, otherwise we are technically waiting 2276 * since the first ping that did not received a reply. */ 2277 if (ri->last_ping_time == 0) ri->last_ping_time = mstime(); 2278 return 1; 2279 } else { 2280 return 0; 2281 } 2282 } 2283 2284 /* Send periodic PING, INFO, and PUBLISH to the Hello channel to 2285 * the specified master or slave instance. */ 2286 void sentinelSendPeriodicCommands(sentinelRedisInstance *ri) { 2287 mstime_t now = mstime(); 2288 mstime_t info_period, ping_period; 2289 int retval; 2290 2291 /* Return ASAP if we have already a PING or INFO already pending, or 2292 * in the case the instance is not properly connected. */ 2293 if (ri->flags & SRI_DISCONNECTED) return; 2294 2295 /* For INFO, PING, PUBLISH that are not critical commands to send we 2296 * also have a limit of SENTINEL_MAX_PENDING_COMMANDS. We don't 2297 * want to use a lot of memory just because a link is not working 2298 * properly (note that anyway there is a redundant protection about this, 2299 * that is, the link will be disconnected and reconnected if a long 2300 * timeout condition is detected. */ 2301 if (ri->pending_commands >= SENTINEL_MAX_PENDING_COMMANDS) return; 2302 2303 /* If this is a slave of a master in O_DOWN condition we start sending 2304 * it INFO every second, instead of the usual SENTINEL_INFO_PERIOD 2305 * period. In this state we want to closely monitor slaves in case they 2306 * are turned into masters by another Sentinel, or by the sysadmin. */ 2307 if ((ri->flags & SRI_SLAVE) && 2308 (ri->master->flags & (SRI_O_DOWN|SRI_FAILOVER_IN_PROGRESS))) { 2309 info_period = 1000; 2310 } else { 2311 info_period = SENTINEL_INFO_PERIOD; 2312 } 2313 2314 /* We ping instances every time the last received pong is older than 2315 * the configured 'down-after-milliseconds' time, but every second 2316 * anyway if 'down-after-milliseconds' is greater than 1 second. */ 2317 ping_period = ri->down_after_period; 2318 if (ping_period > SENTINEL_PING_PERIOD) ping_period = SENTINEL_PING_PERIOD; 2319 2320 if ((ri->flags & SRI_SENTINEL) == 0 && 2321 (ri->info_refresh == 0 || 2322 (now - ri->info_refresh) > info_period)) 2323 { 2324 /* Send INFO to masters and slaves, not sentinels. */ 2325 retval = redisAsyncCommand(ri->cc, 2326 sentinelInfoReplyCallback, NULL, "INFO"); 2327 if (retval == REDIS_OK) ri->pending_commands++; 2328 } else if ((now - ri->last_pong_time) > ping_period) { 2329 /* Send PING to all the three kinds of instances. */ 2330 sentinelSendPing(ri); 2331 } else if ((now - ri->last_pub_time) > SENTINEL_PUBLISH_PERIOD) { 2332 /* PUBLISH hello messages to all the three kinds of instances. */ 2333 sentinelSendHello(ri); 2334 } 2335 } 2336 2337 /* =========================== SENTINEL command ============================= */ 2338 2339 const char *sentinelFailoverStateStr(int state) { 2340 switch(state) { 2341 case SENTINEL_FAILOVER_STATE_NONE: return "none"; 2342 case SENTINEL_FAILOVER_STATE_WAIT_START: return "wait_start"; 2343 case SENTINEL_FAILOVER_STATE_SELECT_SLAVE: return "select_slave"; 2344 case SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE: return "send_slaveof_noone"; 2345 case SENTINEL_FAILOVER_STATE_WAIT_PROMOTION: return "wait_promotion"; 2346 case SENTINEL_FAILOVER_STATE_RECONF_SLAVES: return "reconf_slaves"; 2347 case SENTINEL_FAILOVER_STATE_UPDATE_CONFIG: return "update_config"; 2348 default: return "unknown"; 2349 } 2350 } 2351 2352 /* Redis instance to Redis protocol representation. */ 2353 void addReplySentinelRedisInstance(redisClient *c, sentinelRedisInstance *ri) { 2354 char *flags = sdsempty(); 2355 void *mbl; 2356 int fields = 0; 2357 2358 mbl = addDeferredMultiBulkLength(c); 2359 2360 addReplyBulkCString(c,"name"); 2361 addReplyBulkCString(c,ri->name); 2362 fields++; 2363 2364 addReplyBulkCString(c,"ip"); 2365 addReplyBulkCString(c,ri->addr->ip); 2366 fields++; 2367 2368 addReplyBulkCString(c,"port"); 2369 addReplyBulkLongLong(c,ri->addr->port); 2370 fields++; 2371 2372 addReplyBulkCString(c,"runid"); 2373 addReplyBulkCString(c,ri->runid ? ri->runid : ""); 2374 fields++; 2375 2376 addReplyBulkCString(c,"flags"); 2377 if (ri->flags & SRI_S_DOWN) flags = sdscat(flags,"s_down,"); 2378 if (ri->flags & SRI_O_DOWN) flags = sdscat(flags,"o_down,"); 2379 if (ri->flags & SRI_MASTER) flags = sdscat(flags,"master,"); 2380 if (ri->flags & SRI_SLAVE) flags = sdscat(flags,"slave,"); 2381 if (ri->flags & SRI_SENTINEL) flags = sdscat(flags,"sentinel,"); 2382 if (ri->flags & SRI_DISCONNECTED) flags = sdscat(flags,"disconnected,"); 2383 if (ri->flags & SRI_MASTER_DOWN) flags = sdscat(flags,"master_down,"); 2384 if (ri->flags & SRI_FAILOVER_IN_PROGRESS) 2385 flags = sdscat(flags,"failover_in_progress,"); 2386 if (ri->flags & SRI_PROMOTED) flags = sdscat(flags,"promoted,"); 2387 if (ri->flags & SRI_RECONF_SENT) flags = sdscat(flags,"reconf_sent,"); 2388 if (ri->flags & SRI_RECONF_INPROG) flags = sdscat(flags,"reconf_inprog,"); 2389 if (ri->flags & SRI_RECONF_DONE) flags = sdscat(flags,"reconf_done,"); 2390 2391 if (sdslen(flags) != 0) sdsrange(flags,0,-2); /* remove last "," */ 2392 addReplyBulkCString(c,flags); 2393 sdsfree(flags); 2394 fields++; 2395 2396 addReplyBulkCString(c,"pending-commands"); 2397 addReplyBulkLongLong(c,ri->pending_commands); 2398 fields++; 2399 2400 if (ri->flags & SRI_FAILOVER_IN_PROGRESS) { 2401 addReplyBulkCString(c,"failover-state"); 2402 addReplyBulkCString(c,(char*)sentinelFailoverStateStr(ri->failover_state)); 2403 fields++; 2404 } 2405 2406 addReplyBulkCString(c,"last-ping-sent"); 2407 addReplyBulkLongLong(c, 2408 ri->last_ping_time ? (mstime() - ri->last_ping_time) : 0); 2409 fields++; 2410 2411 addReplyBulkCString(c,"last-ok-ping-reply"); 2412 addReplyBulkLongLong(c,mstime() - ri->last_avail_time); 2413 fields++; 2414 2415 addReplyBulkCString(c,"last-ping-reply"); 2416 addReplyBulkLongLong(c,mstime() - ri->last_pong_time); 2417 fields++; 2418 2419 if (ri->flags & SRI_S_DOWN) { 2420 addReplyBulkCString(c,"s-down-time"); 2421 addReplyBulkLongLong(c,mstime()-ri->s_down_since_time); 2422 fields++; 2423 } 2424 2425 if (ri->flags & SRI_O_DOWN) { 2426 addReplyBulkCString(c,"o-down-time"); 2427 addReplyBulkLongLong(c,mstime()-ri->o_down_since_time); 2428 fields++; 2429 } 2430 2431 addReplyBulkCString(c,"down-after-milliseconds"); 2432 addReplyBulkLongLong(c,ri->down_after_period); 2433 fields++; 2434 2435 /* Masters and Slaves */ 2436 if (ri->flags & (SRI_MASTER|SRI_SLAVE)) { 2437 addReplyBulkCString(c,"info-refresh"); 2438 addReplyBulkLongLong(c,mstime() - ri->info_refresh); 2439 fields++; 2440 2441 addReplyBulkCString(c,"role-reported"); 2442 addReplyBulkCString(c, (ri->role_reported == SRI_MASTER) ? "master" : 2443 "slave"); 2444 fields++; 2445 2446 addReplyBulkCString(c,"role-reported-time"); 2447 addReplyBulkLongLong(c,mstime() - ri->role_reported_time); 2448 fields++; 2449 } 2450 2451 /* Only masters */ 2452 if (ri->flags & SRI_MASTER) { 2453 addReplyBulkCString(c,"config-epoch"); 2454 addReplyBulkLongLong(c,ri->config_epoch); 2455 fields++; 2456 2457 addReplyBulkCString(c,"num-slaves"); 2458 addReplyBulkLongLong(c,dictSize(ri->slaves)); 2459 fields++; 2460 2461 addReplyBulkCString(c,"num-other-sentinels"); 2462 addReplyBulkLongLong(c,dictSize(ri->sentinels)); 2463 fields++; 2464 2465 addReplyBulkCString(c,"quorum"); 2466 addReplyBulkLongLong(c,ri->quorum); 2467 fields++; 2468 2469 addReplyBulkCString(c,"failover-timeout"); 2470 addReplyBulkLongLong(c,ri->failover_timeout); 2471 fields++; 2472 2473 addReplyBulkCString(c,"parallel-syncs"); 2474 addReplyBulkLongLong(c,ri->parallel_syncs); 2475 fields++; 2476 2477 if (ri->notification_script) { 2478 addReplyBulkCString(c,"notification-script"); 2479 addReplyBulkCString(c,ri->notification_script); 2480 fields++; 2481 } 2482 2483 if (ri->client_reconfig_script) { 2484 addReplyBulkCString(c,"client-reconfig-script"); 2485 addReplyBulkCString(c,ri->client_reconfig_script); 2486 fields++; 2487 } 2488 } 2489 2490 /* Only slaves */ 2491 if (ri->flags & SRI_SLAVE) { 2492 addReplyBulkCString(c,"master-link-down-time"); 2493 addReplyBulkLongLong(c,ri->master_link_down_time); 2494 fields++; 2495 2496 addReplyBulkCString(c,"master-link-status"); 2497 addReplyBulkCString(c, 2498 (ri->slave_master_link_status == SENTINEL_MASTER_LINK_STATUS_UP) ? 2499 "ok" : "err"); 2500 fields++; 2501 2502 addReplyBulkCString(c,"master-host"); 2503 addReplyBulkCString(c, 2504 ri->slave_master_host ? ri->slave_master_host : "?"); 2505 fields++; 2506 2507 addReplyBulkCString(c,"master-port"); 2508 addReplyBulkLongLong(c,ri->slave_master_port); 2509 fields++; 2510 2511 addReplyBulkCString(c,"slave-priority"); 2512 addReplyBulkLongLong(c,ri->slave_priority); 2513 fields++; 2514 2515 addReplyBulkCString(c,"slave-repl-offset"); 2516 addReplyBulkLongLong(c,ri->slave_repl_offset); 2517 fields++; 2518 } 2519 2520 /* Only sentinels */ 2521 if (ri->flags & SRI_SENTINEL) { 2522 addReplyBulkCString(c,"last-hello-message"); 2523 addReplyBulkLongLong(c,mstime() - ri->last_hello_time); 2524 fields++; 2525 2526 addReplyBulkCString(c,"voted-leader"); 2527 addReplyBulkCString(c,ri->leader ? ri->leader : "?"); 2528 fields++; 2529 2530 addReplyBulkCString(c,"voted-leader-epoch"); 2531 addReplyBulkLongLong(c,ri->leader_epoch); 2532 fields++; 2533 } 2534 2535 setDeferredMultiBulkLength(c,mbl,fields*2); 2536 } 2537 2538 /* Output a number of instances contained inside a dictionary as 2539 * Redis protocol. */ 2540 void addReplyDictOfRedisInstances(redisClient *c, dict *instances) { 2541 dictIterator *di; 2542 dictEntry *de; 2543 2544 di = dictGetIterator(instances); 2545 addReplyMultiBulkLen(c,dictSize(instances)); 2546 while((de = dictNext(di)) != NULL) { 2547 sentinelRedisInstance *ri = dictGetVal(de); 2548 2549 addReplySentinelRedisInstance(c,ri); 2550 } 2551 dictReleaseIterator(di); 2552 } 2553 2554 /* Lookup the named master into sentinel.masters. 2555 * If the master is not found reply to the client with an error and returns 2556 * NULL. */ 2557 sentinelRedisInstance *sentinelGetMasterByNameOrReplyError(redisClient *c, 2558 robj *name) 2559 { 2560 sentinelRedisInstance *ri; 2561 2562 ri = dictFetchValue(sentinel.masters,c->argv[2]->ptr); 2563 if (!ri) { 2564 addReplyError(c,"No such master with that name"); 2565 return NULL; 2566 } 2567 return ri; 2568 } 2569 2570 void sentinelCommand(redisClient *c) { 2571 if (!strcasecmp(c->argv[1]->ptr,"masters")) { 2572 /* SENTINEL MASTERS */ 2573 if (c->argc != 2) goto numargserr; 2574 addReplyDictOfRedisInstances(c,sentinel.masters); 2575 } else if (!strcasecmp(c->argv[1]->ptr,"master")) { 2576 /* SENTINEL MASTER <name> */ 2577 sentinelRedisInstance *ri; 2578 2579 if (c->argc != 3) goto numargserr; 2580 if ((ri = sentinelGetMasterByNameOrReplyError(c,c->argv[2])) 2581 == NULL) return; 2582 addReplySentinelRedisInstance(c,ri); 2583 } else if (!strcasecmp(c->argv[1]->ptr,"slaves")) { 2584 /* SENTINEL SLAVES <master-name> */ 2585 sentinelRedisInstance *ri; 2586 2587 if (c->argc != 3) goto numargserr; 2588 if ((ri = sentinelGetMasterByNameOrReplyError(c,c->argv[2])) == NULL) 2589 return; 2590 addReplyDictOfRedisInstances(c,ri->slaves); 2591 } else if (!strcasecmp(c->argv[1]->ptr,"sentinels")) { 2592 /* SENTINEL SENTINELS <master-name> */ 2593 sentinelRedisInstance *ri; 2594 2595 if (c->argc != 3) goto numargserr; 2596 if ((ri = sentinelGetMasterByNameOrReplyError(c,c->argv[2])) == NULL) 2597 return; 2598 addReplyDictOfRedisInstances(c,ri->sentinels); 2599 } else if (!strcasecmp(c->argv[1]->ptr,"is-master-down-by-addr")) { 2600 /* SENTINEL IS-MASTER-DOWN-BY-ADDR <ip> <port> <current-epoch> <runid>*/ 2601 sentinelRedisInstance *ri; 2602 long long req_epoch; 2603 uint64_t leader_epoch = 0; 2604 char *leader = NULL; 2605 long port; 2606 int isdown = 0; 2607 2608 if (c->argc != 6) goto numargserr; 2609 if (getLongFromObjectOrReply(c,c->argv[3],&port,NULL) != REDIS_OK || 2610 getLongLongFromObjectOrReply(c,c->argv[4],&req_epoch,NULL) 2611 != REDIS_OK) 2612 return; 2613 ri = getSentinelRedisInstanceByAddrAndRunID(sentinel.masters, 2614 c->argv[2]->ptr,port,NULL); 2615 2616 /* It exists? Is actually a master? Is subjectively down? It's down. 2617 * Note: if we are in tilt mode we always reply with "0". */ 2618 if (!sentinel.tilt && ri && (ri->flags & SRI_S_DOWN) && 2619 (ri->flags & SRI_MASTER)) 2620 isdown = 1; 2621 2622 /* Vote for the master (or fetch the previous vote) if the request 2623 * includes a runid, otherwise the sender is not seeking for a vote. */ 2624 if (ri && ri->flags & SRI_MASTER && strcasecmp(c->argv[5]->ptr,"*")) { 2625 leader = sentinelVoteLeader(ri,(uint64_t)req_epoch, 2626 c->argv[5]->ptr, 2627 &leader_epoch); 2628 } 2629 2630 /* Reply with a three-elements multi-bulk reply: 2631 * down state, leader, vote epoch. */ 2632 addReplyMultiBulkLen(c,3); 2633 addReply(c, isdown ? shared.cone : shared.czero); 2634 addReplyBulkCString(c, leader ? leader : "*"); 2635 addReplyLongLong(c, (long long)leader_epoch); 2636 if (leader) sdsfree(leader); 2637 } else if (!strcasecmp(c->argv[1]->ptr,"reset")) { 2638 /* SENTINEL RESET <pattern> */ 2639 if (c->argc != 3) goto numargserr; 2640 addReplyLongLong(c,sentinelResetMastersByPattern(c->argv[2]->ptr,SENTINEL_GENERATE_EVENT)); 2641 } else if (!strcasecmp(c->argv[1]->ptr,"get-master-addr-by-name")) { 2642 /* SENTINEL GET-MASTER-ADDR-BY-NAME <master-name> */ 2643 sentinelRedisInstance *ri; 2644 2645 if (c->argc != 3) goto numargserr; 2646 ri = sentinelGetMasterByName(c->argv[2]->ptr); 2647 if (ri == NULL) { 2648 addReply(c,shared.nullmultibulk); 2649 } else { 2650 sentinelAddr *addr = sentinelGetCurrentMasterAddress(ri); 2651 2652 addReplyMultiBulkLen(c,2); 2653 addReplyBulkCString(c,addr->ip); 2654 addReplyBulkLongLong(c,addr->port); 2655 } 2656 } else if (!strcasecmp(c->argv[1]->ptr,"failover")) { 2657 /* SENTINEL FAILOVER <master-name> */ 2658 sentinelRedisInstance *ri; 2659 2660 if (c->argc != 3) goto numargserr; 2661 if ((ri = sentinelGetMasterByNameOrReplyError(c,c->argv[2])) == NULL) 2662 return; 2663 if (ri->flags & SRI_FAILOVER_IN_PROGRESS) { 2664 addReplySds(c,sdsnew("-INPROG Failover already in progress\r\n")); 2665 return; 2666 } 2667 if (sentinelSelectSlave(ri) == NULL) { 2668 addReplySds(c,sdsnew("-NOGOODSLAVE No suitable slave to promote\r\n")); 2669 return; 2670 } 2671 redisLog(REDIS_WARNING,"Executing user requested FAILOVER of '%s'", 2672 ri->name); 2673 sentinelStartFailover(ri); 2674 ri->flags |= SRI_FORCE_FAILOVER; 2675 addReply(c,shared.ok); 2676 } else if (!strcasecmp(c->argv[1]->ptr,"pending-scripts")) { 2677 /* SENTINEL PENDING-SCRIPTS */ 2678 2679 if (c->argc != 2) goto numargserr; 2680 sentinelPendingScriptsCommand(c); 2681 } else if (!strcasecmp(c->argv[1]->ptr,"monitor")) { 2682 /* SENTINEL MONITOR <name> <ip> <port> <quorum> */ 2683 sentinelRedisInstance *ri; 2684 long quorum, port; 2685 char buf[32]; 2686 2687 if (c->argc != 6) goto numargserr; 2688 if (getLongFromObjectOrReply(c,c->argv[5],&quorum,"Invalid quorum") 2689 != REDIS_OK) return; 2690 if (getLongFromObjectOrReply(c,c->argv[4],&port,"Invalid port") 2691 != REDIS_OK) return; 2692 /* Make sure the IP field is actually a valid IP before passing it 2693 * to createSentinelRedisInstance(), otherwise we may trigger a 2694 * DNS lookup at runtime. */ 2695 if (anetResolveIP(NULL,c->argv[3]->ptr,buf,sizeof(buf)) == ANET_ERR) { 2696 addReplyError(c,"Invalid IP address specified"); 2697 return; 2698 } 2699 2700 /* Parameters are valid. Try to create the master instance. */ 2701 ri = createSentinelRedisInstance(c->argv[2]->ptr,SRI_MASTER, 2702 c->argv[3]->ptr,port,quorum,NULL); 2703 if (ri == NULL) { 2704 switch(errno) { 2705 case EBUSY: 2706 addReplyError(c,"Duplicated master name"); 2707 break; 2708 case EINVAL: 2709 addReplyError(c,"Invalid port number"); 2710 break; 2711 default: 2712 addReplyError(c,"Unspecified error adding the instance"); 2713 break; 2714 } 2715 } else { 2716 sentinelFlushConfig(); 2717 sentinelEvent(REDIS_WARNING,"+monitor",ri,"%@ quorum %d",ri->quorum); 2718 addReply(c,shared.ok); 2719 } 2720 } else if (!strcasecmp(c->argv[1]->ptr,"remove")) { 2721 /* SENTINEL REMOVE <name> */ 2722 sentinelRedisInstance *ri; 2723 2724 if ((ri = sentinelGetMasterByNameOrReplyError(c,c->argv[2])) 2725 == NULL) return; 2726 sentinelEvent(REDIS_WARNING,"-monitor",ri,"%@"); 2727 dictDelete(sentinel.masters,c->argv[2]->ptr); 2728 sentinelFlushConfig(); 2729 addReply(c,shared.ok); 2730 } else if (!strcasecmp(c->argv[1]->ptr,"set")) { 2731 if (c->argc < 3 || c->argc % 2 == 0) goto numargserr; 2732 sentinelSetCommand(c); 2733 } else { 2734 addReplyErrorFormat(c,"Unknown sentinel subcommand '%s'", 2735 (char*)c->argv[1]->ptr); 2736 } 2737 return; 2738 2739 numargserr: 2740 addReplyErrorFormat(c,"Wrong number of arguments for 'sentinel %s'", 2741 (char*)c->argv[1]->ptr); 2742 } 2743 2744 /* SENTINEL INFO [section] */ 2745 void sentinelInfoCommand(redisClient *c) { 2746 char *section = c->argc == 2 ? c->argv[1]->ptr : "default"; 2747 sds info = sdsempty(); 2748 int defsections = !strcasecmp(section,"default"); 2749 int sections = 0; 2750 2751 if (c->argc > 2) { 2752 addReply(c,shared.syntaxerr); 2753 return; 2754 } 2755 2756 if (!strcasecmp(section,"server") || defsections) { 2757 if (sections++) info = sdscat(info,"\r\n"); 2758 sds serversection = genRedisInfoString("server"); 2759 info = sdscatlen(info,serversection,sdslen(serversection)); 2760 sdsfree(serversection); 2761 } 2762 2763 if (!strcasecmp(section,"sentinel") || defsections) { 2764 dictIterator *di; 2765 dictEntry *de; 2766 int master_id = 0; 2767 2768 if (sections++) info = sdscat(info,"\r\n"); 2769 info = sdscatprintf(info, 2770 "# Sentinel\r\n" 2771 "sentinel_masters:%lu\r\n" 2772 "sentinel_tilt:%d\r\n" 2773 "sentinel_running_scripts:%d\r\n" 2774 "sentinel_scripts_queue_length:%ld\r\n", 2775 dictSize(sentinel.masters), 2776 sentinel.tilt, 2777 sentinel.running_scripts, 2778 listLength(sentinel.scripts_queue)); 2779 2780 di = dictGetIterator(sentinel.masters); 2781 while((de = dictNext(di)) != NULL) { 2782 sentinelRedisInstance *ri = dictGetVal(de); 2783 char *status = "ok"; 2784 2785 if (ri->flags & SRI_O_DOWN) status = "odown"; 2786 else if (ri->flags & SRI_S_DOWN) status = "sdown"; 2787 info = sdscatprintf(info, 2788 "master%d:name=%s,status=%s,address=%s:%d," 2789 "slaves=%lu,sentinels=%lu\r\n", 2790 master_id++, ri->name, status, 2791 ri->addr->ip, ri->addr->port, 2792 dictSize(ri->slaves), 2793 dictSize(ri->sentinels)+1); 2794 } 2795 dictReleaseIterator(di); 2796 } 2797 2798 addReplySds(c,sdscatprintf(sdsempty(),"$%lu\r\n", 2799 (unsigned long)sdslen(info))); 2800 addReplySds(c,info); 2801 addReply(c,shared.crlf); 2802 } 2803 2804 /* Implements Sentinel verison of the ROLE command. The output is 2805 * "sentinel" and the list of currently monitored master names. */ 2806 void sentinelRoleCommand(redisClient *c) { 2807 dictIterator *di; 2808 dictEntry *de; 2809 2810 addReplyMultiBulkLen(c,2); 2811 addReplyBulkCBuffer(c,"sentinel",8); 2812 addReplyMultiBulkLen(c,dictSize(sentinel.masters)); 2813 2814 di = dictGetIterator(sentinel.masters); 2815 while((de = dictNext(di)) != NULL) { 2816 sentinelRedisInstance *ri = dictGetVal(de); 2817 2818 addReplyBulkCString(c,ri->name); 2819 } 2820 dictReleaseIterator(di); 2821 } 2822 2823 /* SENTINEL SET <mastername> [<option> <value> ...] */ 2824 void sentinelSetCommand(redisClient *c) { 2825 sentinelRedisInstance *ri; 2826 int j, changes = 0; 2827 char *option, *value; 2828 2829 if ((ri = sentinelGetMasterByNameOrReplyError(c,c->argv[2])) 2830 == NULL) return; 2831 2832 /* Process option - value pairs. */ 2833 for (j = 3; j < c->argc; j += 2) { 2834 option = c->argv[j]->ptr; 2835 value = c->argv[j+1]->ptr; 2836 robj *o = c->argv[j+1]; 2837 long long ll; 2838 2839 if (!strcasecmp(option,"down-after-milliseconds")) { 2840 /* down-after-millisecodns <milliseconds> */ 2841 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0) 2842 goto badfmt; 2843 ri->down_after_period = ll; 2844 sentinelPropagateDownAfterPeriod(ri); 2845 changes++; 2846 } else if (!strcasecmp(option,"failover-timeout")) { 2847 /* failover-timeout <milliseconds> */ 2848 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0) 2849 goto badfmt; 2850 ri->failover_timeout = ll; 2851 changes++; 2852 } else if (!strcasecmp(option,"parallel-syncs")) { 2853 /* parallel-syncs <milliseconds> */ 2854 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0) 2855 goto badfmt; 2856 ri->parallel_syncs = ll; 2857 changes++; 2858 } else if (!strcasecmp(option,"notification-script")) { 2859 /* notification-script <path> */ 2860 if (strlen(value) && access(value,X_OK) == -1) { 2861 addReplyError(c, 2862 "Notification script seems non existing or non executable"); 2863 if (changes) sentinelFlushConfig(); 2864 return; 2865 } 2866 sdsfree(ri->notification_script); 2867 ri->notification_script = strlen(value) ? sdsnew(value) : NULL; 2868 changes++; 2869 } else if (!strcasecmp(option,"client-reconfig-script")) { 2870 /* client-reconfig-script <path> */ 2871 if (strlen(value) && access(value,X_OK) == -1) { 2872 addReplyError(c, 2873 "Client reconfiguration script seems non existing or " 2874 "non executable"); 2875 if (changes) sentinelFlushConfig(); 2876 return; 2877 } 2878 sdsfree(ri->client_reconfig_script); 2879 ri->client_reconfig_script = strlen(value) ? sdsnew(value) : NULL; 2880 changes++; 2881 } else if (!strcasecmp(option,"auth-pass")) { 2882 /* auth-pass <password> */ 2883 sdsfree(ri->auth_pass); 2884 ri->auth_pass = strlen(value) ? sdsnew(value) : NULL; 2885 changes++; 2886 } else if (!strcasecmp(option,"quorum")) { 2887 /* quorum <count> */ 2888 if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll <= 0) 2889 goto badfmt; 2890 ri->quorum = ll; 2891 changes++; 2892 } else { 2893 addReplyErrorFormat(c,"Unknown option '%s' for SENTINEL SET", 2894 option); 2895 if (changes) sentinelFlushConfig(); 2896 return; 2897 } 2898 sentinelEvent(REDIS_WARNING,"+set",ri,"%@ %s %s",option,value); 2899 } 2900 2901 if (changes) sentinelFlushConfig(); 2902 addReply(c,shared.ok); 2903 return; 2904 2905 badfmt: /* Bad format errors */ 2906 if (changes) sentinelFlushConfig(); 2907 addReplyErrorFormat(c,"Invalid argument '%s' for SENTINEL SET '%s'", 2908 value, option); 2909 } 2910 2911 /* Our fake PUBLISH command: it is actually useful only to receive hello messages 2912 * from the other sentinel instances, and publishing to a channel other than 2913 * SENTINEL_HELLO_CHANNEL is forbidden. 2914 * 2915 * Because we have a Sentinel PUBLISH, the code to send hello messages is the same 2916 * for all the three kind of instances: masters, slaves, sentinels. */ 2917 void sentinelPublishCommand(redisClient *c) { 2918 if (strcmp(c->argv[1]->ptr,SENTINEL_HELLO_CHANNEL)) { 2919 addReplyError(c, "Only HELLO messages are accepted by Sentinel instances."); 2920 return; 2921 } 2922 sentinelProcessHelloMessage(c->argv[2]->ptr,sdslen(c->argv[2]->ptr)); 2923 addReplyLongLong(c,1); 2924 } 2925 2926 /* ===================== SENTINEL availability checks ======================= */ 2927 2928 /* Is this instance down from our point of view? */ 2929 void sentinelCheckSubjectivelyDown(sentinelRedisInstance *ri) { 2930 mstime_t elapsed = 0; 2931 2932 if (ri->last_ping_time) 2933 elapsed = mstime() - ri->last_ping_time; 2934 2935 /* Check if we are in need for a reconnection of one of the 2936 * links, because we are detecting low activity. 2937 * 2938 * 1) Check if the command link seems connected, was connected not less 2939 * than SENTINEL_MIN_LINK_RECONNECT_PERIOD, but still we have a 2940 * pending ping for more than half the timeout. */ 2941 if (ri->cc && 2942 (mstime() - ri->cc_conn_time) > SENTINEL_MIN_LINK_RECONNECT_PERIOD && 2943 ri->last_ping_time != 0 && /* Ther is a pending ping... */ 2944 /* The pending ping is delayed, and we did not received 2945 * error replies as well. */ 2946 (mstime() - ri->last_ping_time) > (ri->down_after_period/2) && 2947 (mstime() - ri->last_pong_time) > (ri->down_after_period/2)) 2948 { 2949 sentinelKillLink(ri,ri->cc); 2950 } 2951 2952 /* 2) Check if the pubsub link seems connected, was connected not less 2953 * than SENTINEL_MIN_LINK_RECONNECT_PERIOD, but still we have no 2954 * activity in the Pub/Sub channel for more than 2955 * SENTINEL_PUBLISH_PERIOD * 3. 2956 */ 2957 if (ri->pc && 2958 (mstime() - ri->pc_conn_time) > SENTINEL_MIN_LINK_RECONNECT_PERIOD && 2959 (mstime() - ri->pc_last_activity) > (SENTINEL_PUBLISH_PERIOD*3)) 2960 { 2961 sentinelKillLink(ri,ri->pc); 2962 } 2963 2964 /* Update the SDOWN flag. We believe the instance is SDOWN if: 2965 * 2966 * 1) It is not replying. 2967 * 2) We believe it is a master, it reports to be a slave for enough time 2968 * to meet the down_after_period, plus enough time to get two times 2969 * INFO report from the instance. */ 2970 if (elapsed > ri->down_after_period || 2971 (ri->flags & SRI_MASTER && 2972 ri->role_reported == SRI_SLAVE && 2973 mstime() - ri->role_reported_time > 2974 (ri->down_after_period+SENTINEL_INFO_PERIOD*2))) 2975 { 2976 /* Is subjectively down */ 2977 if ((ri->flags & SRI_S_DOWN) == 0) { 2978 sentinelEvent(REDIS_WARNING,"+sdown",ri,"%@"); 2979 ri->s_down_since_time = mstime(); 2980 ri->flags |= SRI_S_DOWN; 2981 } 2982 } else { 2983 /* Is subjectively up */ 2984 if (ri->flags & SRI_S_DOWN) { 2985 sentinelEvent(REDIS_WARNING,"-sdown",ri,"%@"); 2986 ri->flags &= ~(SRI_S_DOWN|SRI_SCRIPT_KILL_SENT); 2987 } 2988 } 2989 } 2990 2991 /* Is this instance down according to the configured quorum? 2992 * 2993 * Note that ODOWN is a weak quorum, it only means that enough Sentinels 2994 * reported in a given time range that the instance was not reachable. 2995 * However messages can be delayed so there are no strong guarantees about 2996 * N instances agreeing at the same time about the down state. */ 2997 void sentinelCheckObjectivelyDown(sentinelRedisInstance *master) { 2998 dictIterator *di; 2999 dictEntry *de; 3000 int quorum = 0, odown = 0; 3001 3002 if (master->flags & SRI_S_DOWN) { 3003 /* Is down for enough sentinels? */ 3004 quorum = 1; /* the current sentinel. */ 3005 /* Count all the other sentinels. */ 3006 di = dictGetIterator(master->sentinels); 3007 while((de = dictNext(di)) != NULL) { 3008 sentinelRedisInstance *ri = dictGetVal(de); 3009 3010 if (ri->flags & SRI_MASTER_DOWN) quorum++; 3011 } 3012 dictReleaseIterator(di); 3013 if (quorum >= master->quorum) odown = 1; 3014 } 3015 3016 /* Set the flag accordingly to the outcome. */ 3017 if (odown) { 3018 if ((master->flags & SRI_O_DOWN) == 0) { 3019 sentinelEvent(REDIS_WARNING,"+odown",master,"%@ #quorum %d/%d", 3020 quorum, master->quorum); 3021 master->flags |= SRI_O_DOWN; 3022 master->o_down_since_time = mstime(); 3023 } 3024 } else { 3025 if (master->flags & SRI_O_DOWN) { 3026 sentinelEvent(REDIS_WARNING,"-odown",master,"%@"); 3027 master->flags &= ~SRI_O_DOWN; 3028 } 3029 } 3030 } 3031 3032 /* Receive the SENTINEL is-master-down-by-addr reply, see the 3033 * sentinelAskMasterStateToOtherSentinels() function for more information. */ 3034 void sentinelReceiveIsMasterDownReply(redisAsyncContext *c, void *reply, void *privdata) { 3035 sentinelRedisInstance *ri = c->data; 3036 redisReply *r; 3037 3038 if (ri) ri->pending_commands--; 3039 if (!reply || !ri) return; 3040 r = reply; 3041 3042 /* Ignore every error or unexpected reply. 3043 * Note that if the command returns an error for any reason we'll 3044 * end clearing the SRI_MASTER_DOWN flag for timeout anyway. */ 3045 if (r->type == REDIS_REPLY_ARRAY && r->elements == 3 && 3046 r->element[0]->type == REDIS_REPLY_INTEGER && 3047 r->element[1]->type == REDIS_REPLY_STRING && 3048 r->element[2]->type == REDIS_REPLY_INTEGER) 3049 { 3050 ri->last_master_down_reply_time = mstime(); 3051 if (r->element[0]->integer == 1) { 3052 ri->flags |= SRI_MASTER_DOWN; 3053 } else { 3054 ri->flags &= ~SRI_MASTER_DOWN; 3055 } 3056 if (strcmp(r->element[1]->str,"*")) { 3057 /* If the runid in the reply is not "*" the Sentinel actually 3058 * replied with a vote. */ 3059 sdsfree(ri->leader); 3060 if (ri->leader_epoch != r->element[2]->integer) 3061 redisLog(REDIS_WARNING, 3062 "%s voted for %s %llu", ri->name, 3063 r->element[1]->str, 3064 (unsigned long long) r->element[2]->integer); 3065 ri->leader = sdsnew(r->element[1]->str); 3066 ri->leader_epoch = r->element[2]->integer; 3067 } 3068 } 3069 } 3070 3071 /* If we think the master is down, we start sending 3072 * SENTINEL IS-MASTER-DOWN-BY-ADDR requests to other sentinels 3073 * in order to get the replies that allow to reach the quorum 3074 * needed to mark the master in ODOWN state and trigger a failover. */ 3075 #define SENTINEL_ASK_FORCED (1<<0) 3076 void sentinelAskMasterStateToOtherSentinels(sentinelRedisInstance *master, int flags) { 3077 dictIterator *di; 3078 dictEntry *de; 3079 3080 di = dictGetIterator(master->sentinels); 3081 while((de = dictNext(di)) != NULL) { 3082 sentinelRedisInstance *ri = dictGetVal(de); 3083 mstime_t elapsed = mstime() - ri->last_master_down_reply_time; 3084 char port[32]; 3085 int retval; 3086 3087 /* If the master state from other sentinel is too old, we clear it. */ 3088 if (elapsed > SENTINEL_ASK_PERIOD*5) { 3089 ri->flags &= ~SRI_MASTER_DOWN; 3090 sdsfree(ri->leader); 3091 ri->leader = NULL; 3092 } 3093 3094 /* Only ask if master is down to other sentinels if: 3095 * 3096 * 1) We believe it is down, or there is a failover in progress. 3097 * 2) Sentinel is connected. 3098 * 3) We did not received the info within SENTINEL_ASK_PERIOD ms. */ 3099 if ((master->flags & SRI_S_DOWN) == 0) continue; 3100 if (ri->flags & SRI_DISCONNECTED) continue; 3101 if (!(flags & SENTINEL_ASK_FORCED) && 3102 mstime() - ri->last_master_down_reply_time < SENTINEL_ASK_PERIOD) 3103 continue; 3104 3105 /* Ask */ 3106 ll2string(port,sizeof(port),master->addr->port); 3107 retval = redisAsyncCommand(ri->cc, 3108 sentinelReceiveIsMasterDownReply, NULL, 3109 "SENTINEL is-master-down-by-addr %s %s %llu %s", 3110 master->addr->ip, port, 3111 sentinel.current_epoch, 3112 (master->failover_state > SENTINEL_FAILOVER_STATE_NONE) ? 3113 server.runid : "*"); 3114 if (retval == REDIS_OK) ri->pending_commands++; 3115 } 3116 dictReleaseIterator(di); 3117 } 3118 3119 /* =============================== FAILOVER ================================= */ 3120 3121 /* Vote for the sentinel with 'req_runid' or return the old vote if already 3122 * voted for the specifed 'req_epoch' or one greater. 3123 * 3124 * If a vote is not available returns NULL, otherwise return the Sentinel 3125 * runid and populate the leader_epoch with the epoch of the vote. */ 3126 char *sentinelVoteLeader(sentinelRedisInstance *master, uint64_t req_epoch, char *req_runid, uint64_t *leader_epoch) { 3127 if (req_epoch > sentinel.current_epoch) { 3128 sentinel.current_epoch = req_epoch; 3129 sentinelFlushConfig(); 3130 sentinelEvent(REDIS_WARNING,"+new-epoch",master,"%llu", 3131 (unsigned long long) sentinel.current_epoch); 3132 } 3133 3134 if (master->leader_epoch < req_epoch && sentinel.current_epoch <= req_epoch) 3135 { 3136 sdsfree(master->leader); 3137 master->leader = sdsnew(req_runid); 3138 master->leader_epoch = sentinel.current_epoch; 3139 sentinelFlushConfig(); 3140 sentinelEvent(REDIS_WARNING,"+vote-for-leader",master,"%s %llu", 3141 master->leader, (unsigned long long) master->leader_epoch); 3142 /* If we did not voted for ourselves, set the master failover start 3143 * time to now, in order to force a delay before we can start a 3144 * failover for the same master. */ 3145 if (strcasecmp(master->leader,server.runid)) 3146 master->failover_start_time = mstime()+rand()%SENTINEL_MAX_DESYNC; 3147 } 3148 3149 *leader_epoch = master->leader_epoch; 3150 return master->leader ? sdsnew(master->leader) : NULL; 3151 } 3152 3153 struct sentinelLeader { 3154 char *runid; 3155 unsigned long votes; 3156 }; 3157 3158 /* Helper function for sentinelGetLeader, increment the counter 3159 * relative to the specified runid. */ 3160 int sentinelLeaderIncr(dict *counters, char *runid) { 3161 dictEntry *de = dictFind(counters,runid); 3162 uint64_t oldval; 3163 3164 if (de) { 3165 oldval = dictGetUnsignedIntegerVal(de); 3166 dictSetUnsignedIntegerVal(de,oldval+1); 3167 return oldval+1; 3168 } else { 3169 de = dictAddRaw(counters,runid); 3170 redisAssert(de != NULL); 3171 dictSetUnsignedIntegerVal(de,1); 3172 return 1; 3173 } 3174 } 3175 3176 /* Scan all the Sentinels attached to this master to check if there 3177 * is a leader for the specified epoch. 3178 * 3179 * To be a leader for a given epoch, we should have the majorify of 3180 * the Sentinels we know that reported the same instance as 3181 * leader for the same epoch. */ 3182 char *sentinelGetLeader(sentinelRedisInstance *master, uint64_t epoch) { 3183 dict *counters; 3184 dictIterator *di; 3185 dictEntry *de; 3186 unsigned int voters = 0, voters_quorum; 3187 char *myvote; 3188 char *winner = NULL; 3189 uint64_t leader_epoch; 3190 uint64_t max_votes = 0; 3191 3192 redisAssert(master->flags & (SRI_O_DOWN|SRI_FAILOVER_IN_PROGRESS)); 3193 counters = dictCreate(&leaderVotesDictType,NULL); 3194 3195 /* Count other sentinels votes */ 3196 di = dictGetIterator(master->sentinels); 3197 while((de = dictNext(di)) != NULL) { 3198 sentinelRedisInstance *ri = dictGetVal(de); 3199 if (ri->leader != NULL && ri->leader_epoch == sentinel.current_epoch) 3200 sentinelLeaderIncr(counters,ri->leader); 3201 voters++; 3202 } 3203 dictReleaseIterator(di); 3204 3205 /* Check what's the winner. For the winner to win, it needs two conditions: 3206 * 1) Absolute majority between voters (50% + 1). 3207 * 2) And anyway at least master->quorum votes. */ 3208 di = dictGetIterator(counters); 3209 while((de = dictNext(di)) != NULL) { 3210 uint64_t votes = dictGetUnsignedIntegerVal(de); 3211 3212 if (votes > max_votes) { 3213 max_votes = votes; 3214 winner = dictGetKey(de); 3215 } 3216 } 3217 dictReleaseIterator(di); 3218 3219 /* Count this Sentinel vote: 3220 * if this Sentinel did not voted yet, either vote for the most 3221 * common voted sentinel, or for itself if no vote exists at all. */ 3222 if (winner) 3223 myvote = sentinelVoteLeader(master,epoch,winner,&leader_epoch); 3224 else 3225 myvote = sentinelVoteLeader(master,epoch,server.runid,&leader_epoch); 3226 3227 if (myvote && leader_epoch == epoch) { 3228 uint64_t votes = sentinelLeaderIncr(counters,myvote); 3229 3230 if (votes > max_votes) { 3231 max_votes = votes; 3232 winner = myvote; 3233 } 3234 } 3235 voters++; /* Anyway, count me as one of the voters. */ 3236 3237 voters_quorum = voters/2+1; 3238 if (winner && (max_votes < voters_quorum || max_votes < master->quorum)) 3239 winner = NULL; 3240 3241 winner = winner ? sdsnew(winner) : NULL; 3242 sdsfree(myvote); 3243 dictRelease(counters); 3244 return winner; 3245 } 3246 3247 /* Send SLAVEOF to the specified instance, always followed by a 3248 * CONFIG REWRITE command in order to store the new configuration on disk 3249 * when possible (that is, if the Redis instance is recent enough to support 3250 * config rewriting, and if the server was started with a configuration file). 3251 * 3252 * If Host is NULL the function sends "SLAVEOF NO ONE". 3253 * 3254 * The command returns REDIS_OK if the SLAVEOF command was accepted for 3255 * (later) delivery otherwise REDIS_ERR. The command replies are just 3256 * discarded. */ 3257 int sentinelSendSlaveOf(sentinelRedisInstance *ri, char *host, int port) { 3258 char portstr[32]; 3259 int retval; 3260 3261 ll2string(portstr,sizeof(portstr),port); 3262 3263 /* If host is NULL we send SLAVEOF NO ONE that will turn the instance 3264 * into a master. */ 3265 if (host == NULL) { 3266 host = "NO"; 3267 memcpy(portstr,"ONE",4); 3268 } 3269 3270 /* In order to send SLAVEOF in a safe way, we send a transaction performing 3271 * the following tasks: 3272 * 1) Reconfigure the instance according to the specified host/port params. 3273 * 2) Rewrite the configuraiton. 3274 * 3) Disconnect all clients (but this one sending the commnad) in order 3275 * to trigger the ask-master-on-reconnection protocol for connected 3276 * clients. 3277 * 3278 * Note that we don't check the replies returned by commands, since we 3279 * will observe instead the effects in the next INFO output. */ 3280 retval = redisAsyncCommand(ri->cc, 3281 sentinelDiscardReplyCallback, NULL, "MULTI"); 3282 if (retval == REDIS_ERR) return retval; 3283 ri->pending_commands++; 3284 3285 retval = redisAsyncCommand(ri->cc, 3286 sentinelDiscardReplyCallback, NULL, "SLAVEOF %s %s", host, portstr); 3287 if (retval == REDIS_ERR) return retval; 3288 ri->pending_commands++; 3289 3290 retval = redisAsyncCommand(ri->cc, 3291 sentinelDiscardReplyCallback, NULL, "CONFIG REWRITE"); 3292 if (retval == REDIS_ERR) return retval; 3293 ri->pending_commands++; 3294 3295 /* CLIENT KILL TYPE <type> is only supported starting from Redis 2.8.12, 3296 * however sending it to an instance not understanding this command is not 3297 * an issue because CLIENT is variadic command, so Redis will not 3298 * recognized as a syntax error, and the transaction will not fail (but 3299 * only the unsupported command will fail). */ 3300 retval = redisAsyncCommand(ri->cc, 3301 sentinelDiscardReplyCallback, NULL, "CLIENT KILL TYPE normal"); 3302 if (retval == REDIS_ERR) return retval; 3303 ri->pending_commands++; 3304 3305 retval = redisAsyncCommand(ri->cc, 3306 sentinelDiscardReplyCallback, NULL, "EXEC"); 3307 if (retval == REDIS_ERR) return retval; 3308 ri->pending_commands++; 3309 3310 return REDIS_OK; 3311 } 3312 3313 /* Setup the master state to start a failover. */ 3314 void sentinelStartFailover(sentinelRedisInstance *master) { 3315 redisAssert(master->flags & SRI_MASTER); 3316 3317 master->failover_state = SENTINEL_FAILOVER_STATE_WAIT_START; 3318 master->flags |= SRI_FAILOVER_IN_PROGRESS; 3319 master->failover_epoch = ++sentinel.current_epoch; 3320 sentinelEvent(REDIS_WARNING,"+new-epoch",master,"%llu", 3321 (unsigned long long) sentinel.current_epoch); 3322 sentinelEvent(REDIS_WARNING,"+try-failover",master,"%@"); 3323 master->failover_start_time = mstime()+rand()%SENTINEL_MAX_DESYNC; 3324 master->failover_state_change_time = mstime(); 3325 } 3326 3327 /* This function checks if there are the conditions to start the failover, 3328 * that is: 3329 * 3330 * 1) Master must be in ODOWN condition. 3331 * 2) No failover already in progress. 3332 * 3) No failover already attempted recently. 3333 * 3334 * We still don't know if we'll win the election so it is possible that we 3335 * start the failover but that we'll not be able to act. 3336 * 3337 * Return non-zero if a failover was started. */ 3338 int sentinelStartFailoverIfNeeded(sentinelRedisInstance *master) { 3339 /* We can't failover if the master is not in O_DOWN state. */ 3340 if (!(master->flags & SRI_O_DOWN)) return 0; 3341 3342 /* Failover already in progress? */ 3343 if (master->flags & SRI_FAILOVER_IN_PROGRESS) return 0; 3344 3345 /* Last failover attempt started too little time ago? */ 3346 if (mstime() - master->failover_start_time < 3347 master->failover_timeout*2) 3348 { 3349 if (master->failover_delay_logged != master->failover_start_time) { 3350 time_t clock = (master->failover_start_time + 3351 master->failover_timeout*2) / 1000; 3352 char ctimebuf[26]; 3353 3354 ctime_r(&clock,ctimebuf); 3355 ctimebuf[24] = '\0'; /* Remove newline. */ 3356 master->failover_delay_logged = master->failover_start_time; 3357 redisLog(REDIS_WARNING, 3358 "Next failover delay: I will not start a failover before %s", 3359 ctimebuf); 3360 } 3361 return 0; 3362 } 3363 3364 sentinelStartFailover(master); 3365 return 1; 3366 } 3367 3368 /* Select a suitable slave to promote. The current algorithm only uses 3369 * the following parameters: 3370 * 3371 * 1) None of the following conditions: S_DOWN, O_DOWN, DISCONNECTED. 3372 * 2) Last time the slave replied to ping no more than 5 times the PING period. 3373 * 3) info_refresh not older than 3 times the INFO refresh period. 3374 * 4) master_link_down_time no more than: 3375 * (now - master->s_down_since_time) + (master->down_after_period * 10). 3376 * Basically since the master is down from our POV, the slave reports 3377 * to be disconnected no more than 10 times the configured down-after-period. 3378 * This is pretty much black magic but the idea is, the master was not 3379 * available so the slave may be lagging, but not over a certain time. 3380 * Anyway we'll select the best slave according to replication offset. 3381 * 5) Slave priority can't be zero, otherwise the slave is discarded. 3382 * 3383 * Among all the slaves matching the above conditions we select the slave 3384 * with, in order of sorting key: 3385 * 3386 * - lower slave_priority. 3387 * - bigger processed replication offset. 3388 * - lexicographically smaller runid. 3389 * 3390 * Basically if runid is the same, the slave that processed more commands 3391 * from the master is selected. 3392 * 3393 * The function returns the pointer to the selected slave, otherwise 3394 * NULL if no suitable slave was found. 3395 */ 3396 3397 /* Helper for sentinelSelectSlave(). This is used by qsort() in order to 3398 * sort suitable slaves in a "better first" order, to take the first of 3399 * the list. */ 3400 int compareSlavesForPromotion(const void *a, const void *b) { 3401 sentinelRedisInstance **sa = (sentinelRedisInstance **)a, 3402 **sb = (sentinelRedisInstance **)b; 3403 char *sa_runid, *sb_runid; 3404 3405 if ((*sa)->slave_priority != (*sb)->slave_priority) 3406 return (*sa)->slave_priority - (*sb)->slave_priority; 3407 3408 /* If priority is the same, select the slave with greater replication 3409 * offset (processed more data frmo the master). */ 3410 if ((*sa)->slave_repl_offset > (*sb)->slave_repl_offset) { 3411 return -1; /* a < b */ 3412 } else if ((*sa)->slave_repl_offset < (*sb)->slave_repl_offset) { 3413 return 1; /* b > a */ 3414 } 3415 3416 /* If the replication offset is the same select the slave with that has 3417 * the lexicographically smaller runid. Note that we try to handle runid 3418 * == NULL as there are old Redis versions that don't publish runid in 3419 * INFO. A NULL runid is considered bigger than any other runid. */ 3420 sa_runid = (*sa)->runid; 3421 sb_runid = (*sb)->runid; 3422 if (sa_runid == NULL && sb_runid == NULL) return 0; 3423 else if (sa_runid == NULL) return 1; /* a > b */ 3424 else if (sb_runid == NULL) return -1; /* a < b */ 3425 return strcasecmp(sa_runid, sb_runid); 3426 } 3427 3428 sentinelRedisInstance *sentinelSelectSlave(sentinelRedisInstance *master) { 3429 sentinelRedisInstance **instance = 3430 zmalloc(sizeof(instance[0])*dictSize(master->slaves)); 3431 sentinelRedisInstance *selected = NULL; 3432 int instances = 0; 3433 dictIterator *di; 3434 dictEntry *de; 3435 mstime_t max_master_down_time = 0; 3436 3437 if (master->flags & SRI_S_DOWN) 3438 max_master_down_time += mstime() - master->s_down_since_time; 3439 max_master_down_time += master->down_after_period * 10; 3440 3441 di = dictGetIterator(master->slaves); 3442 while((de = dictNext(di)) != NULL) { 3443 sentinelRedisInstance *slave = dictGetVal(de); 3444 mstime_t info_validity_time; 3445 3446 if (slave->flags & (SRI_S_DOWN|SRI_O_DOWN|SRI_DISCONNECTED)) continue; 3447 if (mstime() - slave->last_avail_time > SENTINEL_PING_PERIOD*5) continue; 3448 if (slave->slave_priority == 0) continue; 3449 3450 /* If the master is in SDOWN state we get INFO for slaves every second. 3451 * Otherwise we get it with the usual period so we need to account for 3452 * a larger delay. */ 3453 if (master->flags & SRI_S_DOWN) 3454 info_validity_time = SENTINEL_PING_PERIOD*5; 3455 else 3456 info_validity_time = SENTINEL_INFO_PERIOD*3; 3457 if (mstime() - slave->info_refresh > info_validity_time) continue; 3458 if (slave->master_link_down_time > max_master_down_time) continue; 3459 instance[instances++] = slave; 3460 } 3461 dictReleaseIterator(di); 3462 if (instances) { 3463 qsort(instance,instances,sizeof(sentinelRedisInstance*), 3464 compareSlavesForPromotion); 3465 selected = instance[0]; 3466 } 3467 zfree(instance); 3468 return selected; 3469 } 3470 3471 /* ---------------- Failover state machine implementation ------------------- */ 3472 void sentinelFailoverWaitStart(sentinelRedisInstance *ri) { 3473 char *leader; 3474 int isleader; 3475 3476 /* Check if we are the leader for the failover epoch. */ 3477 leader = sentinelGetLeader(ri, ri->failover_epoch); 3478 isleader = leader && strcasecmp(leader,server.runid) == 0; 3479 sdsfree(leader); 3480 3481 /* If I'm not the leader, and it is not a forced failover via 3482 * SENTINEL FAILOVER, then I can't continue with the failover. */ 3483 if (!isleader && !(ri->flags & SRI_FORCE_FAILOVER)) { 3484 int election_timeout = SENTINEL_ELECTION_TIMEOUT; 3485 3486 /* The election timeout is the MIN between SENTINEL_ELECTION_TIMEOUT 3487 * and the configured failover timeout. */ 3488 if (election_timeout > ri->failover_timeout) 3489 election_timeout = ri->failover_timeout; 3490 /* Abort the failover if I'm not the leader after some time. */ 3491 if (mstime() - ri->failover_start_time > election_timeout) { 3492 sentinelEvent(REDIS_WARNING,"-failover-abort-not-elected",ri,"%@"); 3493 sentinelAbortFailover(ri); 3494 } 3495 return; 3496 } 3497 sentinelEvent(REDIS_WARNING,"+elected-leader",ri,"%@"); 3498 ri->failover_state = SENTINEL_FAILOVER_STATE_SELECT_SLAVE; 3499 ri->failover_state_change_time = mstime(); 3500 sentinelEvent(REDIS_WARNING,"+failover-state-select-slave",ri,"%@"); 3501 } 3502 3503 void sentinelFailoverSelectSlave(sentinelRedisInstance *ri) { 3504 sentinelRedisInstance *slave = sentinelSelectSlave(ri); 3505 3506 /* We don't handle the timeout in this state as the function aborts 3507 * the failover or go forward in the next state. */ 3508 if (slave == NULL) { 3509 sentinelEvent(REDIS_WARNING,"-failover-abort-no-good-slave",ri,"%@"); 3510 sentinelAbortFailover(ri); 3511 } else { 3512 sentinelEvent(REDIS_WARNING,"+selected-slave",slave,"%@"); 3513 slave->flags |= SRI_PROMOTED; 3514 ri->promoted_slave = slave; 3515 ri->failover_state = SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE; 3516 ri->failover_state_change_time = mstime(); 3517 sentinelEvent(REDIS_NOTICE,"+failover-state-send-slaveof-noone", 3518 slave, "%@"); 3519 } 3520 } 3521 3522 void sentinelFailoverSendSlaveOfNoOne(sentinelRedisInstance *ri) { 3523 int retval; 3524 3525 /* We can't send the command to the promoted slave if it is now 3526 * disconnected. Retry again and again with this state until the timeout 3527 * is reached, then abort the failover. */ 3528 if (ri->promoted_slave->flags & SRI_DISCONNECTED) { 3529 if (mstime() - ri->failover_state_change_time > ri->failover_timeout) { 3530 sentinelEvent(REDIS_WARNING,"-failover-abort-slave-timeout",ri,"%@"); 3531 sentinelAbortFailover(ri); 3532 } 3533 return; 3534 } 3535 3536 /* Send SLAVEOF NO ONE command to turn the slave into a master. 3537 * We actually register a generic callback for this command as we don't 3538 * really care about the reply. We check if it worked indirectly observing 3539 * if INFO returns a different role (master instead of slave). */ 3540 retval = sentinelSendSlaveOf(ri->promoted_slave,NULL,0); 3541 if (retval != REDIS_OK) return; 3542 sentinelEvent(REDIS_NOTICE, "+failover-state-wait-promotion", 3543 ri->promoted_slave,"%@"); 3544 ri->failover_state = SENTINEL_FAILOVER_STATE_WAIT_PROMOTION; 3545 ri->failover_state_change_time = mstime(); 3546 } 3547 3548 /* We actually wait for promotion indirectly checking with INFO when the 3549 * slave turns into a master. */ 3550 void sentinelFailoverWaitPromotion(sentinelRedisInstance *ri) { 3551 /* Just handle the timeout. Switching to the next state is handled 3552 * by the function parsing the INFO command of the promoted slave. */ 3553 if (mstime() - ri->failover_state_change_time > ri->failover_timeout) { 3554 sentinelEvent(REDIS_WARNING,"-failover-abort-slave-timeout",ri,"%@"); 3555 sentinelAbortFailover(ri); 3556 } 3557 } 3558 3559 void sentinelFailoverDetectEnd(sentinelRedisInstance *master) { 3560 int not_reconfigured = 0, timeout = 0; 3561 dictIterator *di; 3562 dictEntry *de; 3563 mstime_t elapsed = mstime() - master->failover_state_change_time; 3564 3565 /* We can't consider failover finished if the promoted slave is 3566 * not reachable. */ 3567 if (master->promoted_slave == NULL || 3568 master->promoted_slave->flags & SRI_S_DOWN) return; 3569 3570 /* The failover terminates once all the reachable slaves are properly 3571 * configured. */ 3572 di = dictGetIterator(master->slaves); 3573 while((de = dictNext(di)) != NULL) { 3574 sentinelRedisInstance *slave = dictGetVal(de); 3575 3576 if (slave->flags & (SRI_PROMOTED|SRI_RECONF_DONE)) continue; 3577 if (slave->flags & SRI_S_DOWN) continue; 3578 not_reconfigured++; 3579 } 3580 dictReleaseIterator(di); 3581 3582 /* Force end of failover on timeout. */ 3583 if (elapsed > master->failover_timeout) { 3584 not_reconfigured = 0; 3585 timeout = 1; 3586 sentinelEvent(REDIS_WARNING,"+failover-end-for-timeout",master,"%@"); 3587 } 3588 3589 if (not_reconfigured == 0) { 3590 sentinelEvent(REDIS_WARNING,"+failover-end",master,"%@"); 3591 master->failover_state = SENTINEL_FAILOVER_STATE_UPDATE_CONFIG; 3592 master->failover_state_change_time = mstime(); 3593 } 3594 3595 /* If I'm the leader it is a good idea to send a best effort SLAVEOF 3596 * command to all the slaves still not reconfigured to replicate with 3597 * the new master. */ 3598 if (timeout) { 3599 dictIterator *di; 3600 dictEntry *de; 3601 3602 di = dictGetIterator(master->slaves); 3603 while((de = dictNext(di)) != NULL) { 3604 sentinelRedisInstance *slave = dictGetVal(de); 3605 int retval; 3606 3607 if (slave->flags & 3608 (SRI_RECONF_DONE|SRI_RECONF_SENT|SRI_DISCONNECTED)) continue; 3609 3610 retval = sentinelSendSlaveOf(slave, 3611 master->promoted_slave->addr->ip, 3612 master->promoted_slave->addr->port); 3613 if (retval == REDIS_OK) { 3614 sentinelEvent(REDIS_NOTICE,"+slave-reconf-sent-be",slave,"%@"); 3615 slave->flags |= SRI_RECONF_SENT; 3616 } 3617 } 3618 dictReleaseIterator(di); 3619 } 3620 } 3621 3622 /* Send SLAVE OF <new master address> to all the remaining slaves that 3623 * still don't appear to have the configuration updated. */ 3624 void sentinelFailoverReconfNextSlave(sentinelRedisInstance *master) { 3625 dictIterator *di; 3626 dictEntry *de; 3627 int in_progress = 0; 3628 3629 di = dictGetIterator(master->slaves); 3630 while((de = dictNext(di)) != NULL) { 3631 sentinelRedisInstance *slave = dictGetVal(de); 3632 3633 if (slave->flags & (SRI_RECONF_SENT|SRI_RECONF_INPROG)) 3634 in_progress++; 3635 } 3636 dictReleaseIterator(di); 3637 3638 di = dictGetIterator(master->slaves); 3639 while(in_progress < master->parallel_syncs && 3640 (de = dictNext(di)) != NULL) 3641 { 3642 sentinelRedisInstance *slave = dictGetVal(de); 3643 int retval; 3644 3645 /* Skip the promoted slave, and already configured slaves. */ 3646 if (slave->flags & (SRI_PROMOTED|SRI_RECONF_DONE)) continue; 3647 3648 /* If too much time elapsed without the slave moving forward to 3649 * the next state, consider it reconfigured even if it is not. 3650 * Sentinels will detect the slave as misconfigured and fix its 3651 * configuration later. */ 3652 if ((slave->flags & SRI_RECONF_SENT) && 3653 (mstime() - slave->slave_reconf_sent_time) > 3654 SENTINEL_SLAVE_RECONF_TIMEOUT) 3655 { 3656 sentinelEvent(REDIS_NOTICE,"-slave-reconf-sent-timeout",slave,"%@"); 3657 slave->flags &= ~SRI_RECONF_SENT; 3658 slave->flags |= SRI_RECONF_DONE; 3659 } 3660 3661 /* Nothing to do for instances that are disconnected or already 3662 * in RECONF_SENT state. */ 3663 if (slave->flags & (SRI_DISCONNECTED|SRI_RECONF_SENT|SRI_RECONF_INPROG)) 3664 continue; 3665 3666 /* Send SLAVEOF <new master>. */ 3667 retval = sentinelSendSlaveOf(slave, 3668 master->promoted_slave->addr->ip, 3669 master->promoted_slave->addr->port); 3670 if (retval == REDIS_OK) { 3671 slave->flags |= SRI_RECONF_SENT; 3672 slave->slave_reconf_sent_time = mstime(); 3673 sentinelEvent(REDIS_NOTICE,"+slave-reconf-sent",slave,"%@"); 3674 in_progress++; 3675 } 3676 } 3677 dictReleaseIterator(di); 3678 3679 /* Check if all the slaves are reconfigured and handle timeout. */ 3680 sentinelFailoverDetectEnd(master); 3681 } 3682 3683 /* This function is called when the slave is in 3684 * SENTINEL_FAILOVER_STATE_UPDATE_CONFIG state. In this state we need 3685 * to remove it from the master table and add the promoted slave instead. */ 3686 void sentinelFailoverSwitchToPromotedSlave(sentinelRedisInstance *master) { 3687 sentinelRedisInstance *ref = master->promoted_slave ? 3688 master->promoted_slave : master; 3689 3690 sentinelEvent(REDIS_WARNING,"+switch-master",master,"%s %s %d %s %d", 3691 master->name, master->addr->ip, master->addr->port, 3692 ref->addr->ip, ref->addr->port); 3693 3694 sentinelResetMasterAndChangeAddress(master,ref->addr->ip,ref->addr->port); 3695 } 3696 3697 void sentinelFailoverStateMachine(sentinelRedisInstance *ri) { 3698 redisAssert(ri->flags & SRI_MASTER); 3699 3700 if (!(ri->flags & SRI_FAILOVER_IN_PROGRESS)) return; 3701 3702 switch(ri->failover_state) { 3703 case SENTINEL_FAILOVER_STATE_WAIT_START: 3704 sentinelFailoverWaitStart(ri); 3705 break; 3706 case SENTINEL_FAILOVER_STATE_SELECT_SLAVE: 3707 sentinelFailoverSelectSlave(ri); 3708 break; 3709 case SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE: 3710 sentinelFailoverSendSlaveOfNoOne(ri); 3711 break; 3712 case SENTINEL_FAILOVER_STATE_WAIT_PROMOTION: 3713 sentinelFailoverWaitPromotion(ri); 3714 break; 3715 case SENTINEL_FAILOVER_STATE_RECONF_SLAVES: 3716 sentinelFailoverReconfNextSlave(ri); 3717 break; 3718 } 3719 } 3720 3721 /* Abort a failover in progress: 3722 * 3723 * This function can only be called before the promoted slave acknowledged 3724 * the slave -> master switch. Otherwise the failover can't be aborted and 3725 * will reach its end (possibly by timeout). */ 3726 void sentinelAbortFailover(sentinelRedisInstance *ri) { 3727 redisAssert(ri->flags & SRI_FAILOVER_IN_PROGRESS); 3728 redisAssert(ri->failover_state <= SENTINEL_FAILOVER_STATE_WAIT_PROMOTION); 3729 3730 ri->flags &= ~(SRI_FAILOVER_IN_PROGRESS|SRI_FORCE_FAILOVER); 3731 ri->failover_state = SENTINEL_FAILOVER_STATE_NONE; 3732 ri->failover_state_change_time = mstime(); 3733 if (ri->promoted_slave) { 3734 ri->promoted_slave->flags &= ~SRI_PROMOTED; 3735 ri->promoted_slave = NULL; 3736 } 3737 } 3738 3739 /* ======================== SENTINEL timer handler ========================== 3740 * This is the "main" our Sentinel, being sentinel completely non blocking 3741 * in design. The function is called every second. 3742 * -------------------------------------------------------------------------- */ 3743 3744 /* Perform scheduled operations for the specified Redis instance. */ 3745 void sentinelHandleRedisInstance(sentinelRedisInstance *ri) { 3746 /* ========== MONITORING HALF ============ */ 3747 /* Every kind of instance */ 3748 sentinelReconnectInstance(ri); 3749 sentinelSendPeriodicCommands(ri); 3750 3751 /* ============== ACTING HALF ============= */ 3752 /* We don't proceed with the acting half if we are in TILT mode. 3753 * TILT happens when we find something odd with the time, like a 3754 * sudden change in the clock. */ 3755 if (sentinel.tilt) { 3756 if (mstime()-sentinel.tilt_start_time < SENTINEL_TILT_PERIOD) return; 3757 sentinel.tilt = 0; 3758 sentinelEvent(REDIS_WARNING,"-tilt",NULL,"#tilt mode exited"); 3759 } 3760 3761 /* Every kind of instance */ 3762 sentinelCheckSubjectivelyDown(ri); 3763 3764 /* Masters and slaves */ 3765 if (ri->flags & (SRI_MASTER|SRI_SLAVE)) { 3766 /* Nothing so far. */ 3767 } 3768 3769 /* Only masters */ 3770 if (ri->flags & SRI_MASTER) { 3771 sentinelCheckObjectivelyDown(ri); 3772 if (sentinelStartFailoverIfNeeded(ri)) 3773 sentinelAskMasterStateToOtherSentinels(ri,SENTINEL_ASK_FORCED); 3774 sentinelFailoverStateMachine(ri); 3775 sentinelAskMasterStateToOtherSentinels(ri,SENTINEL_NO_FLAGS); 3776 } 3777 } 3778 3779 /* Perform scheduled operations for all the instances in the dictionary. 3780 * Recursively call the function against dictionaries of slaves. */ 3781 void sentinelHandleDictOfRedisInstances(dict *instances) { 3782 dictIterator *di; 3783 dictEntry *de; 3784 sentinelRedisInstance *switch_to_promoted = NULL; 3785 3786 /* There are a number of things we need to perform against every master. */ 3787 di = dictGetIterator(instances); 3788 while((de = dictNext(di)) != NULL) { 3789 sentinelRedisInstance *ri = dictGetVal(de); 3790 3791 sentinelHandleRedisInstance(ri); 3792 if (ri->flags & SRI_MASTER) { 3793 sentinelHandleDictOfRedisInstances(ri->slaves); 3794 sentinelHandleDictOfRedisInstances(ri->sentinels); 3795 if (ri->failover_state == SENTINEL_FAILOVER_STATE_UPDATE_CONFIG) { 3796 switch_to_promoted = ri; 3797 } 3798 } 3799 } 3800 if (switch_to_promoted) 3801 sentinelFailoverSwitchToPromotedSlave(switch_to_promoted); 3802 dictReleaseIterator(di); 3803 } 3804 3805 /* This function checks if we need to enter the TITL mode. 3806 * 3807 * The TILT mode is entered if we detect that between two invocations of the 3808 * timer interrupt, a negative amount of time, or too much time has passed. 3809 * Note that we expect that more or less just 100 milliseconds will pass 3810 * if everything is fine. However we'll see a negative number or a 3811 * difference bigger than SENTINEL_TILT_TRIGGER milliseconds if one of the 3812 * following conditions happen: 3813 * 3814 * 1) The Sentiel process for some time is blocked, for every kind of 3815 * random reason: the load is huge, the computer was frozen for some time 3816 * in I/O or alike, the process was stopped by a signal. Everything. 3817 * 2) The system clock was altered significantly. 3818 * 3819 * Under both this conditions we'll see everything as timed out and failing 3820 * without good reasons. Instead we enter the TILT mode and wait 3821 * for SENTINEL_TILT_PERIOD to elapse before starting to act again. 3822 * 3823 * During TILT time we still collect information, we just do not act. */ 3824 void sentinelCheckTiltCondition(void) { 3825 mstime_t now = mstime(); 3826 mstime_t delta = now - sentinel.previous_time; 3827 3828 if (delta < 0 || delta > SENTINEL_TILT_TRIGGER) { 3829 sentinel.tilt = 1; 3830 sentinel.tilt_start_time = mstime(); 3831 sentinelEvent(REDIS_WARNING,"+tilt",NULL,"#tilt mode entered"); 3832 } 3833 sentinel.previous_time = mstime(); 3834 } 3835 3836 void sentinelTimer(void) { 3837 sentinelCheckTiltCondition(); 3838 sentinelHandleDictOfRedisInstances(sentinel.masters); 3839 sentinelRunPendingScripts(); 3840 sentinelCollectTerminatedScripts(); 3841 sentinelKillTimedoutScripts(); 3842 3843 /* We continuously change the frequency of the Redis "timer interrupt" 3844 * in order to desynchronize every Sentinel from every other. 3845 * This non-determinism avoids that Sentinels started at the same time 3846 * exactly continue to stay synchronized asking to be voted at the 3847 * same time again and again (resulting in nobody likely winning the 3848 * election because of split brain voting). */ 3849 server.hz = REDIS_DEFAULT_HZ + rand() % REDIS_DEFAULT_HZ; 3850 } 3851 3852