diff --git a/src/aof.c b/src/aof.c index aa726d33..6a92a0cd 100644 --- a/src/aof.c +++ b/src/aof.c @@ -251,7 +251,10 @@ int startAppendOnly(void) { strerror(errno)); return C_ERR; } - if (rewriteAppendOnlyFileBackground() == C_ERR) { + if (server.rdb_child_pid != -1) { + server.aof_rewrite_scheduled = 1; + serverLog(LL_WARNING,"AOF was enabled but there is already a child process saving an RDB file on disk. An AOF background was scheduled to start when possible."); + } else if (rewriteAppendOnlyFileBackground() == C_ERR) { close(server.aof_fd); serverLog(LL_WARNING,"Redis needs to enable the AOF but can't trigger a background AOF rewrite operation. Check the above logs for more info about the error."); return C_ERR; @@ -1273,7 +1276,7 @@ int rewriteAppendOnlyFileBackground(void) { pid_t childpid; long long start; - if (server.aof_child_pid != -1) return C_ERR; + if (server.aof_child_pid != -1 || server.rdb_child_pid != -1) return C_ERR; if (aofCreatePipes() != C_OK) return C_ERR; start = ustime(); if ((childpid = fork()) == 0) { diff --git a/src/rdb.c b/src/rdb.c index 3b7cec6d..85929794 100644 --- a/src/rdb.c +++ b/src/rdb.c @@ -997,7 +997,7 @@ int rdbSaveBackground(char *filename) { pid_t childpid; long long start; - if (server.rdb_child_pid != -1) return C_ERR; + if (server.aof_child_pid != -1 || server.rdb_child_pid != -1) return C_ERR; server.dirty_before_bgsave = server.dirty; server.lastbgsave_try = time(NULL); @@ -1687,7 +1687,7 @@ int rdbSaveToSlavesSockets(void) { long long start; int pipefds[2]; - if (server.rdb_child_pid != -1) return C_ERR; + if (server.aof_child_pid != -1 || server.rdb_child_pid != -1) return C_ERR; /* Before to fork, create a pipe that will be used in order to * send back to the parent the IDs of the slaves that successfully @@ -1842,11 +1842,33 @@ void saveCommand(client *c) { } } +/* BGSAVE [SCHEDULE] */ void bgsaveCommand(client *c) { + int schedule = 0; + + /* The SCHEDULE option changes the behavior of BGSAVE when an AOF rewrite + * is in progress. Instead of returning an error a BGSAVE gets scheduled. */ + if (c->argc > 1) { + if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"schedule")) { + schedule = 1; + } else { + addReply(c,shared.syntaxerr); + return; + } + } + if (server.rdb_child_pid != -1) { addReplyError(c,"Background save already in progress"); } else if (server.aof_child_pid != -1) { - addReplyError(c,"Can't BGSAVE while AOF log rewriting is in progress"); + if (schedule) { + server.rdb_bgsave_scheduled = 1; + addReplyStatus(c,"Background saving scheduled"); + } else { + addReplyError(c, + "An AOF log rewriting in progress: can't BGSAVE right now. " + "Use BGSAVE SCHEDULE in order to schedule a BGSAVE whenver " + "possible."); + } } else if (rdbSaveBackground(server.rdb_filename) == C_OK) { addReplyStatus(c,"Background saving started"); } else { diff --git a/src/server.c b/src/server.c index abb98edf..f9806d28 100644 --- a/src/server.c +++ b/src/server.c @@ -233,7 +233,7 @@ struct redisCommand redisCommandTable[] = { {"ping",pingCommand,-1,"tF",0,NULL,0,0,0,0,0}, {"echo",echoCommand,2,"F",0,NULL,0,0,0,0,0}, {"save",saveCommand,1,"as",0,NULL,0,0,0,0,0}, - {"bgsave",bgsaveCommand,1,"a",0,NULL,0,0,0,0,0}, + {"bgsave",bgsaveCommand,-1,"a",0,NULL,0,0,0,0,0}, {"bgrewriteaof",bgrewriteaofCommand,1,"a",0,NULL,0,0,0,0,0}, {"shutdown",shutdownCommand,-1,"alt",0,NULL,0,0,0,0,0}, {"lastsave",lastsaveCommand,1,"RF",0,NULL,0,0,0,0,0}, @@ -1113,8 +1113,8 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) { /* Clear the paused clients flag if needed. */ clientsArePaused(); /* Don't check return value, just use the side effect. */ - /* Replication cron function -- used to reconnect to master and - * to detect transfer failures. */ + /* Replication cron function -- used to reconnect to master, + * detect transfer failures, start background RDB transfers and so forth. */ run_with_period(1000) replicationCron(); /* Run the Redis Cluster cron. */ @@ -1132,6 +1132,22 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) { migrateCloseTimedoutSockets(); } + /* Start a scheduled BGSAVE if the corresponding flag is set. This is + * useful when we are forced to postpone a BGSAVE because an AOF + * rewrite is in progress. + * + * Note: this code must be after the replicationCron() call above so + * make sure when refactoring this file to keep this order. This is useful + * because we want to give priority to RDB savings for replication. */ + if (server.rdb_child_pid == -1 && server.aof_child_pid == -1 && + server.rdb_bgsave_scheduled && + (server.unixtime-server.lastbgsave_try > CONFIG_BGSAVE_RETRY_DELAY || + server.lastbgsave_status == C_OK)) + { + if (rdbSaveBackground(server.rdb_filename) == C_OK) + server.rdb_bgsave_scheduled = 0; + } + server.cronloops++; return 1000/server.hz; } @@ -1762,6 +1778,7 @@ void initServer(void) { server.rdb_child_pid = -1; server.aof_child_pid = -1; server.rdb_child_type = RDB_CHILD_TYPE_NONE; + server.rdb_bgsave_scheduled = 0; aofRewriteBufferReset(); server.aof_buf = sdsempty(); server.lastsave = time(NULL); /* At startup we consider the DB saved. */ diff --git a/src/server.h b/src/server.h index 4e34453e..534b59bd 100644 --- a/src/server.h +++ b/src/server.h @@ -918,6 +918,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. */ + 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 */ int stop_writes_on_bgsave_err; /* Don't allow writes if can't BGSAVE */