From ff9df842d822f4eb730ee3ae06ea3ea56a3a7f16 Mon Sep 17 00:00:00 2001 From: John Sully Date: Mon, 24 Aug 2020 03:17:31 +0000 Subject: [PATCH] Implement use-fork config (fails with diskless repl) Former-commit-id: f2d5c2bca22e9fd506db123c47b7f60cdded7e2c --- src/config.cpp | 1 + src/rdb.cpp | 112 ++++++++++++++++++++++++++--------- src/server.cpp | 9 ++- src/server.h | 2 + tests/unit/introspection.tcl | 1 + 5 files changed, 94 insertions(+), 31 deletions(-) diff --git a/src/config.cpp b/src/config.cpp index c4b42d1f4..303f496dd 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -2308,6 +2308,7 @@ standardConfig configs[] = { createBoolConfig("delete-on-evict", NULL, MODIFIABLE_CONFIG, cserver.delete_on_evict, 0, NULL, NULL), createBoolConfig("io-threads-do-reads", NULL, IMMUTABLE_CONFIG, fDummy, 0,NULL, NULL), // Not applicable to KeyDB, just there for compatibility createBoolConfig("multi-master-no-forward", NULL, MODIFIABLE_CONFIG, cserver.multimaster_no_forward, 0, validateMultiMasterNoForward, NULL), + createBoolConfig("use-fork", NULL, IMMUTABLE_CONFIG, cserver.fForkBgSave, 0, NULL, NULL), /* String Configs */ createStringConfig("aclfile", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, g_pserver->acl_filename, "", NULL, NULL), diff --git a/src/rdb.cpp b/src/rdb.cpp index 413a39b24..ffbd030bf 100644 --- a/src/rdb.cpp +++ b/src/rdb.cpp @@ -1492,30 +1492,76 @@ void *rdbSaveThread(void *vargs) return (retval == C_OK) ? (void*)0 : (void*)1; } +int rdbSaveBackgroundFork(rdbSaveInfo *rsi) { + pid_t childpid; + + if (hasActiveChildProcess() || g_pserver->rdb_child_pid != -1) return C_ERR; + serverAssert(g_pserver->rdb_child_pid != 10000); + + g_pserver->dirty_before_bgsave = g_pserver->dirty; + g_pserver->lastbgsave_try = time(NULL); + openChildInfoPipe(); + + if ((childpid = redisFork()) == 0) { + int retval; + + /* Child */ + g_pserver->rdb_child_pid = 10000; + redisSetProcTitle("keydb-rdb-bgsave"); + redisSetCpuAffinity(g_pserver->bgsave_cpulist); + retval = rdbSave(nullptr, rsi); + if (retval == C_OK) { + sendChildCOWInfo(CHILD_INFO_TYPE_RDB, "RDB"); + } + exitFromChild((retval == C_OK) ? 0 : 1); + } else { + /* Parent */ + if (childpid == -1) { + closeChildInfoPipe(); + g_pserver->lastbgsave_status = C_ERR; + serverLog(LL_WARNING,"Can't save in background: fork: %s", + strerror(errno)); + return C_ERR; + } + serverLog(LL_NOTICE,"Background saving started by pid %d",childpid); + g_pserver->rdb_save_time_start = time(NULL); + g_pserver->rdb_child_pid = childpid; + g_pserver->rdb_child_type = RDB_CHILD_TYPE_DISK; + return C_OK; + } + return C_OK; /* unreached */ +} + int launchRdbSaveThread(pthread_t &child, rdbSaveInfo *rsi) { - rdbSaveThreadArgs *args = (rdbSaveThreadArgs*)zmalloc(sizeof(rdbSaveThreadArgs) + ((cserver.dbnum-1)*sizeof(redisDbPersistentDataSnapshot*)), MALLOC_LOCAL); - rdbSaveInfo rsiT = RDB_SAVE_INFO_INIT; - if (rsi == nullptr) - rsi = &rsiT; - memcpy(&args->rsi, rsi, sizeof(rdbSaveInfo)); - memcpy(&args->rsi.repl_id, g_pserver->replid, sizeof(g_pserver->replid)); - args->rsi.master_repl_offset = g_pserver->master_repl_offset; - - for (int idb = 0; idb < cserver.dbnum; ++idb) - args->rgpdb[idb] = g_pserver->db[idb]->createSnapshot(getMvccTstamp(), false /* fOptional */); - - g_pserver->rdbThreadVars.tmpfileNum++; - g_pserver->rdbThreadVars.fRdbThreadCancel = false; - if (pthread_create(&child, NULL, rdbSaveThread, args)) { + if (cserver.fForkBgSave) { + return rdbSaveBackgroundFork(rsi); + } else + { + rdbSaveThreadArgs *args = (rdbSaveThreadArgs*)zmalloc(sizeof(rdbSaveThreadArgs) + ((cserver.dbnum-1)*sizeof(redisDbPersistentDataSnapshot*)), MALLOC_LOCAL); + rdbSaveInfo rsiT = RDB_SAVE_INFO_INIT; + if (rsi == nullptr) + rsi = &rsiT; + memcpy(&args->rsi, rsi, sizeof(rdbSaveInfo)); + memcpy(&args->rsi.repl_id, g_pserver->replid, sizeof(g_pserver->replid)); + args->rsi.master_repl_offset = g_pserver->master_repl_offset; + for (int idb = 0; idb < cserver.dbnum; ++idb) - g_pserver->db[idb]->endSnapshot(args->rgpdb[idb]); - zfree(args); - return C_ERR; + args->rgpdb[idb] = g_pserver->db[idb]->createSnapshot(getMvccTstamp(), false /* fOptional */); + + g_pserver->rdbThreadVars.tmpfileNum++; + g_pserver->rdbThreadVars.fRdbThreadCancel = false; + if (pthread_create(&child, NULL, rdbSaveThread, args)) { + for (int idb = 0; idb < cserver.dbnum; ++idb) + g_pserver->db[idb]->endSnapshot(args->rgpdb[idb]); + zfree(args); + return C_ERR; + } } return C_OK; } + int rdbSaveBackground(rdbSaveInfo *rsi) { pthread_t child; long long start; @@ -2743,18 +2789,26 @@ void backgroundSaveDoneHandler(int exitcode, bool fCancelled) { * the cleanup needed. */ void killRDBChild(bool fSynchronous) { serverAssert(GlobalLocksAcquired()); - g_pserver->rdbThreadVars.fRdbThreadCancel = true; - rdbRemoveTempFile(g_pserver->rdbThreadVars.tmpfileNum); - closeChildInfoPipe(); - updateDictResizePolicy(); - if (fSynchronous) - { - aeReleaseLock(); - serverAssert(!GlobalLocksAcquired()); - void *result; - int err = pthread_join(g_pserver->rdbThreadVars.rdb_child_thread, &result); - g_pserver->rdbThreadVars.fRdbThreadCancel = false; - aeAcquireLock(); + + if (cserver.fForkBgSave) { + kill(g_pserver->rdb_child_pid,SIGUSR1); + rdbRemoveTempFile(g_pserver->rdb_child_pid); + closeChildInfoPipe(); + updateDictResizePolicy(); + } else { + g_pserver->rdbThreadVars.fRdbThreadCancel = true; + rdbRemoveTempFile(g_pserver->rdbThreadVars.tmpfileNum); + closeChildInfoPipe(); + updateDictResizePolicy(); + if (fSynchronous) + { + aeReleaseLock(); + serverAssert(!GlobalLocksAcquired()); + void *result; + int err = pthread_join(g_pserver->rdbThreadVars.rdb_child_thread, &result); + g_pserver->rdbThreadVars.fRdbThreadCancel = false; + aeAcquireLock(); + } } } diff --git a/src/server.cpp b/src/server.cpp index 2d47cab05..80af48bad 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1554,7 +1554,7 @@ int redisDbPersistentData::incrementallyRehash() { * for dict.c to resize the hash tables accordingly to the fact we have o not * running childs. */ void updateDictResizePolicy(void) { - if (!hasActiveChildProcess() || g_pserver->FRdbSaveInProgress()) + if (!hasActiveChildProcess() || (g_pserver->FRdbSaveInProgress() && !cserver.fForkBgSave)) dictEnableResize(); else dictDisableResize(); @@ -1946,7 +1946,7 @@ void checkChildrenDone(void) { if (g_pserver->FRdbSaveInProgress() && g_pserver->rdb_pipe_conns) return; - if (g_pserver->FRdbSaveInProgress()) + if (g_pserver->FRdbSaveInProgress() && !cserver.fForkBgSave) { void *rval = nullptr; int err; @@ -1985,6 +1985,11 @@ void checkChildrenDone(void) { strerror(errno), (int) g_pserver->aof_child_pid, (int) g_pserver->module_child_pid); + } else if (pid == g_pserver->rdb_child_pid) { + backgroundSaveDoneHandler(exitcode,bysignal == SIGUSR1); + g_pserver->rdbThreadVars.fRdbThreadCancel = false; + g_pserver->rdb_child_pid = -1; + if (exitcode == 0) receiveChildInfo(); } else if (pid == g_pserver->aof_child_pid) { backgroundRewriteDoneHandler(exitcode,bysignal); if (!bysignal && exitcode == 0) receiveChildInfo(); diff --git a/src/server.h b/src/server.h index 193004b52..fb4c6634e 100644 --- a/src/server.h +++ b/src/server.h @@ -2130,6 +2130,7 @@ struct redisServerConst { int multimaster_no_forward; int storage_memory_model = STORAGE_WRITETHROUGH; char *storage_conf = nullptr; + int fForkBgSave = false; }; struct redisServer { @@ -2297,6 +2298,7 @@ struct redisServer { time_t lastbgsave_try; /* Unix time of last attempted bgsave */ time_t rdb_save_time_last; /* Time used by last RDB save run. */ time_t rdb_save_time_start; /* Current RDB save start time. */ + pid_t rdb_child_pid = -1; /* Used only during fork bgsave */ int rdb_bgsave_scheduled; /* BGSAVE when possible if true. */ int rdb_child_type; /* Type of save by active child. */ int lastbgsave_status; /* C_OK or C_ERR */ diff --git a/tests/unit/introspection.tcl b/tests/unit/introspection.tcl index 4166606a4..42cbfd64d 100644 --- a/tests/unit/introspection.tcl +++ b/tests/unit/introspection.tcl @@ -101,6 +101,7 @@ start_server {tags {"introspection"}} { bgsave_cpulist storage-cache-mode storage-provider-options + use-fork } set configs {}