summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorantirez <antirez@gmail.com>2014-05-15 11:43:06 +0200
committerantirez <antirez@gmail.com>2014-05-20 17:45:50 +0200
commit5efa5501e8d761fc6c18fe20a66d1f61afe61360 (patch)
tree770cb81dd324fbe0df99a73fb388ee3216c61020
parent687f84a3777cdd279d721ab438075a496ff0b26e (diff)
downloadredis-5efa5501e8d761fc6c18fe20a66d1f61afe61360.tar.gz
CLUSTER RESET implemented.
The new command is able to reset a cluster node so that it starts again as a fresh node. By default the command performs a soft reset (the same as calling it as CLUSTER RESET SOFT), and the following steps are performed: 1) All slots are set as unassigned. 2) The list of known nodes is flushed. 3) Node is set as master if it is a slave. When an hard reset is performed with CLUSTER RESET HARD the following additional operations are performed: 4) A new Node ID is created at random. 5) Epochs are set to 0. CLUSTER RESET is useful both when the sysadmin wants to reconfigure a node with a different role (for example turning a slave into a master) and for testing purposes. It also may play a role in automatically provisioned Redis Clusters, since it allows to reset a node back to the initial state in order to be reconfigured.
-rw-r--r--src/cluster.c88
1 files changed, 88 insertions, 0 deletions
diff --git a/src/cluster.c b/src/cluster.c
index f5b4e38e0..e59cb52d5 100644
--- a/src/cluster.c
+++ b/src/cluster.c
@@ -70,6 +70,8 @@ void clusterDoBeforeSleep(int flags);
void clusterSendUpdate(clusterLink *link, clusterNode *node);
void resetManualFailover(void);
void clusterCloseAllSlots(void);
+void clusterSetNodeAsMaster(clusterNode *n);
+void clusterDelNode(clusterNode *delnode);
/* -----------------------------------------------------------------------------
* Initialization
@@ -462,6 +464,65 @@ void clusterInit(void) {
resetManualFailover();
}
+/* Reset a node performing a soft or hard reset:
+ *
+ * 1) All other nodes are forget.
+ * 2) All the assigned / open slots are released.
+ * 3) If the node is a slave, it turns into a master.
+ * 5) Only for hard reset: a new Node ID is generated.
+ * 6) Only for hard reset: currentEpoch and configEpoch are set to 0.
+ * 7) The new configuration is saved and the cluster state updated. */
+void clusterReset(int hard) {
+ dictIterator *di;
+ dictEntry *de;
+ int j;
+
+ /* Turn into master. */
+ if (nodeIsSlave(myself)) {
+ clusterSetNodeAsMaster(myself);
+ replicationUnsetMaster();
+ }
+
+ /* Close slots, reset manual failover state. */
+ clusterCloseAllSlots();
+ resetManualFailover();
+
+ /* Unassign all the slots. */
+ for (j = 0; j < REDIS_CLUSTER_SLOTS; j++) clusterDelSlot(j);
+
+ /* Forget all the nodes, but myself. */
+ di = dictGetSafeIterator(server.cluster->nodes);
+ while((de = dictNext(di)) != NULL) {
+ clusterNode *node = dictGetVal(de);
+
+ if (node == myself) continue;
+ clusterDelNode(node);
+ }
+ dictReleaseIterator(di);
+
+ /* Hard reset only: set epochs to 0, change node ID. */
+ if (hard) {
+ sds oldname;
+
+ server.cluster->currentEpoch = 0;
+ server.cluster->lastVoteEpoch = 0;
+ myself->configEpoch = 0;
+
+ /* To change the Node ID we need to remove the old name from the
+ * nodes table, change the ID, and re-add back with new name. */
+ oldname = sdsnewlen(myself->name, REDIS_CLUSTER_NAMELEN);
+ dictDelete(server.cluster->nodes,oldname);
+ sdsfree(oldname);
+ getRandomHexChars(myself->name, REDIS_CLUSTER_NAMELEN);
+ clusterAddNode(myself);
+ }
+
+ /* Make sure to persist the new config and update the state. */
+ clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|
+ CLUSTER_TODO_UPDATE_STATE|
+ CLUSTER_TODO_FSYNC_CONFIG);
+}
+
/* -----------------------------------------------------------------------------
* CLUSTER communication link
* -------------------------------------------------------------------------- */
@@ -3728,6 +3789,33 @@ void clusterCommand(redisClient *c) {
CLUSTER_TODO_SAVE_CONFIG);
addReply(c,shared.ok);
}
+ } else if (!strcasecmp(c->argv[1]->ptr,"reset") &&
+ (c->argc == 2 || c->argc == 3))
+ {
+ /* CLUSTER RESET [SOFT|HARD] */
+ int hard = 0;
+
+ /* Parse soft/hard argument. Default is soft. */
+ if (c->argc == 3) {
+ if (!strcasecmp(c->argv[2]->ptr,"hard")) {
+ hard = 1;
+ } else if (!strcasecmp(c->argv[2]->ptr,"hard")) {
+ hard = 0;
+ } else {
+ addReply(c,shared.syntaxerr);
+ return;
+ }
+ }
+
+ /* Slaves can be reset while containing data, but not master nodes
+ * that must be empty. */
+ if (nodeIsMaster(myself) && dictSize(c->db->dict) != 0) {
+ addReplyError(c,"CLUSTER RESET can't be called with "
+ "master nodes containing keys");
+ return;
+ }
+ clusterReset(hard);
+ addReply(c,shared.ok);
} else {
addReplyError(c,"Wrong CLUSTER subcommand or number of arguments");
}