8 #include <sys/resource.h> 
  11 void aofUpdateCurrentSize(void); 
  13 /* Called when the user switches from "appendonly yes" to "appendonly no" 
  14  * at runtime using the CONFIG command. */ 
  15 void stopAppendOnly(void) { 
  16     flushAppendOnlyFile(); 
  17     aof_fsync(server
.appendfd
); 
  18     close(server
.appendfd
); 
  21     server
.appendseldb 
= -1; 
  22     server
.appendonly 
= 0; 
  23     /* rewrite operation in progress? kill it, wait child exit */ 
  24     if (server
.bgrewritechildpid 
!= -1) { 
  27         if (kill(server
.bgrewritechildpid
,SIGKILL
) != -1) 
  28             wait3(&statloc
,0,NULL
); 
  29         /* reset the buffer accumulating changes while the child saves */ 
  30         sdsfree(server
.bgrewritebuf
); 
  31         server
.bgrewritebuf 
= sdsempty(); 
  32         server
.bgrewritechildpid 
= -1; 
  36 /* Called when the user switches from "appendonly no" to "appendonly yes" 
  37  * at runtime using the CONFIG command. */ 
  38 int startAppendOnly(void) { 
  39     server
.appendonly 
= 1; 
  40     server
.lastfsync 
= time(NULL
); 
  41     server
.appendfd 
= open(server
.appendfilename
,O_WRONLY
|O_APPEND
|O_CREAT
,0644); 
  42     if (server
.appendfd 
== -1) { 
  43         redisLog(REDIS_WARNING
,"Used tried to switch on AOF via CONFIG, but I can't open the AOF file: %s",strerror(errno
)); 
  46     if (rewriteAppendOnlyFileBackground() == REDIS_ERR
) { 
  47         server
.appendonly 
= 0; 
  48         close(server
.appendfd
); 
  49         redisLog(REDIS_WARNING
,"Used tried to switch on AOF via CONFIG, I can't trigger a background AOF rewrite operation. Check the above logs for more info about the error.",strerror(errno
)); 
  55 /* Write the append only file buffer on disk. 
  57  * Since we are required to write the AOF before replying to the client, 
  58  * and the only way the client socket can get a write is entering when the 
  59  * the event loop, we accumulate all the AOF writes in a memory 
  60  * buffer and write it on disk using this function just before entering 
  61  * the event loop again. */ 
  62 void flushAppendOnlyFile(void) { 
  65     if (sdslen(server
.aofbuf
) == 0) return; 
  67     /* We want to perform a single write. This should be guaranteed atomic 
  68      * at least if the filesystem we are writing is a real physical one. 
  69      * While this will save us against the server being killed I don't think 
  70      * there is much to do about the whole server stopping for power problems 
  72      nwritten 
= write(server
.appendfd
,server
.aofbuf
,sdslen(server
.aofbuf
)); 
  73      if (nwritten 
!= (signed)sdslen(server
.aofbuf
)) { 
  74         /* Ooops, we are in troubles. The best thing to do for now is 
  75          * aborting instead of giving the illusion that everything is 
  76          * working as expected. */ 
  78             redisLog(REDIS_WARNING
,"Exiting on error writing to the append-only file: %s",strerror(errno
)); 
  80             redisLog(REDIS_WARNING
,"Exiting on short write while writing to the append-only file: %s",strerror(errno
)); 
  84     sdsfree(server
.aofbuf
); 
  85     server
.aofbuf 
= sdsempty(); 
  86     server
.appendonly_current_size 
+= nwritten
; 
  88     /* Don't fsync if no-appendfsync-on-rewrite is set to yes and there are 
  89      * children doing I/O in the background. */ 
  90     if (server
.no_appendfsync_on_rewrite 
&& 
  91         (server
.bgrewritechildpid 
!= -1 || server
.bgsavechildpid 
!= -1)) 
  94     /* Perform the fsync if needed. */ 
  95     if (server
.appendfsync 
== APPENDFSYNC_ALWAYS 
|| 
  96         (server
.appendfsync 
== APPENDFSYNC_EVERYSEC 
&& 
  97          server
.unixtime 
> server
.lastfsync
)) 
  99         /* aof_fsync is defined as fdatasync() for Linux in order to avoid 
 100          * flushing metadata. */ 
 101         aof_fsync(server
.appendfd
); /* Let's try to get this data on the disk */ 
 102         server
.lastfsync 
= server
.unixtime
; 
 106 sds 
catAppendOnlyGenericCommand(sds buf
, int argc
, robj 
**argv
) { 
 108     buf 
= sdscatprintf(buf
,"*%d\r\n",argc
); 
 109     for (j 
= 0; j 
< argc
; j
++) { 
 110         robj 
*o 
= getDecodedObject(argv
[j
]); 
 111         buf 
= sdscatprintf(buf
,"$%lu\r\n",(unsigned long)sdslen(o
->ptr
)); 
 112         buf 
= sdscatlen(buf
,o
->ptr
,sdslen(o
->ptr
)); 
 113         buf 
= sdscatlen(buf
,"\r\n",2); 
 119 sds 
catAppendOnlyExpireAtCommand(sds buf
, robj 
*key
, robj 
*seconds
) { 
 124     /* Make sure we can use strtol */ 
 125     seconds 
= getDecodedObject(seconds
); 
 126     when 
= time(NULL
)+strtol(seconds
->ptr
,NULL
,10); 
 127     decrRefCount(seconds
); 
 129     argv
[0] = createStringObject("EXPIREAT",8); 
 131     argv
[2] = createObject(REDIS_STRING
, 
 132         sdscatprintf(sdsempty(),"%ld",when
)); 
 133     buf 
= catAppendOnlyGenericCommand(buf
, argc
, argv
); 
 134     decrRefCount(argv
[0]); 
 135     decrRefCount(argv
[2]); 
 139 void feedAppendOnlyFile(struct redisCommand 
*cmd
, int dictid
, robj 
**argv
, int argc
) { 
 140     sds buf 
= sdsempty(); 
 143     /* The DB this command was targetting is not the same as the last command 
 144      * we appendend. To issue a SELECT command is needed. */ 
 145     if (dictid 
!= server
.appendseldb
) { 
 148         snprintf(seldb
,sizeof(seldb
),"%d",dictid
); 
 149         buf 
= sdscatprintf(buf
,"*2\r\n$6\r\nSELECT\r\n$%lu\r\n%s\r\n", 
 150             (unsigned long)strlen(seldb
),seldb
); 
 151         server
.appendseldb 
= dictid
; 
 154     if (cmd
->proc 
== expireCommand
) { 
 155         /* Translate EXPIRE into EXPIREAT */ 
 156         buf 
= catAppendOnlyExpireAtCommand(buf
,argv
[1],argv
[2]); 
 157     } else if (cmd
->proc 
== setexCommand
) { 
 158         /* Translate SETEX to SET and EXPIREAT */ 
 159         tmpargv
[0] = createStringObject("SET",3); 
 160         tmpargv
[1] = argv
[1]; 
 161         tmpargv
[2] = argv
[3]; 
 162         buf 
= catAppendOnlyGenericCommand(buf
,3,tmpargv
); 
 163         decrRefCount(tmpargv
[0]); 
 164         buf 
= catAppendOnlyExpireAtCommand(buf
,argv
[1],argv
[2]); 
 166         buf 
= catAppendOnlyGenericCommand(buf
,argc
,argv
); 
 169     /* Append to the AOF buffer. This will be flushed on disk just before 
 170      * of re-entering the event loop, so before the client will get a 
 171      * positive reply about the operation performed. */ 
 172     server
.aofbuf 
= sdscatlen(server
.aofbuf
,buf
,sdslen(buf
)); 
 174     /* If a background append only file rewriting is in progress we want to 
 175      * accumulate the differences between the child DB and the current one 
 176      * in a buffer, so that when the child process will do its work we 
 177      * can append the differences to the new append only file. */ 
 178     if (server
.bgrewritechildpid 
!= -1) 
 179         server
.bgrewritebuf 
= sdscatlen(server
.bgrewritebuf
,buf
,sdslen(buf
)); 
 184 /* In Redis commands are always executed in the context of a client, so in 
 185  * order to load the append only file we need to create a fake client. */ 
 186 struct redisClient 
*createFakeClient(void) { 
 187     struct redisClient 
*c 
= zmalloc(sizeof(*c
)); 
 191     c
->querybuf 
= sdsempty(); 
 196     /* We set the fake client as a slave waiting for the synchronization 
 197      * so that Redis will not try to send replies to this client. */ 
 198     c
->replstate 
= REDIS_REPL_WAIT_BGSAVE_START
; 
 199     c
->reply 
= listCreate(); 
 200     c
->watched_keys 
= listCreate(); 
 201     listSetFreeMethod(c
->reply
,decrRefCount
); 
 202     listSetDupMethod(c
->reply
,dupClientReplyValue
); 
 203     initClientMultiState(c
); 
 207 void freeFakeClient(struct redisClient 
*c
) { 
 208     sdsfree(c
->querybuf
); 
 209     listRelease(c
->reply
); 
 210     listRelease(c
->watched_keys
); 
 211     freeClientMultiState(c
); 
 215 /* Replay the append log file. On error REDIS_OK is returned. On non fatal 
 216  * error (the append only file is zero-length) REDIS_ERR is returned. On 
 217  * fatal error an error message is logged and the program exists. */ 
 218 int loadAppendOnlyFile(char *filename
) { 
 219     struct redisClient 
*fakeClient
; 
 220     FILE *fp 
= fopen(filename
,"r"); 
 221     struct redis_stat sb
; 
 222     int appendonly 
= server
.appendonly
; 
 225     if (fp 
&& redis_fstat(fileno(fp
),&sb
) != -1 && sb
.st_size 
== 0) { 
 226         server
.appendonly_current_size 
= 0; 
 232         redisLog(REDIS_WARNING
,"Fatal error: can't open the append log file for reading: %s",strerror(errno
)); 
 236     /* Temporarily disable AOF, to prevent EXEC from feeding a MULTI 
 237      * to the same file we're about to read. */ 
 238     server
.appendonly 
= 0; 
 240     fakeClient 
= createFakeClient(); 
 249         struct redisCommand 
*cmd
; 
 251         /* Serve the clients from time to time */ 
 252         if (!(loops
++ % 1000)) { 
 253             loadingProgress(ftello(fp
)); 
 254             aeProcessEvents(server
.el
, AE_FILE_EVENTS
|AE_DONT_WAIT
); 
 257         if (fgets(buf
,sizeof(buf
),fp
) == NULL
) { 
 263         if (buf
[0] != '*') goto fmterr
; 
 265         argv 
= zmalloc(sizeof(robj
*)*argc
); 
 266         for (j 
= 0; j 
< argc
; j
++) { 
 267             if (fgets(buf
,sizeof(buf
),fp
) == NULL
) goto readerr
; 
 268             if (buf
[0] != '$') goto fmterr
; 
 269             len 
= strtol(buf
+1,NULL
,10); 
 270             argsds 
= sdsnewlen(NULL
,len
); 
 271             if (len 
&& fread(argsds
,len
,1,fp
) == 0) goto fmterr
; 
 272             argv
[j
] = createObject(REDIS_STRING
,argsds
); 
 273             if (fread(buf
,2,1,fp
) == 0) goto fmterr
; /* discard CRLF */ 
 277         cmd 
= lookupCommand(argv
[0]->ptr
); 
 279             redisLog(REDIS_WARNING
,"Unknown command '%s' reading the append only file", argv
[0]->ptr
); 
 282         /* Run the command in the context of a fake client */ 
 283         fakeClient
->argc 
= argc
; 
 284         fakeClient
->argv 
= argv
; 
 285         cmd
->proc(fakeClient
); 
 287         /* The fake client should not have a reply */ 
 288         redisAssert(fakeClient
->bufpos 
== 0 && listLength(fakeClient
->reply
) == 0); 
 289         /* The fake client should never get blocked */ 
 290         redisAssert((fakeClient
->flags 
& REDIS_BLOCKED
) == 0); 
 292         /* Clean up. Command code may have changed argv/argc so we use the 
 293          * argv/argc of the client instead of the local variables. */ 
 294         for (j 
= 0; j 
< fakeClient
->argc
; j
++) 
 295             decrRefCount(fakeClient
->argv
[j
]); 
 296         zfree(fakeClient
->argv
); 
 299     /* This point can only be reached when EOF is reached without errors. 
 300      * If the client is in the middle of a MULTI/EXEC, log error and quit. */ 
 301     if (fakeClient
->flags 
& REDIS_MULTI
) goto readerr
; 
 304     freeFakeClient(fakeClient
); 
 305     server
.appendonly 
= appendonly
; 
 307     aofUpdateCurrentSize(); 
 308     server
.auto_aofrewrite_base_size 
= server
.appendonly_current_size
; 
 313         redisLog(REDIS_WARNING
,"Unexpected end of file reading the append only file"); 
 315         redisLog(REDIS_WARNING
,"Unrecoverable error reading the append only file: %s", strerror(errno
)); 
 319     redisLog(REDIS_WARNING
,"Bad file format reading the append only file: make a backup of your AOF file, then use ./redis-check-aof --fix <filename>"); 
 323 /* Write a sequence of commands able to fully rebuild the dataset into 
 324  * "filename". Used both by REWRITEAOF and BGREWRITEAOF. */ 
 325 int rewriteAppendOnlyFile(char *filename
) { 
 326     dictIterator 
*di 
= NULL
; 
 331     time_t now 
= time(NULL
); 
 333     /* Note that we have to use a different temp name here compared to the 
 334      * one used by rewriteAppendOnlyFileBackground() function. */ 
 335     snprintf(tmpfile
,256,"temp-rewriteaof-%d.aof", (int) getpid()); 
 336     fp 
= fopen(tmpfile
,"w"); 
 338         redisLog(REDIS_WARNING
, "Failed rewriting the append only file: %s", strerror(errno
)); 
 341     for (j 
= 0; j 
< server
.dbnum
; j
++) { 
 342         char selectcmd
[] = "*2\r\n$6\r\nSELECT\r\n"; 
 343         redisDb 
*db 
= server
.db
+j
; 
 345         if (dictSize(d
) == 0) continue; 
 346         di 
= dictGetSafeIterator(d
); 
 352         /* SELECT the new DB */ 
 353         if (fwrite(selectcmd
,sizeof(selectcmd
)-1,1,fp
) == 0) goto werr
; 
 354         if (fwriteBulkLongLong(fp
,j
) == 0) goto werr
; 
 356         /* Iterate this DB writing every entry */ 
 357         while((de 
= dictNext(di
)) != NULL
) { 
 362             keystr 
= dictGetEntryKey(de
); 
 363             o 
= dictGetEntryVal(de
); 
 364             initStaticStringObject(key
,keystr
); 
 366             expiretime 
= getExpire(db
,&key
); 
 368             /* Save the key and associated value */ 
 369             if (o
->type 
== REDIS_STRING
) { 
 370                 /* Emit a SET command */ 
 371                 char cmd
[]="*3\r\n$3\r\nSET\r\n"; 
 372                 if (fwrite(cmd
,sizeof(cmd
)-1,1,fp
) == 0) goto werr
; 
 374                 if (fwriteBulkObject(fp
,&key
) == 0) goto werr
; 
 375                 if (fwriteBulkObject(fp
,o
) == 0) goto werr
; 
 376             } else if (o
->type 
== REDIS_LIST
) { 
 377                 /* Emit the RPUSHes needed to rebuild the list */ 
 378                 char cmd
[]="*3\r\n$5\r\nRPUSH\r\n"; 
 379                 if (o
->encoding 
== REDIS_ENCODING_ZIPLIST
) { 
 380                     unsigned char *zl 
= o
->ptr
; 
 381                     unsigned char *p 
= ziplistIndex(zl
,0); 
 386                     while(ziplistGet(p
,&vstr
,&vlen
,&vlong
)) { 
 387                         if (fwrite(cmd
,sizeof(cmd
)-1,1,fp
) == 0) goto werr
; 
 388                         if (fwriteBulkObject(fp
,&key
) == 0) goto werr
; 
 390                             if (fwriteBulkString(fp
,(char*)vstr
,vlen
) == 0) 
 393                             if (fwriteBulkLongLong(fp
,vlong
) == 0) 
 396                         p 
= ziplistNext(zl
,p
); 
 398                 } else if (o
->encoding 
== REDIS_ENCODING_LINKEDLIST
) { 
 403                     listRewind(list
,&li
); 
 404                     while((ln 
= listNext(&li
))) { 
 405                         robj 
*eleobj 
= listNodeValue(ln
); 
 407                         if (fwrite(cmd
,sizeof(cmd
)-1,1,fp
) == 0) goto werr
; 
 408                         if (fwriteBulkObject(fp
,&key
) == 0) goto werr
; 
 409                         if (fwriteBulkObject(fp
,eleobj
) == 0) goto werr
; 
 412                     redisPanic("Unknown list encoding"); 
 414             } else if (o
->type 
== REDIS_SET
) { 
 415                 char cmd
[]="*3\r\n$4\r\nSADD\r\n"; 
 417                 /* Emit the SADDs needed to rebuild the set */ 
 418                 if (o
->encoding 
== REDIS_ENCODING_INTSET
) { 
 421                     while(intsetGet(o
->ptr
,ii
++,&llval
)) { 
 422                         if (fwrite(cmd
,sizeof(cmd
)-1,1,fp
) == 0) goto werr
; 
 423                         if (fwriteBulkObject(fp
,&key
) == 0) goto werr
; 
 424                         if (fwriteBulkLongLong(fp
,llval
) == 0) goto werr
; 
 426                 } else if (o
->encoding 
== REDIS_ENCODING_HT
) { 
 427                     dictIterator 
*di 
= dictGetIterator(o
->ptr
); 
 429                     while((de 
= dictNext(di
)) != NULL
) { 
 430                         robj 
*eleobj 
= dictGetEntryKey(de
); 
 431                         if (fwrite(cmd
,sizeof(cmd
)-1,1,fp
) == 0) goto werr
; 
 432                         if (fwriteBulkObject(fp
,&key
) == 0) goto werr
; 
 433                         if (fwriteBulkObject(fp
,eleobj
) == 0) goto werr
; 
 435                     dictReleaseIterator(di
); 
 437                     redisPanic("Unknown set encoding"); 
 439             } else if (o
->type 
== REDIS_ZSET
) { 
 440                 /* Emit the ZADDs needed to rebuild the sorted set */ 
 441                 char cmd
[]="*4\r\n$4\r\nZADD\r\n"; 
 443                 if (o
->encoding 
== REDIS_ENCODING_ZIPLIST
) { 
 444                     unsigned char *zl 
= o
->ptr
; 
 445                     unsigned char *eptr
, *sptr
; 
 451                     eptr 
= ziplistIndex(zl
,0); 
 452                     redisAssert(eptr 
!= NULL
); 
 453                     sptr 
= ziplistNext(zl
,eptr
); 
 454                     redisAssert(sptr 
!= NULL
); 
 456                     while (eptr 
!= NULL
) { 
 457                         redisAssert(ziplistGet(eptr
,&vstr
,&vlen
,&vll
)); 
 458                         score 
= zzlGetScore(sptr
); 
 460                         if (fwrite(cmd
,sizeof(cmd
)-1,1,fp
) == 0) goto werr
; 
 461                         if (fwriteBulkObject(fp
,&key
) == 0) goto werr
; 
 462                         if (fwriteBulkDouble(fp
,score
) == 0) goto werr
; 
 464                             if (fwriteBulkString(fp
,(char*)vstr
,vlen
) == 0) 
 467                             if (fwriteBulkLongLong(fp
,vll
) == 0) 
 470                         zzlNext(zl
,&eptr
,&sptr
); 
 472                 } else if (o
->encoding 
== REDIS_ENCODING_SKIPLIST
) { 
 474                     dictIterator 
*di 
= dictGetIterator(zs
->dict
); 
 477                     while((de 
= dictNext(di
)) != NULL
) { 
 478                         robj 
*eleobj 
= dictGetEntryKey(de
); 
 479                         double *score 
= dictGetEntryVal(de
); 
 481                         if (fwrite(cmd
,sizeof(cmd
)-1,1,fp
) == 0) goto werr
; 
 482                         if (fwriteBulkObject(fp
,&key
) == 0) goto werr
; 
 483                         if (fwriteBulkDouble(fp
,*score
) == 0) goto werr
; 
 484                         if (fwriteBulkObject(fp
,eleobj
) == 0) goto werr
; 
 486                     dictReleaseIterator(di
); 
 488                     redisPanic("Unknown sorted set encoding"); 
 490             } else if (o
->type 
== REDIS_HASH
) { 
 491                 char cmd
[]="*4\r\n$4\r\nHSET\r\n"; 
 493                 /* Emit the HSETs needed to rebuild the hash */ 
 494                 if (o
->encoding 
== REDIS_ENCODING_ZIPMAP
) { 
 495                     unsigned char *p 
= zipmapRewind(o
->ptr
); 
 496                     unsigned char *field
, *val
; 
 497                     unsigned int flen
, vlen
; 
 499                     while((p 
= zipmapNext(p
,&field
,&flen
,&val
,&vlen
)) != NULL
) { 
 500                         if (fwrite(cmd
,sizeof(cmd
)-1,1,fp
) == 0) goto werr
; 
 501                         if (fwriteBulkObject(fp
,&key
) == 0) goto werr
; 
 502                         if (fwriteBulkString(fp
,(char*)field
,flen
) == 0) 
 504                         if (fwriteBulkString(fp
,(char*)val
,vlen
) == 0) 
 508                     dictIterator 
*di 
= dictGetIterator(o
->ptr
); 
 511                     while((de 
= dictNext(di
)) != NULL
) { 
 512                         robj 
*field 
= dictGetEntryKey(de
); 
 513                         robj 
*val 
= dictGetEntryVal(de
); 
 515                         if (fwrite(cmd
,sizeof(cmd
)-1,1,fp
) == 0) goto werr
; 
 516                         if (fwriteBulkObject(fp
,&key
) == 0) goto werr
; 
 517                         if (fwriteBulkObject(fp
,field
) == 0) goto werr
; 
 518                         if (fwriteBulkObject(fp
,val
) == 0) goto werr
; 
 520                     dictReleaseIterator(di
); 
 523                 redisPanic("Unknown object type"); 
 525             /* Save the expire time */ 
 526             if (expiretime 
!= -1) { 
 527                 char cmd
[]="*3\r\n$8\r\nEXPIREAT\r\n"; 
 528                 /* If this key is already expired skip it */ 
 529                 if (expiretime 
< now
) continue; 
 530                 if (fwrite(cmd
,sizeof(cmd
)-1,1,fp
) == 0) goto werr
; 
 531                 if (fwriteBulkObject(fp
,&key
) == 0) goto werr
; 
 532                 if (fwriteBulkLongLong(fp
,expiretime
) == 0) goto werr
; 
 535         dictReleaseIterator(di
); 
 538     /* Make sure data will not remain on the OS's output buffers */ 
 540     aof_fsync(fileno(fp
)); 
 543     /* Use RENAME to make sure the DB file is changed atomically only 
 544      * if the generate DB file is ok. */ 
 545     if (rename(tmpfile
,filename
) == -1) { 
 546         redisLog(REDIS_WARNING
,"Error moving temp append only file on the final destination: %s", strerror(errno
)); 
 550     redisLog(REDIS_NOTICE
,"SYNC append only file rewrite performed"); 
 556     redisLog(REDIS_WARNING
,"Write error writing append only file on disk: %s", strerror(errno
)); 
 557     if (di
) dictReleaseIterator(di
); 
 561 /* This is how rewriting of the append only file in background works: 
 563  * 1) The user calls BGREWRITEAOF 
 564  * 2) Redis calls this function, that forks(): 
 565  *    2a) the child rewrite the append only file in a temp file. 
 566  *    2b) the parent accumulates differences in server.bgrewritebuf. 
 567  * 3) When the child finished '2a' exists. 
 568  * 4) The parent will trap the exit code, if it's OK, will append the 
 569  *    data accumulated into server.bgrewritebuf into the temp file, and 
 570  *    finally will rename(2) the temp file in the actual file name. 
 571  *    The the new file is reopened as the new append only file. Profit! 
 573 int rewriteAppendOnlyFileBackground(void) { 
 577     if (server
.bgrewritechildpid 
!= -1) return REDIS_ERR
; 
 579     if ((childpid 
= fork()) == 0) { 
 583         if (server
.ipfd 
> 0) close(server
.ipfd
); 
 584         if (server
.sofd 
> 0) close(server
.sofd
); 
 585         snprintf(tmpfile
,256,"temp-rewriteaof-bg-%d.aof", (int) getpid()); 
 586         if (rewriteAppendOnlyFile(tmpfile
) == REDIS_OK
) { 
 593         server
.stat_fork_time 
= ustime()-start
; 
 594         if (childpid 
== -1) { 
 595             redisLog(REDIS_WARNING
, 
 596                 "Can't rewrite append only file in background: fork: %s", 
 600         redisLog(REDIS_NOTICE
, 
 601             "Background append only file rewriting started by pid %d",childpid
); 
 602         server
.bgrewritechildpid 
= childpid
; 
 603         updateDictResizePolicy(); 
 604         /* We set appendseldb to -1 in order to force the next call to the 
 605          * feedAppendOnlyFile() to issue a SELECT command, so the differences 
 606          * accumulated by the parent into server.bgrewritebuf will start 
 607          * with a SELECT statement and it will be safe to merge. */ 
 608         server
.appendseldb 
= -1; 
 611     return REDIS_OK
; /* unreached */ 
 614 void bgrewriteaofCommand(redisClient 
*c
) { 
 615     if (server
.bgrewritechildpid 
!= -1) { 
 616         addReplyError(c
,"Background append only file rewriting already in progress"); 
 617     } else if (server
.bgsavechildpid 
!= -1) { 
 618         server
.aofrewrite_scheduled 
= 1; 
 619         addReplyStatus(c
,"Background append only file rewriting scheduled"); 
 620     } else if (rewriteAppendOnlyFileBackground() == REDIS_OK
) { 
 621         addReplyStatus(c
,"Background append only file rewriting started"); 
 623         addReply(c
,shared
.err
); 
 627 void aofRemoveTempFile(pid_t childpid
) { 
 630     snprintf(tmpfile
,256,"temp-rewriteaof-bg-%d.aof", (int) childpid
); 
 634 /* Update the server.appendonly_current_size filed explicitly using stat(2) 
 635  * to check the size of the file. This is useful after a rewrite or after 
 636  * a restart, normally the size is updated just adding the write length 
 637  * to the current lenght, that is much faster. */ 
 638 void aofUpdateCurrentSize(void) { 
 639     struct redis_stat sb
; 
 641     if (redis_fstat(server
.appendfd
,&sb
) == -1) { 
 642         redisLog(REDIS_WARNING
,"Unable to check the AOF length: %s", 
 645         server
.appendonly_current_size 
= sb
.st_size
; 
 649 /* A background append only file rewriting (BGREWRITEAOF) terminated its work. 
 651 void backgroundRewriteDoneHandler(int exitcode
, int bysignal
) { 
 652     if (!bysignal 
&& exitcode 
== 0) { 
 656         redisLog(REDIS_NOTICE
, 
 657             "Background append only file rewriting terminated with success"); 
 658         /* Now it's time to flush the differences accumulated by the parent */ 
 659         snprintf(tmpfile
,256,"temp-rewriteaof-bg-%d.aof", (int) server
.bgrewritechildpid
); 
 660         fd 
= open(tmpfile
,O_WRONLY
|O_APPEND
); 
 662             redisLog(REDIS_WARNING
, "Not able to open the temp append only file produced by the child: %s", strerror(errno
)); 
 665         /* Flush our data... */ 
 666         if (write(fd
,server
.bgrewritebuf
,sdslen(server
.bgrewritebuf
)) != 
 667                 (signed) sdslen(server
.bgrewritebuf
)) { 
 668             redisLog(REDIS_WARNING
, "Error or short write trying to flush the parent diff of the append log file in the child temp file: %s", strerror(errno
)); 
 672         redisLog(REDIS_NOTICE
,"Parent diff flushed into the new append log file with success (%lu bytes)",sdslen(server
.bgrewritebuf
)); 
 673         /* Now our work is to rename the temp file into the stable file. And 
 674          * switch the file descriptor used by the server for append only. */ 
 675         if (rename(tmpfile
,server
.appendfilename
) == -1) { 
 676             redisLog(REDIS_WARNING
,"Can't rename the temp append only file into the stable one: %s", strerror(errno
)); 
 680         /* Mission completed... almost */ 
 681         redisLog(REDIS_NOTICE
,"Append only file successfully rewritten."); 
 682         if (server
.appendfd 
!= -1) { 
 683             /* If append only is actually enabled... */ 
 684             close(server
.appendfd
); 
 685             server
.appendfd 
= fd
; 
 686             if (server
.appendfsync 
!= APPENDFSYNC_NO
) aof_fsync(fd
); 
 687             server
.appendseldb 
= -1; /* Make sure it will issue SELECT */ 
 688             redisLog(REDIS_NOTICE
,"The new append only file was selected for future appends."); 
 689             aofUpdateCurrentSize(); 
 690             server
.auto_aofrewrite_base_size 
= server
.appendonly_current_size
; 
 692             /* If append only is disabled we just generate a dump in this 
 693              * format. Why not? */ 
 696     } else if (!bysignal 
&& exitcode 
!= 0) { 
 697         redisLog(REDIS_WARNING
, "Background append only file rewriting error"); 
 699         redisLog(REDIS_WARNING
, 
 700             "Background append only file rewriting terminated by signal %d", 
 704     sdsfree(server
.bgrewritebuf
); 
 705     server
.bgrewritebuf 
= sdsempty(); 
 706     aofRemoveTempFile(server
.bgrewritechildpid
); 
 707     server
.bgrewritechildpid 
= -1;