diff --git a/msvs/setups/chocolatey/Redis.nuspec b/msvs/setups/chocolatey/Redis.nuspec index 20032d74..dd3e52c6 100644 --- a/msvs/setups/chocolatey/Redis.nuspec +++ b/msvs/setups/chocolatey/Redis.nuspec @@ -3,7 +3,7 @@ redis-64 Redis 64-bit - 4.0.2 + 4.0.2.2 Alexis Campailla, Enrico Giordani, Jonathan Pickett Microsoft Open Technologies, Inc. A porting of Redis on Windows 64-bit. diff --git a/msvs/setups/nuget/Redis.nuspec b/msvs/setups/nuget/Redis.nuspec index ae380c36..e44e1fee 100644 --- a/msvs/setups/nuget/Redis.nuspec +++ b/msvs/setups/nuget/Redis.nuspec @@ -3,7 +3,7 @@ redis-64 Redis 64-bit - 4.0.2 + 4.0.2.2 Alexis Campailla, Enrico Giordani, Jonathan Pickett Microsoft Open Technologies, Inc. A porting of Redis on Windows 64-bit. diff --git a/src/aof.c b/src/aof.c index 5d6fc558..ec5dab8e 100644 --- a/src/aof.c +++ b/src/aof.c @@ -51,7 +51,6 @@ #define MAXPATHLEN 1024 #endif - void aofUpdateCurrentSize(void); void aofClosePipes(void); @@ -84,7 +83,7 @@ void aofRewriteBufferReset(void) { listRelease(server.aof_rewrite_buf_blocks); server.aof_rewrite_buf_blocks = listCreate(); - listSetFreeMethod(server.aof_rewrite_buf_blocks,zfree); + listSetFreeMethod(server.aof_rewrite_buf_blocks, zfree); } /* Return the current size of the AOF rewrite buffer. */ @@ -93,8 +92,8 @@ PORT_ULONG aofRewriteBufferSize(void) { listIter li; PORT_ULONG size = 0; - listRewind(server.aof_rewrite_buf_blocks,&li); - while((ln = listNext(&li))) { + listRewind(server.aof_rewrite_buf_blocks, &li); + while ((ln = listNext(&li))) { aofrwblock *block = listNodeValue(ln); size += block->used; } @@ -113,24 +112,25 @@ void aofChildWriteDiffData(aeEventLoop *el, int fd, void *privdata, int mask) { UNUSED(privdata); UNUSED(mask); - while(1) { + while (1) { ln = listFirst(server.aof_rewrite_buf_blocks); block = ln ? ln->value : NULL; if (server.aof_stop_sending_diff || !block) { #ifndef _WIN32 - aeDeleteFileEvent(server.el,server.aof_pipe_write_data_to_child, - AE_WRITABLE); + aeDeleteFileEvent(server.el, server.aof_pipe_write_data_to_child, + AE_WRITABLE); #endif return; } if (block->used > 0) { nwritten = write(server.aof_pipe_write_data_to_child, - block->buf,block->used); + block->buf, block->used); if (nwritten <= 0) return; - memmove(block->buf,block->buf+nwritten,block->used-nwritten); + memmove(block->buf, block->buf + nwritten, block->used - nwritten); block->used -= nwritten; + block->free += nwritten; } - if (block->used == 0) listDelNode(server.aof_rewrite_buf_blocks,ln); + if (block->used == 0) listDelNode(server.aof_rewrite_buf_blocks, ln); } } @@ -139,13 +139,13 @@ void aofRewriteBufferAppend(unsigned char *s, PORT_ULONG len) { listNode *ln = listLast(server.aof_rewrite_buf_blocks); aofrwblock *block = ln ? ln->value : NULL; - while(len) { + while (len) { /* If we already got at least an allocated block, try appending * at least some piece into it. */ if (block) { PORT_ULONG thislen = (block->free < len) ? block->free : len; if (thislen) { /* The current block is not already full. */ - memcpy(block->buf+block->used, s, thislen); + memcpy(block->buf + block->used, s, thislen); block->used += thislen; block->free -= thislen; s += thislen; @@ -159,23 +159,23 @@ void aofRewriteBufferAppend(unsigned char *s, PORT_ULONG len) { block = zmalloc(sizeof(*block)); block->free = AOF_RW_BUF_BLOCK_SIZE; block->used = 0; - listAddNodeTail(server.aof_rewrite_buf_blocks,block); + listAddNodeTail(server.aof_rewrite_buf_blocks, block); /* Log every time we cross more 10 or 100 blocks, respectively * as a notice or warning. */ - numblocks = (int)listLength(server.aof_rewrite_buf_blocks); WIN_PORT_FIX /* cast (int) */ - if (((numblocks+1) % 10) == 0) { - int level = ((numblocks+1) % 100) == 0 ? LL_WARNING : - LL_NOTICE; - serverLog(level,"Background AOF buffer size: %Iu MB", WIN_PORT_FIX /* %lu -> %Iu */ - aofRewriteBufferSize()/(1024*1024)); - } + numblocks = (int) listLength(server.aof_rewrite_buf_blocks); WIN_PORT_FIX /* cast (int) */ + if (((numblocks + 1) % 10) == 0) { + int level = ((numblocks + 1) % 100) == 0 ? LL_WARNING : + LL_NOTICE; + serverLog(level, "Background AOF buffer size: %Iu MB", WIN_PORT_FIX /* %lu -> %Iu */ + aofRewriteBufferSize() / (1024 * 1024)); + } } } #ifndef _WIN32 /* Install a file event to send data to the rewrite child if there is * not one already. */ - if (aeGetFileEvents(server.el,server.aof_pipe_write_data_to_child) == 0) { + if (aeGetFileEvents(server.el, server.aof_pipe_write_data_to_child) == 0) { aeCreateFileEvent(server.el, server.aof_pipe_write_data_to_child, AE_WRITABLE, aofChildWriteDiffData, NULL); } @@ -190,14 +190,14 @@ ssize_t aofRewriteBufferWrite(int fd) { listIter li; ssize_t count = 0; - listRewind(server.aof_rewrite_buf_blocks,&li); - while((ln = listNext(&li))) { + listRewind(server.aof_rewrite_buf_blocks, &li); + while ((ln = listNext(&li))) { aofrwblock *block = listNodeValue(ln); ssize_t nwritten; if (block->used) { - nwritten = write(fd,block->buf,block->used); - if (nwritten != (ssize_t)block->used) { + nwritten = write(fd, block->buf, block->used); + if (nwritten != (ssize_t) block->used) { if (nwritten == 0) errno = EIO; return -1; } @@ -211,10 +211,10 @@ ssize_t aofRewriteBufferWrite(int fd) { * AOF file implementation * ------------------------------------------------------------------------- */ -/* Starts a background task that performs fsync() against the specified - * file descriptor (the one of the AOF file) in another thread. */ + /* Starts a background task that performs fsync() against the specified + * file descriptor (the one of the AOF file) in another thread. */ void aof_background_fsync(int fd) { - bioCreateBackgroundJob(BIO_AOF_FSYNC,(void*)(PORT_LONG)fd,NULL,NULL); + bioCreateBackgroundJob(BIO_AOF_FSYNC, (void*) (PORT_LONG) fd, NULL, NULL); } /* Called when the user switches from "appendonly yes" to "appendonly no" @@ -232,13 +232,13 @@ void stopAppendOnly(void) { if (server.aof_child_pid != -1) { POSIX_ONLY(int statloc;) - serverLog(LL_NOTICE,"Killing running AOF rewrite child: %Id", WIN_PORT_FIX /* %ld -> %Id */ + serverLog(LL_NOTICE, "Killing running AOF rewrite child: %Id", WIN_PORT_FIX /* %ld -> %Id */ (PORT_LONG) server.aof_child_pid); #ifdef _WIN32 AbortForkOperation(); #else - if (kill(server.aof_child_pid,SIGUSR1) != -1) { - while(wait3(&statloc,0,NULL) != server.aof_child_pid); + if (kill(server.aof_child_pid, SIGUSR1) != -1) { + while (wait3(&statloc, 0, NULL) != server.aof_child_pid); } #endif /* reset the buffer accumulating changes while the child saves */ @@ -257,10 +257,10 @@ int startAppendOnly(void) { char cwd[MAXPATHLEN]; /* Current working dir path for error messages. */ server.aof_last_fsync = server.unixtime; - server.aof_fd = open(server.aof_filename,O_WRONLY|O_APPEND|O_CREAT WIN32_ONLY(|_O_BINARY),IF_WIN32(_S_IREAD|_S_IWRITE,0644)); + server.aof_fd = open(server.aof_filename, O_WRONLY | O_APPEND | O_CREAT WIN32_ONLY(| _O_BINARY), IF_WIN32(_S_IREAD | _S_IWRITE, 0644)); serverAssert(server.aof_state == AOF_OFF); if (server.aof_fd == -1) { - char *cwdp = IF_WIN32(_getcwd,getcwd)(cwd,MAXPATHLEN); + char *cwdp = IF_WIN32(_getcwd, getcwd)(cwd, MAXPATHLEN); serverLog(LL_WARNING, "Redis needs to enable the AOF but can't open the " @@ -270,10 +270,14 @@ int startAppendOnly(void) { IF_WIN32(wsa_strerror(errno), 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); WIN32_ONLY(server.aof_fd = -1;) - 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."); + 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; } /* We correctly switched on AOF, now wait for the rewrite to be complete @@ -321,7 +325,8 @@ void flushAppendOnlyFile(int force) { * postponing the flush and return. */ server.aof_flush_postponed_start = server.unixtime; return; - } else if (server.unixtime - server.aof_flush_postponed_start < 2) { + } + else if (server.unixtime - server.aof_flush_postponed_start < 2) { /* We were already waiting for fsync to finish, but for less * than two seconds this is still ok. Postpone again. */ return; @@ -329,7 +334,7 @@ void flushAppendOnlyFile(int force) { /* Otherwise fall trough, and go write since we can't wait * over two seconds. */ server.aof_delayed_fsync++; - serverLog(LL_NOTICE,"Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis."); + serverLog(LL_NOTICE, "Asynchronous AOF fsync is taking too long (disk is busy?). Writing the AOF buffer without waiting for fsync to complete, this may slow down Redis."); } } /* We want to perform a single write. This should be guaranteed atomic @@ -339,7 +344,7 @@ void flushAppendOnlyFile(int force) { * or alike */ latencyStartMonitor(latency); - nwritten = write(server.aof_fd,server.aof_buf,sdslen(server.aof_buf)); + nwritten = write(server.aof_fd, server.aof_buf, sdslen(server.aof_buf)); latencyEndMonitor(latency); /* We want to capture different events for delayed writes: * when the delay happens with a pending fsync, or with a saving child @@ -347,18 +352,20 @@ void flushAppendOnlyFile(int force) { * We also use an additional event name to save all samples which is * useful for graphing / monitoring purposes. */ if (sync_in_progress) { - latencyAddSampleIfNeeded("aof-write-pending-fsync",latency); - } else if (server.aof_child_pid != -1 || server.rdb_child_pid != -1) { - latencyAddSampleIfNeeded("aof-write-active-child",latency); - } else { - latencyAddSampleIfNeeded("aof-write-alone",latency); + latencyAddSampleIfNeeded("aof-write-pending-fsync", latency); } - latencyAddSampleIfNeeded("aof-write",latency); + else if (server.aof_child_pid != -1 || server.rdb_child_pid != -1) { + latencyAddSampleIfNeeded("aof-write-active-child", latency); + } + else { + latencyAddSampleIfNeeded("aof-write-alone", latency); + } + latencyAddSampleIfNeeded("aof-write", latency); /* We performed the write so reset the postponed flush sentinel to zero. */ server.aof_flush_postponed_start = 0; - if (nwritten != (signed)sdslen(server.aof_buf)) { + if (nwritten != (signed) sdslen(server.aof_buf)) { static time_t last_write_error_log = 0; int can_log = 0; @@ -371,27 +378,29 @@ void flushAppendOnlyFile(int force) { /* Log the AOF write error and record the error code. */ if (nwritten == -1) { if (can_log) { - serverLog(LL_WARNING,"Error writing to the AOF file: %s", + serverLog(LL_WARNING, "Error writing to the AOF file: %s", IF_WIN32(wsa_strerror(errno), strerror(errno))); server.aof_last_write_errno = errno; } - } else { + } + else { if (can_log) { - serverLog(LL_WARNING,"Short write while writing to " - "the AOF file: (nwritten=%lld, " - "expected=%lld)", - (PORT_LONGLONG)nwritten, - (PORT_LONGLONG)sdslen(server.aof_buf)); + serverLog(LL_WARNING, "Short write while writing to " + "the AOF file: (nwritten=%lld, " + "expected=%lld)", + (PORT_LONGLONG) nwritten, + (PORT_LONGLONG) sdslen(server.aof_buf)); } if (ftruncate(server.aof_fd, server.aof_current_size) == -1) { if (can_log) { serverLog(LL_WARNING, "Could not remove short write " - "from the append-only file. Redis may refuse " - "to load the AOF the next time it starts. " - "ftruncate: %s", IF_WIN32(wsa_strerror(errno), strerror(errno))); + "from the append-only file. Redis may refuse " + "to load the AOF the next time it starts. " + "ftruncate: %s", IF_WIN32(wsa_strerror(errno), strerror(errno))); } - } else { + } + else { /* If the ftruncate() succeeded we can set nwritten to * -1 since there is no longer partial data into the AOF. */ nwritten = -1; @@ -405,9 +414,10 @@ void flushAppendOnlyFile(int force) { * reply for the client is already in the output buffers, and we * have the contract with the user that on acknowledged write data * is synced on disk. */ - serverLog(LL_WARNING,"Can't recover from AOF write error when the AOF fsync policy is 'always'. Exiting..."); + serverLog(LL_WARNING, "Can't recover from AOF write error when the AOF fsync policy is 'always'. Exiting..."); exit(1); - } else { + } + else { /* Recover from failed write leaving data into the buffer. However * set an error to stop accepting writes as PORT_LONG as the error * condition is not cleared. */ @@ -417,11 +427,12 @@ void flushAppendOnlyFile(int force) { * was no way to undo it with ftruncate(2). */ if (nwritten > 0) { server.aof_current_size += nwritten; - sdsrange(server.aof_buf,(int)nwritten,-1); WIN_PORT_FIX /* cast (int) */ + sdsrange(server.aof_buf, (int) nwritten, -1); WIN_PORT_FIX /* cast (int) */ } return; /* We'll try again on the next call... */ } - } else { + } + else { /* Successful write(2). If AOF was in error state, restore the * OK state and log the event. */ if (server.aof_last_write_status == C_ERR) { @@ -434,9 +445,10 @@ void flushAppendOnlyFile(int force) { /* Re-use AOF buffer when it is small enough. The maximum comes from the * arena size of 4k minus some overhead (but is otherwise arbitrary). */ - if ((sdslen(server.aof_buf)+sdsavail(server.aof_buf)) < 4000) { + if ((sdslen(server.aof_buf) + sdsavail(server.aof_buf)) < 4000) { sdsclear(server.aof_buf); - } else { + } + else { sdsfree(server.aof_buf); server.aof_buf = sdsempty(); } @@ -445,7 +457,7 @@ void flushAppendOnlyFile(int force) { * children doing I/O in the background. */ if (server.aof_no_fsync_on_rewrite && (server.aof_child_pid != -1 || server.rdb_child_pid != -1)) - return; + return; /* Perform the fsync if needed. */ if (server.aof_fsync == AOF_FSYNC_ALWAYS) { @@ -454,10 +466,11 @@ void flushAppendOnlyFile(int force) { latencyStartMonitor(latency); aof_fsync(server.aof_fd); /* Let's try to get this data on the disk */ latencyEndMonitor(latency); - latencyAddSampleIfNeeded("aof-fsync-always",latency); + latencyAddSampleIfNeeded("aof-fsync-always", latency); server.aof_last_fsync = server.unixtime; - } else if ((server.aof_fsync == AOF_FSYNC_EVERYSEC && - server.unixtime > server.aof_last_fsync)) { + } + else if ((server.aof_fsync == AOF_FSYNC_EVERYSEC && + server.unixtime > server.aof_last_fsync)) { if (!sync_in_progress) aof_background_fsync(server.aof_fd); server.aof_last_fsync = server.unixtime; } @@ -469,20 +482,20 @@ sds catAppendOnlyGenericCommand(sds dst, int argc, robj **argv) { robj *o; buf[0] = '*'; - len = 1+ll2string(buf+1,sizeof(buf)-1,argc); + len = 1 + ll2string(buf + 1, sizeof(buf) - 1, argc); buf[len++] = '\r'; buf[len++] = '\n'; - dst = sdscatlen(dst,buf,len); + dst = sdscatlen(dst, buf, len); for (j = 0; j < argc; j++) { o = getDecodedObject(argv[j]); buf[0] = '$'; - len = 1+ll2string(buf+1,sizeof(buf)-1,sdslen(o->ptr)); + len = 1 + ll2string(buf + 1, sizeof(buf) - 1, sdslen(o->ptr)); buf[len++] = '\r'; buf[len++] = '\n'; - dst = sdscatlen(dst,buf,len); - dst = sdscatlen(dst,o->ptr,sdslen(o->ptr)); - dst = sdscatlen(dst,"\r\n",2); + dst = sdscatlen(dst, buf, len); + dst = sdscatlen(dst, o->ptr, sdslen(o->ptr)); + dst = sdscatlen(dst, "\r\n", 2); decrRefCount(o); } return dst; @@ -499,9 +512,9 @@ sds catAppendOnlyExpireAtCommand(sds buf, struct redisCommand *cmd, robj *key, r PORT_LONGLONG when; robj *argv[3]; - /* Make sure we can use strtol */ + /* Make sure we can use strtoll */ seconds = getDecodedObject(seconds); - when = strtol(seconds->ptr,NULL,10); + when = strtoll(seconds->ptr, NULL, 10); /* Convert argument into milliseconds for EXPIRE, SETEX, EXPIREAT */ if (cmd->proc == expireCommand || cmd->proc == setexCommand || cmd->proc == expireatCommand) @@ -516,7 +529,7 @@ sds catAppendOnlyExpireAtCommand(sds buf, struct redisCommand *cmd, robj *key, r } decrRefCount(seconds); - argv[0] = createStringObject("PEXPIREAT",9); + argv[0] = createStringObject("PEXPIREAT", 9); argv[1] = key; argv[2] = createStringObjectFromLongLong(when); buf = catAppendOnlyGenericCommand(buf, 3, argv); @@ -534,57 +547,76 @@ void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int a if (dictid != server.aof_selected_db) { char seldb[64]; - snprintf(seldb,sizeof(seldb),"%d",dictid); - buf = sdscatprintf(buf,"*2\r\n$6\r\nSELECT\r\n$%Iu\r\n%s\r\n", WIN_PORT_FIX /* %lu -> %Iu */ - (PORT_ULONG)strlen(seldb),seldb); + snprintf(seldb, sizeof(seldb), "%d", dictid); + buf = sdscatprintf(buf, "*2\r\n$6\r\nSELECT\r\n$%Iu\r\n%s\r\n", WIN_PORT_FIX /* %lu -> %Iu */ + (PORT_ULONG)strlen(seldb), seldb); server.aof_selected_db = dictid; } if (cmd->proc == expireCommand || cmd->proc == pexpireCommand || cmd->proc == expireatCommand) { /* Translate EXPIRE/PEXPIRE/EXPIREAT into PEXPIREAT */ - buf = catAppendOnlyExpireAtCommand(buf,cmd,argv[1],argv[2]); - } else if (cmd->proc == setexCommand || cmd->proc == psetexCommand) { + buf = catAppendOnlyExpireAtCommand(buf, cmd, argv[1], argv[2]); + } + else if (cmd->proc == setexCommand || cmd->proc == psetexCommand) { /* Translate SETEX/PSETEX to SET and PEXPIREAT */ - tmpargv[0] = createStringObject("SET",3); + tmpargv[0] = createStringObject("SET", 3); tmpargv[1] = argv[1]; tmpargv[2] = argv[3]; - buf = catAppendOnlyGenericCommand(buf,3,tmpargv); + buf = catAppendOnlyGenericCommand(buf, 3, tmpargv); decrRefCount(tmpargv[0]); - buf = catAppendOnlyExpireAtCommand(buf,cmd,argv[1],argv[2]); - } else { + buf = catAppendOnlyExpireAtCommand(buf, cmd, argv[1], argv[2]); + } + else if (cmd->proc == setCommand && argc > 3) { + int i; + robj *exarg = NULL, *pxarg = NULL; + /* Translate SET [EX seconds][PX milliseconds] to SET and PEXPIREAT */ + buf = catAppendOnlyGenericCommand(buf, 3, argv); + for (i = 3; i < argc; i++) { + if (!strcasecmp(argv[i]->ptr, "ex")) exarg = argv[i + 1]; + if (!strcasecmp(argv[i]->ptr, "px")) pxarg = argv[i + 1]; + } + serverAssert(!(exarg && pxarg)); + if (exarg) + buf = catAppendOnlyExpireAtCommand(buf, server.expireCommand, argv[1], + exarg); + if (pxarg) + buf = catAppendOnlyExpireAtCommand(buf, server.pexpireCommand, argv[1], + pxarg); + } + else { /* All the other commands don't need translation or need the * same translation already operated in the command vector * for the replication itself. */ - buf = catAppendOnlyGenericCommand(buf,argc,argv); + buf = catAppendOnlyGenericCommand(buf, argc, argv); } /* Append to the AOF buffer. This will be flushed on disk just before * of re-entering the event loop, so before the client will get a * positive reply about the operation performed. */ if (server.aof_state == AOF_ON) - server.aof_buf = sdscatlen(server.aof_buf,buf,sdslen(buf)); + server.aof_buf = sdscatlen(server.aof_buf, buf, sdslen(buf)); /* If a background append only file rewriting is in progress we want to * accumulate the differences between the child DB and the current one * in a buffer, so that when the child process will do its work we * can append the differences to the new append only file. */ if (server.aof_child_pid != -1) - aofRewriteBufferAppend((unsigned char*)buf,(PORT_ULONG)sdslen(buf)); WIN_PORT_FIX /* cast (PORT_ULONG) */ + aofRewriteBufferAppend((unsigned char*) buf, (PORT_ULONG) sdslen(buf)); WIN_PORT_FIX /* cast (PORT_ULONG) */ - sdsfree(buf); + sdsfree(buf); } /* ---------------------------------------------------------------------------- * AOF loading * ------------------------------------------------------------------------- */ -/* In Redis commands are always executed in the context of a client, so in - * order to load the append only file we need to create a fake client. */ + /* In Redis commands are always executed in the context of a client, so in + * order to load the append only file we need to create a fake client. */ struct client *createFakeClient(void) { struct client *c = zmalloc(sizeof(*c)); - selectDb(c,0); + selectDb(c, 0); c->fd = -1; c->name = NULL; c->querybuf = sdsempty(); @@ -602,8 +634,8 @@ struct client *createFakeClient(void) { c->obuf_soft_limit_reached_time = 0; c->watched_keys = listCreate(); c->peerid = NULL; - listSetFreeMethod(c->reply,decrRefCountVoid); - listSetDupMethod(c->reply,dupClientReplyValue); + listSetFreeMethod(c->reply, decrRefCountVoid); + listSetDupMethod(c->reply, dupClientReplyValue); initClientMultiState(c); return c; } @@ -629,23 +661,27 @@ void freeFakeClient(struct client *c) { * fatal error an error message is logged and the program exists. */ int loadAppendOnlyFile(char *filename) { struct client *fakeClient; - FILE *fp = fopen(filename,IF_WIN32("rb","r")); + FILE *fp = fopen(filename, IF_WIN32("rb", "r")); struct redis_stat sb; int old_aof_state = server.aof_state; PORT_LONG loops = 0; - off_t valid_up_to = 0; /* Offset of the latest well-formed command loaded. */ + off_t valid_up_to = 0; /* Offset of latest well-formed command loaded. */ - if (fp && redis_fstat(fileno(fp),&sb) != -1 && sb.st_size == 0) { + if (fp == NULL) { + serverLog(LL_WARNING, "Fatal error: can't open the append log file for reading: %s", strerror(errno)); + exit(1); + } + + /* Handle a zero-length AOF file as a special case. An emtpy AOF file + * is a valid AOF because an empty server with AOF enabled will create + * a zero length file at startup, that will remain like that if no write + * operation is received. */ + if (fp && redis_fstat(fileno(fp), &sb) != -1 && sb.st_size == 0) { server.aof_current_size = 0; fclose(fp); return C_ERR; } - if (fp == NULL) { - serverLog(LL_WARNING,"Fatal error: can't open the append log file for reading: %s", IF_WIN32(wsa_strerror(errno), strerror(errno))); - exit(1); - } - /* Temporarily disable AOF, to prevent EXEC from feeding a MULTI * to the same file we're about to read. */ server.aof_state = AOF_OFF; @@ -653,7 +689,31 @@ int loadAppendOnlyFile(char *filename) { fakeClient = createFakeClient(); startLoading(fp); - while(1) { + /* Check if this AOF file has an RDB preamble. In that case we need to + * load the RDB file and later continue loading the AOF tail. */ + char sig[5]; /* "REDIS" */ + if (fread(sig, 1, 5, fp) != 5 || memcmp(sig, "REDIS", 5) != 0) { + /* No RDB preamble, seek back at 0 offset. */ + if (fseek(fp, 0, SEEK_SET) == -1) goto readerr; + } + else { + /* RDB preamble. Pass loading the RDB functions. */ + rio rdb; + + serverLog(LL_NOTICE, "Reading RDB preamble from AOF file..."); + if (fseek(fp, 0, SEEK_SET) == -1) goto readerr; + rioInitWithFile(&rdb, fp); + if (rdbLoadRio(&rdb, NULL) != C_OK) { + serverLog(LL_WARNING, "Error reading the RDB preamble of the AOF file, AOF loading aborted"); + goto readerr; + } + else { + serverLog(LL_NOTICE, "Reading the remaining AOF tail..."); + } + } + + /* Read the actual AOF file, in REPL format, command by command. */ + while (1) { int argc, j; PORT_ULONG len; robj **argv; @@ -663,11 +723,11 @@ int loadAppendOnlyFile(char *filename) { /* Serve the clients from time to time */ if (!(loops++ % 1000)) { - loadingProgress((off_t)ftello(fp)); WIN_PORT_FIX /* cast (off_t) */ - processEventsWhileBlocked(); + loadingProgress((off_t) ftello(fp)); WIN_PORT_FIX /* cast (off_t) */ + processEventsWhileBlocked(); } - if (fgets(buf,sizeof(buf),fp) == NULL) { + if (fgets(buf, sizeof(buf), fp) == NULL) { if (feof(fp)) break; else @@ -675,7 +735,7 @@ int loadAppendOnlyFile(char *filename) { } if (buf[0] != '*') goto fmterr; if (buf[1] == '\0') goto readerr; - argc = atoi(buf+1); + argc = atoi(buf + 1); if (argc < 1) goto fmterr; argv = zmalloc(sizeof(robj*)*argc); @@ -683,23 +743,23 @@ int loadAppendOnlyFile(char *filename) { fakeClient->argv = argv; for (j = 0; j < argc; j++) { - if (fgets(buf,sizeof(buf),fp) == NULL) { + if (fgets(buf, sizeof(buf), fp) == NULL) { fakeClient->argc = j; /* Free up to j-1. */ freeFakeClientArgv(fakeClient); goto readerr; } if (buf[0] != '$') goto fmterr; - len = strtol(buf+1,NULL,10); - argsds = sdsnewlen(NULL,len); - if (len && fread(argsds,len,1,fp) == 0) { + len = strtol(buf + 1, NULL, 10); + argsds = sdsnewlen(NULL, len); + if (len && fread(argsds, len, 1, fp) == 0) { sdsfree(argsds); fakeClient->argc = j; /* Free up to j-1. */ freeFakeClientArgv(fakeClient); goto readerr; } - argv[j] = createObject(OBJ_STRING,argsds); - if (fread(buf,2,1,fp) == 0) { - fakeClient->argc = j+1; /* Free up to j. */ + argv[j] = createObject(OBJ_STRING, argsds); + if (fread(buf, 2, 1, fp) == 0) { + fakeClient->argc = j + 1; /* Free up to j. */ freeFakeClientArgv(fakeClient); goto readerr; /* discard CRLF */ } @@ -708,11 +768,12 @@ int loadAppendOnlyFile(char *filename) { /* Command lookup */ cmd = lookupCommand(argv[0]->ptr); if (!cmd) { - serverLog(LL_WARNING,"Unknown command '%s' reading the append only file", (char*)argv[0]->ptr); + serverLog(LL_WARNING, "Unknown command '%s' reading the append only file", (char*) argv[0]->ptr); exit(1); } /* Run the command in the context of a fake client */ + fakeClient->cmd = cmd; cmd->proc(fakeClient); /* The fake client should not have a reply */ @@ -723,6 +784,7 @@ int loadAppendOnlyFile(char *filename) { /* Clean up. Command code may have changed argv/argc so we use the * argv/argc of the client instead of the local variables. */ freeFakeClientArgv(fakeClient); + fakeClient->cmd = NULL; if (server.aof_load_truncated) valid_up_to = ftello(fp); } @@ -742,29 +804,32 @@ loaded_ok: /* DB loaded, cleanup and return C_OK to the caller. */ readerr: /* Read error. If feof(fp) is true, fall through to unexpected EOF. */ if (!feof(fp)) { if (fakeClient) freeFakeClient(fakeClient); /* avoid valgrind warning */ - serverLog(LL_WARNING,"Unrecoverable error reading the append only file: %s", IF_WIN32(wsa_strerror(errno), strerror(errno))); + serverLog(LL_WARNING, "Unrecoverable error reading the append only file: %s", IF_WIN32(wsa_strerror(errno), strerror(errno))); exit(1); } uxeof: /* Unexpected AOF end of file. */ if (server.aof_load_truncated) { - serverLog(LL_WARNING,"!!! Warning: short read while loading the AOF file !!!"); - serverLog(LL_WARNING,"!!! Truncating the AOF at offset %llu !!!", + serverLog(LL_WARNING, "!!! Warning: short read while loading the AOF file !!!"); + serverLog(LL_WARNING, "!!! Truncating the AOF at offset %llu !!!", (PORT_ULONGLONG) valid_up_to); - if (valid_up_to == -1 || truncate(filename,valid_up_to) == -1) { + if (valid_up_to == -1 || truncate(filename, valid_up_to) == -1) { if (valid_up_to == -1) { - serverLog(LL_WARNING,"Last valid command offset is invalid"); - } else { - serverLog(LL_WARNING,"Error truncating the AOF file: %s", + serverLog(LL_WARNING, "Last valid command offset is invalid"); + } + else { + serverLog(LL_WARNING, "Error truncating the AOF file: %s", IF_WIN32(wsa_strerror(errno), strerror(errno))); } - } else { + } + else { /* Make sure the AOF file descriptor points to the end of the * file after the truncate call. */ - if (server.aof_fd != -1 && lseek(server.aof_fd,0,SEEK_END) == -1) { - serverLog(LL_WARNING,"Can't seek the end of the AOF file: %s", + if (server.aof_fd != -1 && lseek(server.aof_fd, 0, SEEK_END) == -1) { + serverLog(LL_WARNING, "Can't seek the end of the AOF file: %s", IF_WIN32(wsa_strerror(errno), strerror(errno))); - } else { + } + else { serverLog(LL_WARNING, "AOF loaded anyway because aof-load-truncated is enabled"); goto loaded_ok; @@ -772,12 +837,12 @@ uxeof: /* Unexpected AOF end of file. */ } } if (fakeClient) freeFakeClient(fakeClient); /* avoid valgrind warning */ - serverLog(LL_WARNING,"Unexpected end of file reading the append only file. You can: 1) Make a backup of your AOF file, then use ./redis-check-aof --fix . 2) Alternatively you can set the 'aof-load-truncated' configuration option to yes and restart the server."); + serverLog(LL_WARNING, "Unexpected end of file reading the append only file. You can: 1) Make a backup of your AOF file, then use ./redis-check-aof --fix . 2) Alternatively you can set the 'aof-load-truncated' configuration option to yes and restart the server."); exit(1); fmterr: /* Format error. */ if (fakeClient) freeFakeClient(fakeClient); /* avoid valgrind warning */ - serverLog(LL_WARNING,"Bad file format reading the append only file: make a backup of your AOF file, then use ./redis-check-aof --fix "); + serverLog(LL_WARNING, "Bad file format reading the append only file: make a backup of your AOF file, then use ./redis-check-aof --fix "); exit(1); } @@ -785,16 +850,18 @@ fmterr: /* Format error. */ * AOF rewrite * ------------------------------------------------------------------------- */ -/* Delegate writing an object to writing a bulk string or bulk PORT_LONGLONG. - * This is not placed in rio.c since that adds the server.h dependency. */ + /* Delegate writing an object to writing a bulk string or bulk PORT_LONGLONG. + * This is not placed in rio.c since that adds the server.h dependency. */ int rioWriteBulkObject(rio *r, robj *obj) { /* Avoid using getDecodedObject to help copy-on-write (we are often * in a child process when this function is called). */ if (obj->encoding == OBJ_ENCODING_INT) { - return (int) rioWriteBulkLongLong(r,(PORT_LONG)obj->ptr); WIN_PORT_FIX /* cast (int) */ - } else if (sdsEncodedObject(obj)) { - return (int) rioWriteBulkString(r,obj->ptr,sdslen(obj->ptr)); WIN_PORT_FIX /* cast (int) */ - } else { + return (int) rioWriteBulkLongLong(r, (PORT_LONG) obj->ptr); WIN_PORT_FIX /* cast (int) */ + } + else if (sdsEncodedObject(obj)) { + return (int) rioWriteBulkString(r, obj->ptr, sdslen(obj->ptr)); WIN_PORT_FIX /* cast (int) */ + } + else { serverPanic("Unknown string encoding"); } } @@ -809,24 +876,27 @@ int rewriteListObject(rio *r, robj *key, robj *o) { quicklistIter *li = quicklistGetIterator(list, AL_START_HEAD); quicklistEntry entry; - while (quicklistNext(li,&entry)) { + while (quicklistNext(li, &entry)) { if (count == 0) { int cmd_items = (items > AOF_REWRITE_ITEMS_PER_CMD) ? AOF_REWRITE_ITEMS_PER_CMD : items; - if (rioWriteBulkCount(r,'*',2+cmd_items) == 0) return 0; - if (rioWriteBulkString(r,"RPUSH",5) == 0) return 0; - if (rioWriteBulkObject(r,key) == 0) return 0; + if (rioWriteBulkCount(r, '*', 2 + cmd_items) == 0) return 0; + if (rioWriteBulkString(r, "RPUSH", 5) == 0) return 0; + if (rioWriteBulkObject(r, key) == 0) return 0; } + if (entry.value) { - if (rioWriteBulkString(r,(char*)entry.value,entry.sz) == 0) return 0; - } else { - if (rioWriteBulkLongLong(r,entry.longval) == 0) return 0; + if (rioWriteBulkString(r, (char*) entry.value, entry.sz) == 0) return 0; + } + else { + if (rioWriteBulkLongLong(r, entry.longval) == 0) return 0; } if (++count == AOF_REWRITE_ITEMS_PER_CMD) count = 0; items--; } quicklistReleaseIterator(li); - } else { + } + else { serverPanic("Unknown list encoding"); } return 1; @@ -841,39 +911,41 @@ int rewriteSetObject(rio *r, robj *key, robj *o) { int ii = 0; int64_t llval; - while(intsetGet(o->ptr,ii++,&llval)) { + while (intsetGet(o->ptr, ii++, &llval)) { if (count == 0) { int cmd_items = (items > AOF_REWRITE_ITEMS_PER_CMD) ? - AOF_REWRITE_ITEMS_PER_CMD : (int)items; WIN_PORT_FIX /* cast (int) */ + AOF_REWRITE_ITEMS_PER_CMD : (int) items; WIN_PORT_FIX /* cast (int) */ - if (rioWriteBulkCount(r,'*',2+cmd_items) == 0) return 0; - if (rioWriteBulkString(r,"SADD",4) == 0) return 0; - if (rioWriteBulkObject(r,key) == 0) return 0; + if (rioWriteBulkCount(r, '*', 2 + cmd_items) == 0) return 0; + if (rioWriteBulkString(r, "SADD", 4) == 0) return 0; + if (rioWriteBulkObject(r, key) == 0) return 0; } - if (rioWriteBulkLongLong(r,llval) == 0) return 0; + if (rioWriteBulkLongLong(r, llval) == 0) return 0; if (++count == AOF_REWRITE_ITEMS_PER_CMD) count = 0; items--; } - } else if (o->encoding == OBJ_ENCODING_HT) { + } + else if (o->encoding == OBJ_ENCODING_HT) { dictIterator *di = dictGetIterator(o->ptr); dictEntry *de; - while((de = dictNext(di)) != NULL) { - robj *eleobj = dictGetKey(de); + while ((de = dictNext(di)) != NULL) { + sds ele = dictGetKey(de); if (count == 0) { int cmd_items = (items > AOF_REWRITE_ITEMS_PER_CMD) ? - AOF_REWRITE_ITEMS_PER_CMD : (int)items; WIN_PORT_FIX /* cast (int) */ + AOF_REWRITE_ITEMS_PER_CMD : (int) items; WIN_PORT_FIX /* cast (int) */ - if (rioWriteBulkCount(r,'*',2+cmd_items) == 0) return 0; - if (rioWriteBulkString(r,"SADD",4) == 0) return 0; - if (rioWriteBulkObject(r,key) == 0) return 0; + if (rioWriteBulkCount(r, '*', 2 + cmd_items) == 0) return 0; + if (rioWriteBulkString(r, "SADD", 4) == 0) return 0; + if (rioWriteBulkObject(r, key) == 0) return 0; } - if (rioWriteBulkObject(r,eleobj) == 0) return 0; + if (rioWriteBulkString(r, ele, sdslen(ele)) == 0) return 0; if (++count == AOF_REWRITE_ITEMS_PER_CMD) count = 0; items--; } dictReleaseIterator(di); - } else { + } + else { serverPanic("Unknown set encoding"); } return 1; @@ -892,57 +964,60 @@ int rewriteSortedSetObject(rio *r, robj *key, robj *o) { PORT_LONGLONG vll; double score; - eptr = ziplistIndex(zl,0); + eptr = ziplistIndex(zl, 0); serverAssert(eptr != NULL); - sptr = ziplistNext(zl,eptr); + sptr = ziplistNext(zl, eptr); serverAssert(sptr != NULL); while (eptr != NULL) { - serverAssert(ziplistGet(eptr,&vstr,&vlen,&vll)); + serverAssert(ziplistGet(eptr, &vstr, &vlen, &vll)); score = zzlGetScore(sptr); if (count == 0) { int cmd_items = (items > AOF_REWRITE_ITEMS_PER_CMD) ? - AOF_REWRITE_ITEMS_PER_CMD : (int)items; WIN_PORT_FIX /* cast (int) */ + AOF_REWRITE_ITEMS_PER_CMD : (int) items; WIN_PORT_FIX /* cast (int) */ - if (rioWriteBulkCount(r,'*',2+cmd_items*2) == 0) return 0; - if (rioWriteBulkString(r,"ZADD",4) == 0) return 0; - if (rioWriteBulkObject(r,key) == 0) return 0; + if (rioWriteBulkCount(r, '*', 2 + cmd_items * 2) == 0) return 0; + if (rioWriteBulkString(r, "ZADD", 4) == 0) return 0; + if (rioWriteBulkObject(r, key) == 0) return 0; } - if (rioWriteBulkDouble(r,score) == 0) return 0; + if (rioWriteBulkDouble(r, score) == 0) return 0; if (vstr != NULL) { - if (rioWriteBulkString(r,(char*)vstr,vlen) == 0) return 0; - } else { - if (rioWriteBulkLongLong(r,vll) == 0) return 0; + if (rioWriteBulkString(r, (char*) vstr, vlen) == 0) return 0; } - zzlNext(zl,&eptr,&sptr); + else { + if (rioWriteBulkLongLong(r, vll) == 0) return 0; + } + zzlNext(zl, &eptr, &sptr); if (++count == AOF_REWRITE_ITEMS_PER_CMD) count = 0; items--; } - } else if (o->encoding == OBJ_ENCODING_SKIPLIST) { + } + else if (o->encoding == OBJ_ENCODING_SKIPLIST) { zset *zs = o->ptr; dictIterator *di = dictGetIterator(zs->dict); dictEntry *de; - while((de = dictNext(di)) != NULL) { - robj *eleobj = dictGetKey(de); + while ((de = dictNext(di)) != NULL) { + sds ele = dictGetKey(de); double *score = dictGetVal(de); if (count == 0) { int cmd_items = (items > AOF_REWRITE_ITEMS_PER_CMD) ? - AOF_REWRITE_ITEMS_PER_CMD : (int)items; WIN_PORT_FIX /* cast (int) */ + AOF_REWRITE_ITEMS_PER_CMD : (int) items; WIN_PORT_FIX /* cast (int) */ - if (rioWriteBulkCount(r,'*',2+cmd_items*2) == 0) return 0; - if (rioWriteBulkString(r,"ZADD",4) == 0) return 0; - if (rioWriteBulkObject(r,key) == 0) return 0; + if (rioWriteBulkCount(r, '*', 2 + cmd_items * 2) == 0) return 0; + if (rioWriteBulkString(r, "ZADD", 4) == 0) return 0; + if (rioWriteBulkObject(r, key) == 0) return 0; } - if (rioWriteBulkDouble(r,*score) == 0) return 0; - if (rioWriteBulkObject(r,eleobj) == 0) return 0; + if (rioWriteBulkDouble(r, *score) == 0) return 0; + if (rioWriteBulkString(r, ele, sdslen(ele)) == 0) return 0; if (++count == AOF_REWRITE_ITEMS_PER_CMD) count = 0; items--; } dictReleaseIterator(di); - } else { + } + else { serverPanic("Unknown sorted zset encoding"); } return 1; @@ -962,16 +1037,15 @@ static int rioWriteHashIteratorCursor(rio *r, hashTypeIterator *hi, int what) { hashTypeCurrentFromZiplist(hi, what, &vstr, &vlen, &vll); if (vstr) { - return (int)rioWriteBulkString(r, (char*)vstr, vlen); WIN_PORT_FIX /* cast (int) */ - } else { - return (int)rioWriteBulkLongLong(r, vll); WIN_PORT_FIX /* cast (int) */ + return (int) rioWriteBulkString(r, (char*) vstr, vlen); WIN_PORT_FIX /* cast (int) */ } - - } else if (hi->encoding == OBJ_ENCODING_HT) { - robj *value; - - hashTypeCurrentFromHashTable(hi, what, &value); - return rioWriteBulkObject(r, value); + else { + return (int) rioWriteBulkLongLong(r, vll); WIN_PORT_FIX /* cast (int) */ + } + } + else if (hi->encoding == OBJ_ENCODING_HT) { + sds value = hashTypeCurrentFromHashTable(hi, what); + return rioWriteBulkString(r, value, sdslen(value)); } serverPanic("Unknown hash encoding"); @@ -987,12 +1061,12 @@ int rewriteHashObject(rio *r, robj *key, robj *o) { hi = hashTypeInitIterator(o); while (hashTypeNext(hi) != C_ERR) { if (count == 0) { - int cmd_items = (int)((items > AOF_REWRITE_ITEMS_PER_CMD) ? WIN_PORT_FIX /* cast (int) */ + int cmd_items = (int) ((items > AOF_REWRITE_ITEMS_PER_CMD) ? WIN_PORT_FIX /* cast (int) */ AOF_REWRITE_ITEMS_PER_CMD : items); - if (rioWriteBulkCount(r,'*',2+cmd_items*2) == 0) return 0; - if (rioWriteBulkString(r,"HMSET",5) == 0) return 0; - if (rioWriteBulkObject(r,key) == 0) return 0; + if (rioWriteBulkCount(r, '*', 2 + cmd_items * 2) == 0) return 0; + if (rioWriteBulkString(r, "HMSET", 5) == 0) return 0; + if (rioWriteBulkObject(r, key) == 0) return 0; } if (rioWriteHashIteratorCursor(r, hi, OBJ_HASH_KEY) == 0) return 0; @@ -1014,13 +1088,98 @@ ssize_t aofReadDiffFromParent(void) { ssize_t nread, total = 0; while ((nread = - read(server.aof_pipe_read_data_from_parent,buf,sizeof(buf))) > 0) { - server.aof_child_diff = sdscatlen(server.aof_child_diff,buf,nread); + read(server.aof_pipe_read_data_from_parent, buf, sizeof(buf))) > 0) { + server.aof_child_diff = sdscatlen(server.aof_child_diff, buf, nread); total += nread; } return total; } +int rewriteAppendOnlyFileRio(rio *aof) { + dictIterator *di = NULL; + dictEntry *de; + size_t processed = 0; + PORT_LONGLONG now = mstime(); + int j; + + for (j = 0; j < server.dbnum; j++) { + char selectcmd[] = "*2\r\n$6\r\nSELECT\r\n"; + redisDb *db = server.db + j; + dict *d = db->dict; + if (dictSize(d) == 0) continue; + di = dictGetSafeIterator(d); + + /* SELECT the new DB */ + if (rioWrite(aof, selectcmd, sizeof(selectcmd) - 1) == 0) goto werr; + if (rioWriteBulkLongLong(aof, j) == 0) goto werr; + + /* Iterate this DB writing every entry */ + while ((de = dictNext(di)) != NULL) { + sds keystr; + robj key, *o; + PORT_LONGLONG expiretime; + + keystr = dictGetKey(de); + o = dictGetVal(de); + initStaticStringObject(key, keystr); + + expiretime = getExpire(db, &key); + + /* If this key is already expired skip it */ + if (expiretime != -1 && expiretime < now) continue; + + /* Save the key and associated value */ + if (o->type == OBJ_STRING) { + /* Emit a SET command */ + char cmd[] = "*3\r\n$3\r\nSET\r\n"; + if (rioWrite(aof, cmd, sizeof(cmd) - 1) == 0) goto werr; + /* Key and value */ + if (rioWriteBulkObject(aof, &key) == 0) goto werr; + if (rioWriteBulkObject(aof, o) == 0) goto werr; + } + else if (o->type == OBJ_LIST) { + if (rewriteListObject(aof, &key, o) == 0) goto werr; + } + else if (o->type == OBJ_SET) { + if (rewriteSetObject(aof, &key, o) == 0) goto werr; + } + else if (o->type == OBJ_ZSET) { + if (rewriteSortedSetObject(aof, &key, o) == 0) goto werr; + } + else if (o->type == OBJ_HASH) { + if (rewriteHashObject(aof, &key, o) == 0) goto werr; + } + /* TODO: [tporadowski] modules support + else if (o->type == OBJ_MODULE) { + if (rewriteModuleObject(aof, &key, o) == 0) goto werr; + } + */ + else { + serverPanic("Unknown object type"); + } + /* Save the expire time */ + if (expiretime != -1) { + char cmd[] = "*3\r\n$9\r\nPEXPIREAT\r\n"; + if (rioWrite(aof, cmd, sizeof(cmd) - 1) == 0) goto werr; + if (rioWriteBulkObject(aof, &key) == 0) goto werr; + if (rioWriteBulkLongLong(aof, expiretime) == 0) goto werr; + } + /* Read some diff from the parent process from time to time. */ + if (aof->processed_bytes > processed + AOF_READ_DIFF_INTERVAL_BYTES) { + processed = aof->processed_bytes; + aofReadDiffFromParent(); + } + } + dictReleaseIterator(di); + di = NULL; + } + return C_OK; + +werr: + if (di) dictReleaseIterator(di); + return C_ERR; +} + /* Write a sequence of commands able to fully rebuild the dataset into * "filename". Used both by REWRITEAOF and BGREWRITEAOF. * @@ -1029,93 +1188,35 @@ ssize_t aofReadDiffFromParent(void) { * and ZADD. However at max AOF_REWRITE_ITEMS_PER_CMD items per time * are inserted using a single command. */ int rewriteAppendOnlyFile(char *filename) { - dictIterator *di = NULL; - dictEntry *de; rio aof; FILE *fp; char tmpfile[256]; - int j; - PORT_LONGLONG now = mstime(); char byte; - size_t processed = 0; /* Note that we have to use a different temp name here compared to the * one used by rewriteAppendOnlyFileBackground() function. */ - snprintf(tmpfile,256,"temp-rewriteaof-%d.aof", (int) getpid()); - fp = fopen(tmpfile,IF_WIN32("wb","w")); + snprintf(tmpfile, 256, "temp-rewriteaof-%d.aof", (int) getpid()); + fp = fopen(tmpfile, IF_WIN32("wb", "w")); if (!fp) { serverLog(LL_WARNING, "Opening the temp file for AOF rewrite in rewriteAppendOnlyFile(): %s", IF_WIN32(wsa_strerror(errno), strerror(errno))); return C_ERR; } server.aof_child_diff = sdsempty(); - rioInitWithFile(&aof,fp); + rioInitWithFile(&aof, fp); + if (server.aof_rewrite_incremental_fsync) - rioSetAutoSync(&aof,AOF_AUTOSYNC_BYTES); - for (j = 0; j < server.dbnum; j++) { - char selectcmd[] = "*2\r\n$6\r\nSELECT\r\n"; - redisDb *db = server.db+j; - dict *d = db->dict; - if (dictSize(d) == 0) continue; - di = dictGetSafeIterator(d); - if (!di) { - fclose(fp); - return C_ERR; + rioSetAutoSync(&aof, AOF_AUTOSYNC_BYTES); + + if (server.aof_use_rdb_preamble) { + int error; + if (rdbSaveRio(&aof, &error, RDB_SAVE_AOF_PREAMBLE, NULL) == C_ERR) { + errno = error; + goto werr; } - - /* SELECT the new DB */ - if (rioWrite(&aof,selectcmd,sizeof(selectcmd)-1) == 0) goto werr; - if (rioWriteBulkLongLong(&aof,j) == 0) goto werr; - - /* Iterate this DB writing every entry */ - while((de = dictNext(di)) != NULL) { - sds keystr; - robj key, *o; - PORT_LONGLONG expiretime; - - keystr = dictGetKey(de); - o = dictGetVal(de); - initStaticStringObject(key,keystr); - - expiretime = getExpire(db,&key); - - /* If this key is already expired skip it */ - if (expiretime != -1 && expiretime < now) continue; - - /* Save the key and associated value */ - if (o->type == OBJ_STRING) { - /* Emit a SET command */ - char cmd[]="*3\r\n$3\r\nSET\r\n"; - if (rioWrite(&aof,cmd,sizeof(cmd)-1) == 0) goto werr; - /* Key and value */ - if (rioWriteBulkObject(&aof,&key) == 0) goto werr; - if (rioWriteBulkObject(&aof,o) == 0) goto werr; - } else if (o->type == OBJ_LIST) { - if (rewriteListObject(&aof,&key,o) == 0) goto werr; - } else if (o->type == OBJ_SET) { - if (rewriteSetObject(&aof,&key,o) == 0) goto werr; - } else if (o->type == OBJ_ZSET) { - if (rewriteSortedSetObject(&aof,&key,o) == 0) goto werr; - } else if (o->type == OBJ_HASH) { - if (rewriteHashObject(&aof,&key,o) == 0) goto werr; - } else { - serverPanic("Unknown object type"); - } - /* Save the expire time */ - if (expiretime != -1) { - char cmd[]="*3\r\n$9\r\nPEXPIREAT\r\n"; - if (rioWrite(&aof,cmd,sizeof(cmd)-1) == 0) goto werr; - if (rioWriteBulkObject(&aof,&key) == 0) goto werr; - if (rioWriteBulkLongLong(&aof,expiretime) == 0) goto werr; - } - /* Read some diff from the parent process from time to time. */ - if (aof.processed_bytes > processed+1024*10) { - processed = aof.processed_bytes; - aofReadDiffFromParent(); - } - } - dictReleaseIterator(di); - di = NULL; + } + else { + if (rewriteAppendOnlyFileRio(&aof) == C_ERR) goto werr; } /* Do an initial slow fsync here while the parent is still sending @@ -1131,7 +1232,7 @@ int rewriteAppendOnlyFile(char *filename) { * happens after 20 ms without new data). */ int nodata = 0; mstime_t start = mstime(); - while(mstime()-start < 1000 && nodata < 20) { + while (mstime() - start < 1000 && nodata < 20) { if (aeWait(server.aof_pipe_read_data_from_parent, AE_READABLE, 1) <= 0) { nodata++; @@ -1143,17 +1244,17 @@ int rewriteAppendOnlyFile(char *filename) { } /* Ask the master to stop sending diffs. */ - if (write(server.aof_pipe_write_ack_to_parent,"!",1) != 1) goto werr; + if (write(server.aof_pipe_write_ack_to_parent, "!", 1) != 1) goto werr; #ifndef _WIN32 - if (anetNonBlock(NULL,server.aof_pipe_read_ack_from_parent) != ANET_OK) + if (anetNonBlock(NULL, server.aof_pipe_read_ack_from_parent) != ANET_OK) goto werr; #endif /* We read the ACK from the server using a 10 seconds timeout. Normally * it should reply ASAP, but just in case we lose its reply, we are sure * the child will eventually get terminated. */ - if (syncRead(server.aof_pipe_read_ack_from_parent,&byte,1,5000) != 1 || + if (syncRead(server.aof_pipe_read_ack_from_parent, &byte, 1, 5000) != 1 || byte != '!') goto werr; - serverLog(LL_NOTICE,"Parent agreed to stop sending diffs. Finalizing AOF..."); + serverLog(LL_NOTICE, "Parent agreed to stop sending diffs. Finalizing AOF..."); /* Read the final diff if any. */ aofReadDiffFromParent(); @@ -1161,8 +1262,8 @@ int rewriteAppendOnlyFile(char *filename) { /* Write the received diff to the file. */ serverLog(LL_NOTICE, "Concatenating %.2f MB of AOF diff received from parent.", - (double) sdslen(server.aof_child_diff) / (1024*1024)); - if (rioWrite(&aof,server.aof_child_diff,sdslen(server.aof_child_diff)) == 0) + (double) sdslen(server.aof_child_diff) / (1024 * 1024)); + if (rioWrite(&aof, server.aof_child_diff, sdslen(server.aof_child_diff)) == 0) goto werr; /* Make sure data will not remain on the OS's output buffers */ @@ -1172,19 +1273,18 @@ int rewriteAppendOnlyFile(char *filename) { /* Use RENAME to make sure the DB file is changed atomically only * if the generate DB file is ok. */ - if (rename(tmpfile,filename) == -1) { - serverLog(LL_WARNING,"Error moving temp append only file on the final destination: %s", IF_WIN32(wsa_strerror(errno), strerror(errno))); + if (rename(tmpfile, filename) == -1) { + serverLog(LL_WARNING, "Error moving temp append only file on the final destination: %s", IF_WIN32(wsa_strerror(errno), strerror(errno))); unlink(tmpfile); return C_ERR; } - serverLog(LL_NOTICE,"SYNC append only file rewrite performed"); + serverLog(LL_NOTICE, "SYNC append only file rewrite performed"); return C_OK; werr: - serverLog(LL_WARNING,"Write error writing append only file on disk: %s", IF_WIN32(wsa_strerror(errno), strerror(errno))); + serverLog(LL_WARNING, "Write error writing append only file on disk: %s", IF_WIN32(wsa_strerror(errno), strerror(errno))); fclose(fp); unlink(tmpfile); - if (di) dictReleaseIterator(di); return C_ERR; } @@ -1192,31 +1292,31 @@ werr: * AOF rewrite pipes for IPC * -------------------------------------------------------------------------- */ -/* This event handler is called when the AOF rewriting child sends us a - * single '!' char to signal we should stop sending buffer diffs. The - * parent sends a '!' as well to acknowledge. */ + /* This event handler is called when the AOF rewriting child sends us a + * single '!' char to signal we should stop sending buffer diffs. The + * parent sends a '!' as well to acknowledge. */ void aofChildPipeReadable(aeEventLoop *el, int fd, void *privdata, int mask) { char byte; UNUSED(el); UNUSED(privdata); UNUSED(mask); - if (read(fd,&byte,1) == 1 && byte == '!') { - serverLog(LL_NOTICE,"AOF rewrite child asks to stop sending diffs."); + if (read(fd, &byte, 1) == 1 && byte == '!') { + serverLog(LL_NOTICE, "AOF rewrite child asks to stop sending diffs."); server.aof_stop_sending_diff = 1; - if (write(server.aof_pipe_write_ack_to_child,"!",1) != 1) { + if (write(server.aof_pipe_write_ack_to_child, "!", 1) != 1) { /* If we can't send the ack, inform the user, but don't try again * since in the other side the children will use a timeout if the * kernel can't buffer our write, or, the children was * terminated. */ - serverLog(LL_WARNING,"Can't send ACK to AOF child: %s", + serverLog(LL_WARNING, "Can't send ACK to AOF child: %s", IF_WIN32(wsa_strerror(errno), strerror(errno))); } } #ifndef _WIN32 /* Remove the handler since this can be called only one time during a * rewrite. */ - aeDeleteFileEvent(server.el,server.aof_pipe_read_ack_from_child,AE_READABLE); + aeDeleteFileEvent(server.el, server.aof_pipe_read_ack_from_child, AE_READABLE); #endif } @@ -1226,16 +1326,16 @@ void aofChildPipeReadable(aeEventLoop *el, int fd, void *privdata, int mask) { * the rewrite so no more data should be written, and another for the * parent to acknowledge it understood this new condition. */ int aofCreatePipes(void) { - int fds[6] = {-1, -1, -1, -1, -1, -1}; + int fds[6] = { -1, -1, -1, -1, -1, -1 }; int j; if (pipe(fds) == -1) goto error; /* parent -> children data. */ - if (pipe(fds+2) == -1) goto error; /* children -> parent ack. */ - if (pipe(fds+4) == -1) goto error; /* children -> parent ack. */ + if (pipe(fds + 2) == -1) goto error; /* children -> parent ack. */ + if (pipe(fds + 4) == -1) goto error; /* children -> parent ack. */ /* Parent -> children data is non blocking. */ #ifndef WIN32 - if (anetNonBlock(NULL,fds[0]) != ANET_OK) goto error; - if (anetNonBlock(NULL,fds[1]) != ANET_OK) goto error; + if (anetNonBlock(NULL, fds[0]) != ANET_OK) goto error; + if (anetNonBlock(NULL, fds[1]) != ANET_OK) goto error; if (aeCreateFileEvent(server.el, fds[2], AE_READABLE, aofChildPipeReadable, NULL) == AE_ERR) goto error; #else if (FDAPI_PipeSetNonBlock(fds[0], 1) != 0) goto error; @@ -1253,15 +1353,15 @@ int aofCreatePipes(void) { return C_OK; error: - serverLog(LL_WARNING,"Error opening /setting AOF rewrite IPC pipes: %s", + serverLog(LL_WARNING, "Error opening /setting AOF rewrite IPC pipes: %s", IF_WIN32(wsa_strerror(errno), strerror(errno))); - for (j = 0; j < 6; j++) if(fds[j] != -1) close(fds[j]); + for (j = 0; j < 6; j++) if (fds[j] != -1) close(fds[j]); return C_ERR; } void aofClosePipes(void) { - aeDeleteFileEvent(server.el,server.aof_pipe_read_ack_from_child,AE_READABLE); - aeDeleteFileEvent(server.el,server.aof_pipe_write_data_to_child,AE_WRITABLE); + aeDeleteFileEvent(server.el, server.aof_pipe_read_ack_from_child, AE_READABLE); + aeDeleteFileEvent(server.el, server.aof_pipe_write_data_to_child, AE_WRITABLE); close(server.aof_pipe_write_data_to_child); close(server.aof_pipe_read_data_from_parent); close(server.aof_pipe_write_ack_to_parent); @@ -1274,24 +1374,25 @@ void aofClosePipes(void) { * AOF background rewrite * ------------------------------------------------------------------------- */ -/* This is how rewriting of the append only file in background works: - * - * 1) The user calls BGREWRITEAOF - * 2) Redis calls this function, that forks(): - * 2a) the child rewrite the append only file in a temp file. - * 2b) the parent accumulates differences in server.aof_rewrite_buf. - * 3) When the child finished '2a' exists. - * 4) The parent will trap the exit code, if it's OK, will append the - * data accumulated into server.aof_rewrite_buf into the temp file, and - * finally will rename(2) the temp file in the actual file name. - * The the new file is reopened as the new append only file. Profit! - */ + /* This is how rewriting of the append only file in background works: + * + * 1) The user calls BGREWRITEAOF + * 2) Redis calls this function, that forks(): + * 2a) the child rewrite the append only file in a temp file. + * 2b) the parent accumulates differences in server.aof_rewrite_buf. + * 3) When the child finished '2a' exists. + * 4) The parent will trap the exit code, if it's OK, will append the + * data accumulated into server.aof_rewrite_buf into the temp file, and + * finally will rename(2) the temp file in the actual file name. + * The the new file is reopened as the new append only file. Profit! + */ int rewriteAppendOnlyFileBackground(void) { pid_t childpid; PORT_LONGLONG 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; + openChildInfoPipe(); start = ustime(); #ifndef _WIN32 @@ -1304,42 +1405,49 @@ int rewriteAppendOnlyFileBackground(void) { closeListeningSockets(0); redisSetProcTitle("redis-aof-rewrite"); #endif - snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", (int) getpid()); + snprintf(tmpfile, 256, "temp-rewriteaof-bg-%d.aof", (int) getpid()); #ifdef _WIN32 childpid = BeginForkOperation_Aof(server.aof_pipe_write_ack_to_parent, - server.aof_pipe_read_ack_from_parent, - server.aof_pipe_read_data_from_parent, - tmpfile, - &server, - sizeof(server), - dictGetHashFunctionSeed()); + server.aof_pipe_read_ack_from_parent, + server.aof_pipe_read_data_from_parent, + tmpfile, + &server, + sizeof(server), + dictGetHashFunctionSeed()); #else if (rewriteAppendOnlyFile(tmpfile) == C_OK) { - size_t private_dirty = zmalloc_get_private_dirty(); + size_t private_dirty = zmalloc_get_private_dirty(-1); if (private_dirty) { serverLog(LL_NOTICE, - "AOF rewrite: %lu MB of memory used by copy-on-write", - private_dirty/(1024*1024)); + "AOF rewrite: %Iu MB of memory used by copy-on-write", WIN_PORT_FIX /* %lu -> %Iu */ + private_dirty / (1024 * 1024)); } + + server.child_info_data.cow_size = private_dirty; + sendChildInfo(CHILD_INFO_TYPE_AOF); exitFromChild(0); - } else { + } + else { exitFromChild(1); } - } else { + } + else { #endif /* Parent */ - server.stat_fork_time = ustime()-start; - server.stat_fork_rate = (double) zmalloc_used_memory() * 1000000 / server.stat_fork_time / (1024*1024*1024); /* GB per second. */ - latencyAddSampleIfNeeded("fork",server.stat_fork_time/1000); + server.stat_fork_time = ustime() - start; + server.stat_fork_rate = (double) zmalloc_used_memory() * 1000000 / server.stat_fork_time / (1024 * 1024 * 1024); /* GB per second. */ + latencyAddSampleIfNeeded("fork", server.stat_fork_time / 1000); if (childpid == -1) { + closeChildInfoPipe(); serverLog(LL_WARNING, "Can't rewrite append only file in background: fork: %s", IF_WIN32(wsa_strerror(errno), strerror(errno))); + aofClosePipes(); return C_ERR; } serverLog(LL_NOTICE, - "Background append only file rewriting started by pid %d",childpid); + "Background append only file rewriting started by pid %d", childpid); server.aof_rewrite_scheduled = 0; server.aof_rewrite_time_start = time(NULL); server.aof_child_pid = childpid; @@ -1359,21 +1467,24 @@ int rewriteAppendOnlyFileBackground(void) { void bgrewriteaofCommand(client *c) { if (server.aof_child_pid != -1) { - addReplyError(c,"Background append only file rewriting already in progress"); - } else if (server.rdb_child_pid != -1) { + addReplyError(c, "Background append only file rewriting already in progress"); + } + else if (server.rdb_child_pid != -1) { server.aof_rewrite_scheduled = 1; - addReplyStatus(c,"Background append only file rewriting scheduled"); - } else if (rewriteAppendOnlyFileBackground() == C_OK) { - addReplyStatus(c,"Background append only file rewriting started"); - } else { - addReply(c,shared.err); + addReplyStatus(c, "Background append only file rewriting scheduled"); + } + else if (rewriteAppendOnlyFileBackground() == C_OK) { + addReplyStatus(c, "Background append only file rewriting started"); + } + else { + addReply(c, shared.err); } } void aofRemoveTempFile(pid_t childpid) { char tmpfile[256]; - snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", (int) childpid); + snprintf(tmpfile, 256, "temp-rewriteaof-bg-%d.aof", (int) childpid); unlink(tmpfile); } @@ -1387,20 +1498,21 @@ void aofUpdateCurrentSize(void) { #ifdef _WIN32 if (server.aof_fd == -1) { - serverLog(LL_NOTICE,"Unable to check the AOF length: %s", "appendfd is -1"); + serverLog(LL_NOTICE, "Unable to check the AOF length: %s", "appendfd is -1"); return; } #endif latencyStartMonitor(latency); - if (redis_fstat(server.aof_fd,&sb) == -1) { - serverLog(LL_WARNING,"Unable to obtain the AOF file length. stat: %s", + if (redis_fstat(server.aof_fd, &sb) == -1) { + serverLog(LL_WARNING, "Unable to obtain the AOF file length. stat: %s", IF_WIN32(wsa_strerror(errno), strerror(errno))); - } else { + } + else { server.aof_current_size = sb.st_size; } latencyEndMonitor(latency); - latencyAddSampleIfNeeded("aof-fstat",latency); + latencyAddSampleIfNeeded("aof-fstat", latency); } /* A background append only file rewriting (BGREWRITEAOF) terminated its work. @@ -1419,13 +1531,13 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) { * rewritten AOF. */ latencyStartMonitor(latency); #ifdef _WIN32 - snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", + snprintf(tmpfile, 256, "temp-rewriteaof-bg-%d.aof", getpid()); - newfd = open(tmpfile,O_WRONLY|O_APPEND|O_CREAT|_O_BINARY,_S_IREAD|_S_IWRITE); + newfd = open(tmpfile, O_WRONLY | O_APPEND | O_CREAT | _O_BINARY, _S_IREAD | _S_IWRITE); #else - snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", - (int)server.aof_child_pid); - newfd = open(tmpfile,O_WRONLY|O_APPEND); + snprintf(tmpfile, 256, "temp-rewriteaof-bg-%d.aof", + (int) server.aof_child_pid); + newfd = open(tmpfile, O_WRONLY | O_APPEND); #endif if (newfd == -1) { serverLog(LL_WARNING, @@ -1440,10 +1552,10 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) { goto cleanup; } latencyEndMonitor(latency); - latencyAddSampleIfNeeded("aof-rewrite-diff-write",latency); + latencyAddSampleIfNeeded("aof-rewrite-diff-write", latency); serverLog(LL_NOTICE, - "Residual parent diff successfully flushed to the rewritten AOF (%.2f MB)", (double) aofRewriteBufferSize() / (1024*1024)); + "Residual parent diff successfully flushed to the rewritten AOF (%.2f MB)", (double) aofRewriteBufferSize() / (1024 * 1024)); /* The only remaining thing to do is to rename the temporary file to * the configured file and switch the file descriptor used to do AOF @@ -1473,14 +1585,14 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) { * we don't care what the outcome or duration of that close operation * is, as PORT_LONG as the file descriptor is released again. */ #ifdef _WIN32 - // On Windows, if AOF is disabled and an AOF file already exists, - // we don't need to open it since it will have to be closed anyway - // before renaming the temp AOF. + // On Windows, if AOF is disabled and an AOF file already exists, + // we don't need to open it since it will have to be closed anyway + // before renaming the temp AOF. oldfd = -1; /* We'll set this to the current AOF filedes later. */ char tmpfile_win_old[256]; snprintf(tmpfile_win_old, 256, "temp-rewriteaof-old-%d.aof", - (int)server.aof_child_pid); + (int) server.aof_child_pid); if (server.aof_fd != -1) { // AOF enabled, close the existing AOF file @@ -1497,7 +1609,7 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) { latencyStartMonitor(latency); // Close the temp AOF file before renaming it close(newfd); - if (rename(tmpfile,server.aof_filename) == -1) { + if (rename(tmpfile, server.aof_filename) == -1) { serverLog(LL_WARNING, "Error trying to rename the temporary AOF file %s into %s: %s", tmpfile, @@ -1521,7 +1633,7 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) { goto cleanup; } /* now open the files again with new names */ - newfd = open(server.aof_filename, O_WRONLY|O_APPEND|_O_BINARY,0); + newfd = open(server.aof_filename, O_WRONLY | O_APPEND | _O_BINARY, 0); if (newfd == -1) { /* Windows fix: More info */ serverLog(LL_WARNING, "Not able to reopen the temporary AOF file after rename"); @@ -1530,7 +1642,7 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) { if (server.aof_fd != -1) { server.aof_fd = open( tmpfile_win_old, - O_WRONLY|O_APPEND|O_CREAT|_O_BINARY|_O_TEMPORARY, // _O_TEMPORARY forces delete on close flag in CreateFile call. File will be deleted in BIO_CLOSE_FILE job. + O_WRONLY | O_APPEND | O_CREAT | _O_BINARY | _O_TEMPORARY, // _O_TEMPORARY forces delete on close flag in CreateFile call. File will be deleted in BIO_CLOSE_FILE job. 0644); } #else @@ -1540,8 +1652,9 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) { /* Don't care if this fails: oldfd will be -1 and we handle that. * One notable case of -1 return is if the old file does * not exist. */ - oldfd = open(server.aof_filename,O_RDONLY|O_NONBLOCK); - } else { + oldfd = open(server.aof_filename, O_RDONLY | O_NONBLOCK); + } + else { /* AOF enabled */ oldfd = -1; /* We'll set this to the current AOF filedes later. */ } @@ -1549,7 +1662,7 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) { /* Rename the temporary file. This will not unlink the target file if * it exists, because we reference it with "oldfd". */ latencyStartMonitor(latency); - if (rename(tmpfile,server.aof_filename) == -1) { + if (rename(tmpfile, server.aof_filename) == -1) { serverLog(LL_WARNING, "Error trying to rename the temporary AOF file %s into %s: %s", tmpfile, @@ -1561,13 +1674,14 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) { } #endif latencyEndMonitor(latency); - latencyAddSampleIfNeeded("aof-rename",latency); + latencyAddSampleIfNeeded("aof-rename", latency); if (server.aof_fd == -1) { /* AOF disabled, we don't need to set the AOF file descriptor * to this new file, so we can close it. */ close(newfd); - } else { + } + else { /* AOF enabled, replace the old fd with the new one. */ oldfd = server.aof_fd; server.aof_fd = newfd; @@ -1593,18 +1707,20 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) { server.aof_state = AOF_ON; /* Asynchronously close the overwritten AOF. */ - if (oldfd != -1) bioCreateBackgroundJob(BIO_CLOSE_FILE,(void*)(PORT_LONG)oldfd,NULL,NULL); + if (oldfd != -1) bioCreateBackgroundJob(BIO_CLOSE_FILE, (void*) (PORT_LONG) oldfd, NULL, NULL); serverLog(LL_VERBOSE, - "Background AOF rewrite signal handler took %lldus", ustime()-now); - } else if (!bysignal && exitcode != 0) { + "Background AOF rewrite signal handler took %lldus", ustime() - now); + } + else if (!bysignal && exitcode != 0) { /* SIGUSR1 is whitelisted, so we have a way to kill a child without * tirggering an error conditon. */ if (bysignal != SIGUSR1) server.aof_lastbgrewrite_status = C_ERR; serverLog(LL_WARNING, "Background AOF rewrite terminated with error"); - } else { + } + else { server.aof_lastbgrewrite_status = C_ERR; serverLog(LL_WARNING, @@ -1614,9 +1730,9 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) { cleanup: aofClosePipes(); aofRewriteBufferReset(); - aofRemoveTempFile(IF_WIN32(getpid(),server.aof_child_pid)); + aofRemoveTempFile(IF_WIN32(getpid(), server.aof_child_pid)); server.aof_child_pid = -1; - server.aof_rewrite_time_last = time(NULL)-server.aof_rewrite_time_start; + server.aof_rewrite_time_last = time(NULL) - server.aof_rewrite_time_start; server.aof_rewrite_time_start = -1; /* Schedule a new rewrite if we are waiting for it to switch the AOF ON. */ if (server.aof_state == AOF_WAIT_REWRITE) @@ -1638,4 +1754,4 @@ void aofProcessDiffRewriteEvents(aeEventLoop* eventLoop) aofChildPipeReadable(eventLoop, server.aof_pipe_read_ack_from_child, NULL, 0); } } -} +} \ No newline at end of file diff --git a/src/version.h b/src/version.h index 3b59470d..ab90c7e1 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define REDIS_VERSION "4.0.2.1" +#define REDIS_VERSION "4.0.2.2"