Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/cluster.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ int getClusterSize(void);
int getMyShardSlotCount(void);
int clusterNodePending(clusterNode *node);
int clusterNodeIsPrimary(clusterNode *n);
int clusterNodeIsVotingPrimary(clusterNode *n);
char **getClusterNodesList(size_t *numnodes);
char *clusterNodeIp(clusterNode *node, client *c);
int clusterNodeIsReplica(clusterNode *node);
Expand Down
2 changes: 1 addition & 1 deletion src/cluster_legacy.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ void clusterCommandFlushslot(client *c);

/* Only primaries that own slots have voting rights.
* Returns 1 if the node has voting rights, otherwise returns 0. */
static inline int clusterNodeIsVotingPrimary(clusterNode *n) {
int clusterNodeIsVotingPrimary(clusterNode *n) {
return (n->flags & CLUSTER_NODE_PRIMARY) && n->numslots;
}

Expand Down
8 changes: 7 additions & 1 deletion src/commands.def
Original file line number Diff line number Diff line change
Expand Up @@ -7917,11 +7917,17 @@ struct COMMAND_ARG SHUTDOWN_abort_selector_save_selector_block_save_selector_Sub
{MAKE_ARG("save",ARG_TYPE_PURE_TOKEN,-1,"SAVE",NULL,NULL,CMD_ARG_NONE,0,NULL)},
};

/* SHUTDOWN abort_selector save_selector_block force_safe_selector argument table */
struct COMMAND_ARG SHUTDOWN_abort_selector_save_selector_block_force_safe_selector_Subargs[] = {
{MAKE_ARG("force",ARG_TYPE_PURE_TOKEN,-1,"FORCE",NULL,"7.0.0",CMD_ARG_NONE,0,NULL)},
{MAKE_ARG("safe",ARG_TYPE_PURE_TOKEN,-1,"SAVE",NULL,"9.0.0",CMD_ARG_NONE,0,NULL)},
};

/* SHUTDOWN abort_selector save_selector_block argument table */
struct COMMAND_ARG SHUTDOWN_abort_selector_save_selector_block_Subargs[] = {
{MAKE_ARG("save-selector",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=SHUTDOWN_abort_selector_save_selector_block_save_selector_Subargs},
{MAKE_ARG("now",ARG_TYPE_PURE_TOKEN,-1,"NOW",NULL,"7.0.0",CMD_ARG_OPTIONAL,0,NULL)},
{MAKE_ARG("force",ARG_TYPE_PURE_TOKEN,-1,"FORCE",NULL,"7.0.0",CMD_ARG_OPTIONAL,0,NULL)},
{MAKE_ARG("force-safe-selector",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=SHUTDOWN_abort_selector_save_selector_block_force_safe_selector_Subargs},
};

/* SHUTDOWN abort_selector argument table */
Expand Down
7 changes: 7 additions & 0 deletions src/commands/shutdown.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,13 @@
"token": "FORCE",
"optional": true,
"since": "7.0.0"
},
{
"name": "safe",
"type": "pure-token",
"token": "SAFE",
"optional": true,
"since": "9.0.0"
}
]
},
Expand Down
3 changes: 2 additions & 1 deletion src/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,12 @@ configEnum aof_fsync_enum[] = {
{NULL, 0}};

configEnum shutdown_on_sig_enum[] = {
{"default", 0},
{"default", SHUTDOWN_NOFLAGS},
{"save", SHUTDOWN_SAVE},
{"nosave", SHUTDOWN_NOSAVE},
{"now", SHUTDOWN_NOW},
{"force", SHUTDOWN_FORCE},
{"safe", SHUTDOWN_SAFE},
{NULL, 0}};

configEnum repl_diskless_load_enum[] = {
Expand Down
4 changes: 3 additions & 1 deletion src/db.c
Original file line number Diff line number Diff line change
Expand Up @@ -1314,7 +1314,7 @@ void typeCommand(client *c) {
addReplyStatus(c, getObjectTypeName(o));
}

/* SHUTDOWN [[NOSAVE | SAVE] [NOW] [FORCE] | ABORT] */
/* SHUTDOWN [[NOSAVE | SAVE] [NOW] [FORCE] [SAFE] | ABORT] */
void shutdownCommand(client *c) {
int flags = SHUTDOWN_NOFLAGS;
int abort = 0;
Expand All @@ -1329,6 +1329,8 @@ void shutdownCommand(client *c) {
flags |= SHUTDOWN_FORCE;
} else if (!strcasecmp(c->argv[i]->ptr, "abort")) {
abort = 1;
} else if (!strcasecmp(c->argv[i]->ptr, "safe")) {
flags |= SHUTDOWN_SAFE;
} else {
addReplyErrorObject(c, shared.syntaxerr);
return;
Expand Down
12 changes: 12 additions & 0 deletions src/server.c
Original file line number Diff line number Diff line change
Expand Up @@ -4553,6 +4553,7 @@ int finishShutdown(void) {
int save = server.shutdown_flags & SHUTDOWN_SAVE;
int nosave = server.shutdown_flags & SHUTDOWN_NOSAVE;
int force = server.shutdown_flags & SHUTDOWN_FORCE;
int safe = server.shutdown_flags & SHUTDOWN_SAFE;

/* Log a warning for each replica that is lagging. */
listIter replicas_iter;
Expand All @@ -4575,6 +4576,17 @@ int finishShutdown(void) {
num_replicas);
}

if (safe && server.cluster_enabled && clusterNodeIsVotingPrimary(getMyClusterNode())) {
if (force) {
serverLog(LL_WARNING, "I am a voting primary, shutting down may cause the cluster to down. Exit anyway.");
} else {
serverLog(LL_WARNING, "I am a voting primary, shutting down may cause the cluster to down, can't exit.");
if (server.supervised_mode == SUPERVISED_SYSTEMD)
serverCommunicateSystemd("I am a voting primary, shutting down may cause the cluster to down, can't exit.\n");
goto error;
}
}

/* Kill all the Lua debugger forked sessions. */
ldbKillForkedSessions();

Expand Down
12 changes: 6 additions & 6 deletions src/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -570,12 +570,12 @@ typedef enum {
#define UNIT_MILLISECONDS 1

/* SHUTDOWN flags */
#define SHUTDOWN_NOFLAGS 0 /* No flags. */
#define SHUTDOWN_SAVE 1 /* Force SAVE on SHUTDOWN even if no save \
points are configured. */
#define SHUTDOWN_NOSAVE 2 /* Don't SAVE on SHUTDOWN. */
#define SHUTDOWN_NOW 4 /* Don't wait for replicas to catch up. */
#define SHUTDOWN_FORCE 8 /* Don't let errors prevent shutdown. */
#define SHUTDOWN_NOFLAGS 0 /* No flags. */
#define SHUTDOWN_SAVE (1 << 0) /* Force SAVE on SHUTDOWN even if no save points are configured. */
#define SHUTDOWN_NOSAVE (1 << 1) /* Don't SAVE on SHUTDOWN. */
#define SHUTDOWN_NOW (1 << 2) /* Don't wait for replicas to catch up. */
#define SHUTDOWN_FORCE (1 << 3) /* Don't let errors prevent shutdown. */
#define SHUTDOWN_SAFE (1 << 4) /* Shutdown only when safe. */

/* Command call flags, see call() function */
#define CMD_CALL_NONE 0
Expand Down
15 changes: 15 additions & 0 deletions tests/unit/cluster/shutdown.tcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
start_cluster 3 0 {tags {external:skip cluster shutdown}} {
test "Test shutdown safe is work" {
assert_error {ERR Errors trying to SHUTDOWN*} {R 0 shutdown safe}
assert_error {ERR Errors trying to SHUTDOWN*} {R 1 shutdown safe}
assert_error {ERR Errors trying to SHUTDOWN*} {R 2 shutdown safe}

catch {R 0 shutdown safe force}
wait_for_condition 1000 50 {
[CI 1 cluster_state] eq {fail} &&
[CI 2 cluster_state] eq {fail}
} else {
fail "Cluster doesn't fail"
}
}
}
5 changes: 5 additions & 0 deletions valkey.conf
Original file line number Diff line number Diff line change
Expand Up @@ -1705,6 +1705,11 @@ aof-timestamp-enabled no
# nosave: Prevents DB saving operation even if one or more save points are configured.
# now: Skips waiting for lagging replicas.
# force: Ignores any errors that would normally prevent the server from exiting.
# safe: Shutdown only when safe. Note that safe cannot prevent force, in the case of
# force, safe will print the relevant logs. The definition of safe may be different
# in different modes. Here are the definitions:
# 1. In cluster mode, it is unsafe to shutdown a primary with slots, and may cause
# the cluster to go down.
#
# Any combination of values is allowed as long as "save" and "nosave" are not set simultaneously.
# Example: "nosave force now"
Expand Down
Loading