Better handling of background saving process killed or crashed

This commit is contained in:
antirez 2009-06-16 16:42:20 +02:00
parent c3cb078d46
commit a3b21203d2
2 changed files with 31 additions and 9 deletions

3
TODO
View File

@ -1,12 +1,11 @@
BEFORE REDIS 1.0.0-rc1 BEFORE REDIS 1.0.0-rc1
* Add number of keys for every DB in INFO
* Cover most of the source code with test-redis.tcl * Cover most of the source code with test-redis.tcl
* Remove tmp-.... files when saving child exits in the wrong way, to do so use tmp-pid.rdb as filename so that the parent can rebuild the file name just from the child pid. * Remove tmp-.... files when saving child exits in the wrong way, to do so use tmp-pid.rdb as filename so that the parent can rebuild the file name just from the child pid.
AFTER 1.0 stable release AFTER 1.0 stable release
* Max command payload bytes configurable, with a pretty large default. * Add a command to inspect the currently selected DB index
* Consistent hashing implemented in all the client libraries having an user base * Consistent hashing implemented in all the client libraries having an user base
* SORT: Don't copy the list into a vector when BY argument is constant. * SORT: Don't copy the list into a vector when BY argument is constant.
* SORT ... STORE keyname. Instead to return the SORTed data set it into key. * SORT ... STORE keyname. Instead to return the SORTed data set it into key.

37
redis.c
View File

@ -327,10 +327,11 @@ static int deleteIfVolatile(redisDb *db, robj *key);
static int deleteKey(redisDb *db, robj *key); static int deleteKey(redisDb *db, robj *key);
static time_t getExpire(redisDb *db, robj *key); static time_t getExpire(redisDb *db, robj *key);
static int setExpire(redisDb *db, robj *key, time_t when); static int setExpire(redisDb *db, robj *key, time_t when);
static void updateSalvesWaitingBgsave(int bgsaveerr); static void updateSlavesWaitingBgsave(int bgsaveerr);
static void freeMemoryIfNeeded(void); static void freeMemoryIfNeeded(void);
static int processCommand(redisClient *c); static int processCommand(redisClient *c);
static void setupSigSegvAction(void); static void setupSigSegvAction(void);
static void rdbRemoveTempFile(pid_t childpid);
static void authCommand(redisClient *c); static void authCommand(redisClient *c);
static void pingCommand(redisClient *c); static void pingCommand(redisClient *c);
@ -778,10 +779,11 @@ static int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientD
} else { } else {
redisLog(REDIS_WARNING, redisLog(REDIS_WARNING,
"Background saving terminated by signal"); "Background saving terminated by signal");
rdbRemoveTempFile(server.bgsavechildpid);
} }
server.bgsaveinprogress = 0; server.bgsaveinprogress = 0;
server.bgsavechildpid = -1; server.bgsavechildpid = -1;
updateSalvesWaitingBgsave(exitcode == 0 ? REDIS_OK : REDIS_ERR); updateSlavesWaitingBgsave(exitcode == 0 ? REDIS_OK : REDIS_ERR);
} }
} else { } else {
/* If there is not a background saving in progress check if /* If there is not a background saving in progress check if
@ -1900,7 +1902,7 @@ static int rdbSave(char *filename) {
int j; int j;
time_t now = time(NULL); time_t now = time(NULL);
snprintf(tmpfile,256,"temp-%d.%ld.rdb",(int)time(NULL),(long int)random()); snprintf(tmpfile,256,"temp-%d.rdb", (int) getpid());
fp = fopen(tmpfile,"w"); fp = fopen(tmpfile,"w");
if (!fp) { if (!fp) {
redisLog(REDIS_WARNING, "Failed saving the DB: %s", strerror(errno)); redisLog(REDIS_WARNING, "Failed saving the DB: %s", strerror(errno));
@ -2027,6 +2029,13 @@ static int rdbSaveBackground(char *filename) {
return REDIS_OK; /* unreached */ return REDIS_OK; /* unreached */
} }
static void rdbRemoveTempFile(pid_t childpid) {
char tmpfile[256];
snprintf(tmpfile,256,"temp-%d.rdb", (int) childpid);
unlink(tmpfile);
}
static int rdbLoadType(FILE *fp) { static int rdbLoadType(FILE *fp) {
unsigned char type; unsigned char type;
if (fread(&type,1,1,fp) == 0) return -1; if (fread(&type,1,1,fp) == 0) return -1;
@ -2544,11 +2553,15 @@ static void bgsaveCommand(redisClient *c) {
static void shutdownCommand(redisClient *c) { static void shutdownCommand(redisClient *c) {
redisLog(REDIS_WARNING,"User requested shutdown, saving DB..."); redisLog(REDIS_WARNING,"User requested shutdown, saving DB...");
/* Kill the saving child if there is a background saving in progress.
We want to avoid race conditions, for instance our saving child may
overwrite the synchronous saving did by SHUTDOWN. */
if (server.bgsaveinprogress) { if (server.bgsaveinprogress) {
redisLog(REDIS_WARNING,"There is a live saving child. Killing it!"); redisLog(REDIS_WARNING,"There is a live saving child. Killing it!");
signal(SIGCHLD, SIG_IGN);
kill(server.bgsavechildpid,SIGKILL); kill(server.bgsavechildpid,SIGKILL);
rdbRemoveTempFile(server.bgsavechildpid);
} }
/* SYNC SAVE */
if (rdbSave(server.dbfilename) == REDIS_OK) { if (rdbSave(server.dbfilename) == REDIS_OK) {
if (server.daemonize) if (server.daemonize)
unlink(server.pidfile); unlink(server.pidfile);
@ -2556,7 +2569,10 @@ static void shutdownCommand(redisClient *c) {
redisLog(REDIS_WARNING,"Server exit now, bye bye..."); redisLog(REDIS_WARNING,"Server exit now, bye bye...");
exit(1); exit(1);
} else { } else {
signal(SIGCHLD, SIG_DFL); /* Ooops.. error saving! The best we can do is to continue operating.
* Note that if there was a background saving process, in the next
* cron() Redis will be notified that the background saving aborted,
* handling special stuff like slaves pending for synchronization... */
redisLog(REDIS_WARNING,"Error trying to save the DB, can't exit"); redisLog(REDIS_WARNING,"Error trying to save the DB, can't exit");
addReplySds(c,sdsnew("-ERR can't quit, problems saving the DB\r\n")); addReplySds(c,sdsnew("-ERR can't quit, problems saving the DB\r\n"));
} }
@ -3939,7 +3955,13 @@ static void sendBulkToSlave(aeEventLoop *el, int fd, void *privdata, int mask) {
} }
} }
static void updateSalvesWaitingBgsave(int bgsaveerr) { /* This function is called at the end of every backgrond saving.
* The argument bgsaveerr is REDIS_OK if the background saving succeeded
* otherwise REDIS_ERR is passed to the function.
*
* The goal of this function is to handle slaves waiting for a successful
* background saving in order to perform non-blocking synchronization. */
static void updateSlavesWaitingBgsave(int bgsaveerr) {
listNode *ln; listNode *ln;
int startbgsave = 0; int startbgsave = 0;
@ -4183,7 +4205,7 @@ static struct redisFunctionSym symsTable[] = {
{"deleteKey", (unsigned long)deleteKey}, {"deleteKey", (unsigned long)deleteKey},
{"getExpire", (unsigned long)getExpire}, {"getExpire", (unsigned long)getExpire},
{"setExpire", (unsigned long)setExpire}, {"setExpire", (unsigned long)setExpire},
{"updateSalvesWaitingBgsave", (unsigned long)updateSalvesWaitingBgsave}, {"updateSlavesWaitingBgsave", (unsigned long)updateSlavesWaitingBgsave},
{"freeMemoryIfNeeded", (unsigned long)freeMemoryIfNeeded}, {"freeMemoryIfNeeded", (unsigned long)freeMemoryIfNeeded},
{"authCommand", (unsigned long)authCommand}, {"authCommand", (unsigned long)authCommand},
{"pingCommand", (unsigned long)pingCommand}, {"pingCommand", (unsigned long)pingCommand},
@ -4246,6 +4268,7 @@ static struct redisFunctionSym symsTable[] = {
{"processCommand", (unsigned long)processCommand}, {"processCommand", (unsigned long)processCommand},
{"setupSigSegvAction", (unsigned long)setupSigSegvAction}, {"setupSigSegvAction", (unsigned long)setupSigSegvAction},
{"readQueryFromClient", (unsigned long)readQueryFromClient}, {"readQueryFromClient", (unsigned long)readQueryFromClient},
{"rdbRemoveTempFile", (unsigned long)rdbRemoveTempFile},
{NULL,0} {NULL,0}
}; };