summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorantirez <antirez@gmail.com>2014-04-29 19:15:16 +0200
committerantirez <antirez@gmail.com>2014-04-29 19:15:16 +0200
commit11d9ecb71d44987d2cb365e90b00fe15434426d7 (patch)
treed2af3ecbdb659dedef3f0dc6c88db5c310182a56
parent7b5ce1ffb1d730774f1d8e990fea38e9ab54a148 (diff)
downloadredis-11d9ecb71d44987d2cb365e90b00fe15434426d7.tar.gz
CLUSTER SET-CONFIG-EPOCH implemented.
Initially Redis Cluster accepted that after cluster creation all the nodes were at configEpoch 0, evolving from zero as failovers happen. However later the semantic was made more strict in order to make sure a cluster has always all the master nodes with a different configEpoch, which is more robust in some corner case (especially resulting from errors by the system administrator). To assign different configEpochs to different nodes at startup was a task performed naturally by the config conflicts resolution algorithm (see the Cluster specification). However this works well only for small clusters or when there are actually just a few collisions, since it is designed for exceptional cases. When a large cluster is created hundred of nodes can be at epoch 0, so the conflict resolution code is slow to provide an unique config to each node. For this reason this new command was introduced. It can be called only when a node is totally fresh: no other nodes known, and configEpoch set to zero, so it is safe even against misuses. redis-trib will use the new command in order to start the cluster already setting an incremental unique config to every node.
-rw-r--r--src/cluster.c31
1 files changed, 31 insertions, 0 deletions
diff --git a/src/cluster.c b/src/cluster.c
index be0522ca5..42a2c2cfc 100644
--- a/src/cluster.c
+++ b/src/cluster.c
@@ -3631,6 +3631,7 @@ void clusterCommand(redisClient *c) {
sdsfree(ni);
}
} else if (!strcasecmp(c->argv[1]->ptr,"failover") && c->argc == 2) {
+ /* CLUSTER FAILOVER */
if (nodeIsMaster(myself)) {
addReplyError(c,"You should send CLUSTER FAILOVER to a slave");
return;
@@ -3646,6 +3647,36 @@ void clusterCommand(redisClient *c) {
clusterSendMFStart(myself->slaveof);
redisLog(REDIS_WARNING,"Manual failover user request accepted.");
addReply(c,shared.ok);
+ } else if (!strcasecmp(c->argv[1]->ptr,"set-config-epoch") && c->argc == 3)
+ {
+ /* CLUSTER SET-CONFIG-EPOCH <epoch>
+ *
+ * The user is allowed to set the config epoch only when a node is
+ * totally fresh: no config epoch, no other known node, and so forth.
+ * This happens at cluster creation time to start with a cluster where
+ * every node has a different node ID, without to rely on the conflicts
+ * resolution system which is too slow when a big cluster is created. */
+ long long epoch;
+
+ if (getLongLongFromObjectOrReply(c,c->argv[2],&epoch,NULL) != REDIS_OK)
+ return;
+
+ if (epoch < 0) {
+ addReplyErrorFormat(c,"Invalid config epoch specified: %lld",epoch);
+ } else if (dictSize(server.cluster->nodes) > 1) {
+ addReplyError(c,"The user can assign a config epoch only when the "
+ "node does not know any other node.");
+ } else if (myself->configEpoch != 0) {
+ addReplyError(c,"Node config epoch is already non-zero");
+ } else {
+ myself->configEpoch = epoch;
+ /* No need to fsync the config here since in the unlucky event
+ * of a failure to persist the config, the conflict resolution code
+ * will assign an unique config to this node. */
+ clusterDoBeforeSleep(CLUSTER_TODO_UPDATE_STATE|
+ CLUSTER_TODO_SAVE_CONFIG);
+ addReply(c,shared.ok);
+ }
} else {
addReplyError(c,"Wrong CLUSTER subcommand or number of arguments");
}