@@ -87,6 +87,8 @@ void UNLOCK_SETTING(void) {
8787
8888static pthread_mutex_t shutdown_lock = PTHREAD_MUTEX_INITIALIZER;
8989volatile sig_atomic_t memcached_shutdown=0;
90+ volatile int32_t shutdown_delay=0;
91+ volatile bool dynamic_shutdown=false;
9092
9193/*
9294 * We keep the current time of day in a global variable that's updated by a
@@ -10068,6 +10070,41 @@ static void process_lqdetect_command(conn *c, token_t *tokens, size_t ntokens)
1006810070}
1006910071#endif
1007010072
10073+ static void process_shutdown_command(conn *c, token_t *tokens, size_t ntokens)
10074+ {
10075+ int32_t delay;
10076+
10077+ if (memcached_shutdown > 0 && shutdown_delay < 1000) {
10078+ out_string(c, "DENIED");
10079+ return;
10080+ }
10081+
10082+ if (ntokens == 2) {
10083+ delay = 2;
10084+ dynamic_shutdown = true;
10085+ } else {
10086+ if (! safe_strtol(tokens[1].value, &delay)) {
10087+ print_invalid_command(c, tokens, ntokens);
10088+ out_string(c, "CLIENT_ERROR bad command line format");
10089+ return;
10090+ } else if (delay < 0 || delay > 600) {
10091+ print_invalid_command(c, tokens, ntokens);
10092+ out_string(c, "CLIENT_ERROR invalid arguments");
10093+ return;
10094+ }
10095+ dynamic_shutdown = false;
10096+ }
10097+ mc_logger->log(EXTENSION_LOG_WARNING, c,
10098+ "shutdown scheduled (time=%d, dynamic=%d)\n", (int)delay, dynamic_shutdown);
10099+ shutdown_delay = delay * 1000;
10100+ pthread_mutex_lock(&shutdown_lock);
10101+ if (memcached_shutdown == 0) {
10102+ memcached_shutdown = 1;
10103+ }
10104+ pthread_mutex_unlock(&shutdown_lock);
10105+ out_string(c, "OK");
10106+ }
10107+
1007110108static inline int get_coll_create_attr_from_tokens(token_t *tokens, const int ntokens,
1007210109 int coll_type, item_attr *attrp)
1007310110{
@@ -13244,6 +13281,10 @@ static void process_command_ascii(conn *c, char *command, int cmdlen)
1324413281#endif
1324513282 out_string(c, response);
1324613283 }
13284+ else if ((ntokens >= 2) && (strcmp(tokens[COMMAND_TOKEN].value, "shutdown") == 0))
13285+ {
13286+ process_shutdown_command(c, tokens, ntokens);
13287+ }
1324713288 else /* no matching command */
1324813289 {
1324913290 if (settings.extensions.ascii != NULL) {
@@ -14542,6 +14583,8 @@ static void remove_pidfile(const char *pid_file)
1454214583
1454314584static void shutdown_server(void)
1454414585{
14586+ shutdown_delay = 0;
14587+ dynamic_shutdown = false;
1454514588 pthread_mutex_lock(&shutdown_lock);
1454614589 if (memcached_shutdown == 0) {
1454714590 memcached_shutdown = 1;
@@ -15828,7 +15871,28 @@ int main (int argc, char **argv)
1582815871 close_listen_sockets();
1582915872 mc_logger->log(EXTENSION_LOG_INFO, NULL, "Listen sockets closed.\n");
1583015873
15831- /* 4) shutdown all threads */
15874+ /* 4) wait until existing clients close */
15875+ unsigned int prev_conns = UINT_MAX;
15876+ while (shutdown_delay > 0) {
15877+ if (dynamic_shutdown) {
15878+ LOCK_STATS();
15879+ if (mc_stats.curr_conns > 0 && mc_stats.curr_conns < prev_conns) {
15880+ prev_conns = mc_stats.curr_conns;
15881+ } else {
15882+ mc_logger->log(EXTENSION_LOG_INFO, NULL,
15883+ "Client connections haven't been reduced. "
15884+ "Shuts down %dms earlier than scheduled time.\n",
15885+ shutdown_delay);
15886+ shutdown_delay = 0;
15887+ }
15888+ UNLOCK_STATS();
15889+ if (shutdown_delay <= 0) break;
15890+ }
15891+ usleep(200000); /* sleep 200ms */
15892+ shutdown_delay -= 200;
15893+ }
15894+
15895+ /* 5) shutdown all threads */
1583215896 pthread_mutex_lock(&shutdown_lock);
1583315897 memcached_shutdown = 2;
1583415898 pthread_mutex_unlock(&shutdown_lock);
@@ -15839,7 +15903,7 @@ int main (int argc, char **argv)
1583915903 }
1584015904 mc_logger->log(EXTENSION_LOG_INFO, NULL, "Worker threads terminated.\n");
1584115905
15842- /* 5 ) destroy data structures */
15906+ /* 6 ) destroy data structures */
1584315907#ifdef COMMAND_LOGGING
1584415908 cmdlog_final(); /* finalize command logging */
1584515909#endif
@@ -15850,7 +15914,7 @@ int main (int argc, char **argv)
1585015914 mc_logger->log(EXTENSION_LOG_INFO, NULL, "Memcached engine destroyed.\n");
1585115915
1585215916#ifdef ENABLE_ZK_INTEGRATION
15853- /* 6 ) destroy cluster config structure */
15917+ /* 7 ) destroy cluster config structure */
1585415918 if (arcus_zk_cfg) {
1585515919 arcus_zk_destroy();
1585615920 free(arcus_zk_cfg);
0 commit comments