Skip to content

Commit

Permalink
Sentinel: client reconfiguration script execution.
Browse files Browse the repository at this point in the history
This commit adds support to optionally execute a script when one of the
following events happen:

* The failover starts (with a slave already promoted).
* The failover ends.
* The failover is aborted.

The script is called with enough parameters (documented in the example
sentinel.conf file) to provide information about the old and new ip:port
pair of the master, the role of the sentinel (leader or observer) and
the name of the master.

The goal of the script is to inform clients of the configuration change
in a way specific to the environment Sentinel is running, that can't be
implemented in a genereal way inside Sentinel itself.
  • Loading branch information
antirez committed Sep 27, 2012
1 parent 524b79d commit 26a3400
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 0 deletions.
37 changes: 37 additions & 0 deletions sentinel.conf
Expand Up @@ -91,4 +91,41 @@ sentinel failover-timeout mymaster 900000
#
# sentinel notification-script mymaster /var/redis/notify.sh

# CLIENTS RECONFIGURATION SCRIPT
#
# sentinel client-reconfig-script <master-name> <script-path>
#
# When the failover starts, ends, or is aborted, a script can be called in
# order to perform application-specific tasks to notify the clients that the
# configuration has changed and the master is at a different address.
#
# The script is called in the following cases:
#
# Failover started (a slave is already promoted)
# Failover finished (all the additional slaves already reconfigured)
# Failover aborted (in that case the script was previously called when the
# failover started, and now gets called again with swapped
# addresses).
#
# The following arguments are passed to the script:
#
# <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
#
# <state> is "start", "end" or "abort"
# <role> is either "leader" or "observer"
#
# The arguments from-ip, from-port, to-ip, to-port are used to communicate
# the old address of the master and the new address of the elected slave
# (now a master) in the case state is "start" or "end".
#
# For abort instead the "from" is the address of the promoted slave and
# "to" is the address of the original master address, since the failover
# was aborted.
#
# This script should be resistant to multiple invocations.
#
# Example:
#
# sentinel client-reconfig-script mymaster /var/redis/reconfig.sh


42 changes: 42 additions & 0 deletions src/sentinel.c
Expand Up @@ -116,6 +116,8 @@ typedef struct sentinelAddr {
/* Generic flags that can be used with different functions. */
#define SENTINEL_NO_FLAGS 0
#define SENTINEL_GENERATE_EVENT 1
#define SENTINEL_LEADER 2
#define SENTINEL_OBSERVER 4

/* Script execution flags and limits. */
#define SENTINEL_SCRIPT_NONE 0
Expand Down Expand Up @@ -762,6 +764,32 @@ void sentinelPendingScriptsCommand(redisClient *c) {
}
}

/* This function calls, if any, the client reconfiguration script with the
* following parameters:
*
* <master-name> <role> <state> <from-ip> <from-port> <to-ip> <to-port>
*
* It is called every time a failover starts, ends, or is aborted.
*
* <state> is "start", "end" or "abort".
* <role> is either "leader" or "observer".
*
* from/to fields are respectively master -> promoted slave addresses for
* "start" and "end", or the reverse (promoted slave -> master) in case of
* "abort".
*/
void sentinelCallClientReconfScript(sentinelRedisInstance *master, int role, char *state, sentinelAddr *from, sentinelAddr *to) {
char fromport[32], toport[32];

if (master->client_reconfig_script == NULL) return;
ll2string(fromport,sizeof(fromport),from->port);
ll2string(toport,sizeof(toport),to->port);
sentinelScheduleScriptExecution(master->client_reconfig_script,
master->name,
(role == SENTINEL_LEADER) ? "leader" : "observer",
state, from->ip, fromport, to->ip, toport);
}

/* ========================== sentinelRedisInstance ========================= */

/* Create a redis instance, the following fields must be populated by the
Expand Down Expand Up @@ -1434,6 +1462,8 @@ void sentinelRefreshInstanceInfo(sentinelRedisInstance *ri, const char *info) {
sentinelEvent(REDIS_WARNING,"+promoted-slave",ri,"%@");
sentinelEvent(REDIS_WARNING,"+failover-state-reconf-slaves",
ri->master,"%@");
sentinelCallClientReconfScript(ri->master,SENTINEL_LEADER,
"start",ri->master->addr,ri->addr);
}
} else if (!(ri->master->flags & SRI_FAILOVER_IN_PROGRESS) ||
((ri->master->flags & SRI_FAILOVER_IN_PROGRESS) &&
Expand All @@ -1459,6 +1489,8 @@ void sentinelRefreshInstanceInfo(sentinelRedisInstance *ri, const char *info) {
ri->master->failover_state_change_time = mstime();
ri->master->promoted_slave = ri;
ri->flags |= SRI_PROMOTED;
sentinelCallClientReconfScript(ri->master,SENTINEL_OBSERVER,
"start", ri->master->addr,ri->addr);
/* We are an observer, so we can only assume that the leader
* is reconfiguring the slave instances. For this reason we
* set all the instances as RECONF_SENT waiting for progresses
Expand Down Expand Up @@ -2521,9 +2553,14 @@ void sentinelFailoverDetectEnd(sentinelRedisInstance *master) {
}

if (not_reconfigured == 0) {
int role = (master->flags & SRI_I_AM_THE_LEADER) ? SENTINEL_LEADER :
SENTINEL_OBSERVER;

sentinelEvent(REDIS_WARNING,"+failover-end",master,"%@");
master->failover_state = SENTINEL_FAILOVER_STATE_UPDATE_CONFIG;
master->failover_state_change_time = mstime();
sentinelCallClientReconfScript(master,role,"end",master->addr,
master->promoted_slave->addr);
}

/* If I'm the leader it is a good idea to send a best effort SLAVEOF
Expand Down Expand Up @@ -2678,6 +2715,7 @@ void sentinelAbortFailover(sentinelRedisInstance *ri) {
char master_port[32];
dictIterator *di;
dictEntry *de;
int sentinel_role;

redisAssert(ri->flags & SRI_FAILOVER_IN_PROGRESS);
ll2string(master_port,sizeof(master_port),ri->addr->port);
Expand Down Expand Up @@ -2707,10 +2745,14 @@ void sentinelAbortFailover(sentinelRedisInstance *ri) {
}
dictReleaseIterator(di);

sentinel_role = (ri->flags & SRI_I_AM_THE_LEADER) ? SENTINEL_LEADER :
SENTINEL_OBSERVER;
ri->flags &= ~(SRI_FAILOVER_IN_PROGRESS|SRI_I_AM_THE_LEADER);
ri->failover_state = SENTINEL_FAILOVER_STATE_NONE;
ri->failover_state_change_time = mstime();
if (ri->promoted_slave) {
sentinelCallClientReconfScript(ri,sentinel_role,"abort",
ri->promoted_slave->addr,ri->addr);
ri->promoted_slave->flags &= ~SRI_PROMOTED;
ri->promoted_slave = NULL;
}
Expand Down

0 comments on commit 26a3400

Please sign in to comment.