1 #include "redis.h" 2 3 #include <signal.h> 4 #include <fcntl.h> 5 #include <sys/stat.h> 6 #include <sys/types.h> 7 #include <sys/time.h> 8 #include <sys/resource.h> 9 #include <sys/wait.h> 10 11 void aofUpdateCurrentSize(void); 12 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); 19 20 server.appendfd = -1; 21 server.appendseldb = -1; 22 server.appendonly = 0; 23 /* rewrite operation in progress? kill it, wait child exit */ 24 if (server.bgrewritechildpid != -1) { 25 int statloc; 26 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; 33 } 34 } 35 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)); 44 return REDIS_ERR; 45 } 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)); 50 return REDIS_ERR; 51 } 52 return REDIS_OK; 53 } 54 55 /* Write the append only file buffer on disk. 56 * 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) { 63 time_t now; 64 ssize_t nwritten; 65 66 if (sdslen(server.aofbuf) == 0) return; 67 68 /* We want to perform a single write. This should be guaranteed atomic 69 * at least if the filesystem we are writing is a real physical one. 70 * While this will save us against the server being killed I don't think 71 * there is much to do about the whole server stopping for power problems 72 * or alike */ 73 nwritten = write(server.appendfd,server.aofbuf,sdslen(server.aofbuf)); 74 if (nwritten != (signed)sdslen(server.aofbuf)) { 75 /* Ooops, we are in troubles. The best thing to do for now is 76 * aborting instead of giving the illusion that everything is 77 * working as expected. */ 78 if (nwritten == -1) { 79 redisLog(REDIS_WARNING,"Exiting on error writing to the append-only file: %s",strerror(errno)); 80 } else { 81 redisLog(REDIS_WARNING,"Exiting on short write while writing to the append-only file: %s",strerror(errno)); 82 } 83 exit(1); 84 } 85 sdsfree(server.aofbuf); 86 server.aofbuf = sdsempty(); 87 server.appendonly_current_size += nwritten; 88 89 /* Don't Fsync if no-appendfsync-on-rewrite is set to yes and we have 90 * childs performing heavy I/O on disk. */ 91 if (server.no_appendfsync_on_rewrite && 92 (server.bgrewritechildpid != -1 || server.bgsavechildpid != -1)) 93 return; 94 /* Fsync if needed */ 95 now = time(NULL); 96 if (server.appendfsync == APPENDFSYNC_ALWAYS || 97 (server.appendfsync == APPENDFSYNC_EVERYSEC && 98 now-server.lastfsync > 1)) 99 { 100 /* aof_fsync is defined as fdatasync() for Linux in order to avoid 101 * flushing metadata. */ 102 aof_fsync(server.appendfd); /* Let's try to get this data on the disk */ 103 server.lastfsync = now; 104 } 105 } 106 107 sds catAppendOnlyGenericCommand(sds buf, int argc, robj **argv) { 108 int j; 109 buf = sdscatprintf(buf,"*%d\r\n",argc); 110 for (j = 0; j < argc; j++) { 111 robj *o = getDecodedObject(argv[j]); 112 buf = sdscatprintf(buf,"$%lu\r\n",(unsigned long)sdslen(o->ptr)); 113 buf = sdscatlen(buf,o->ptr,sdslen(o->ptr)); 114 buf = sdscatlen(buf,"\r\n",2); 115 decrRefCount(o); 116 } 117 return buf; 118 } 119 120 sds catAppendOnlyExpireAtCommand(sds buf, robj *key, robj *seconds) { 121 int argc = 3; 122 long when; 123 robj *argv[3]; 124 125 /* Make sure we can use strtol */ 126 seconds = getDecodedObject(seconds); 127 when = time(NULL)+strtol(seconds->ptr,NULL,10); 128 decrRefCount(seconds); 129 130 argv[0] = createStringObject("EXPIREAT",8); 131 argv[1] = key; 132 argv[2] = createObject(REDIS_STRING, 133 sdscatprintf(sdsempty(),"%ld",when)); 134 buf = catAppendOnlyGenericCommand(buf, argc, argv); 135 decrRefCount(argv[0]); 136 decrRefCount(argv[2]); 137 return buf; 138 } 139 140 void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int argc) { 141 sds buf = sdsempty(); 142 robj *tmpargv[3]; 143 144 /* The DB this command was targetting is not the same as the last command 145 * we appendend. To issue a SELECT command is needed. */ 146 if (dictid != server.appendseldb) { 147 char seldb[64]; 148 149 snprintf(seldb,sizeof(seldb),"%d",dictid); 150 buf = sdscatprintf(buf,"*2\r\n$6\r\nSELECT\r\n$%lu\r\n%s\r\n", 151 (unsigned long)strlen(seldb),seldb); 152 server.appendseldb = dictid; 153 } 154 155 if (cmd->proc == expireCommand) { 156 /* Translate EXPIRE into EXPIREAT */ 157 buf = catAppendOnlyExpireAtCommand(buf,argv[1],argv[2]); 158 } else if (cmd->proc == setexCommand) { 159 /* Translate SETEX to SET and EXPIREAT */ 160 tmpargv[0] = createStringObject("SET",3); 161 tmpargv[1] = argv[1]; 162 tmpargv[2] = argv[3]; 163 buf = catAppendOnlyGenericCommand(buf,3,tmpargv); 164 decrRefCount(tmpargv[0]); 165 buf = catAppendOnlyExpireAtCommand(buf,argv[1],argv[2]); 166 } else { 167 buf = catAppendOnlyGenericCommand(buf,argc,argv); 168 } 169 170 /* Append to the AOF buffer. This will be flushed on disk just before 171 * of re-entering the event loop, so before the client will get a 172 * positive reply about the operation performed. */ 173 server.aofbuf = sdscatlen(server.aofbuf,buf,sdslen(buf)); 174 175 /* If a background append only file rewriting is in progress we want to 176 * accumulate the differences between the child DB and the current one 177 * in a buffer, so that when the child process will do its work we 178 * can append the differences to the new append only file. */ 179 if (server.bgrewritechildpid != -1) 180 server.bgrewritebuf = sdscatlen(server.bgrewritebuf,buf,sdslen(buf)); 181 182 sdsfree(buf); 183 } 184 185 /* In Redis commands are always executed in the context of a client, so in 186 * order to load the append only file we need to create a fake client. */ 187 struct redisClient *createFakeClient(void) { 188 struct redisClient *c = zmalloc(sizeof(*c)); 189 190 selectDb(c,0); 191 c->fd = -1; 192 c->querybuf = sdsempty(); 193 c->argc = 0; 194 c->argv = NULL; 195 c->bufpos = 0; 196 c->flags = 0; 197 /* We set the fake client as a slave waiting for the synchronization 198 * so that Redis will not try to send replies to this client. */ 199 c->replstate = REDIS_REPL_WAIT_BGSAVE_START; 200 c->reply = listCreate(); 201 c->watched_keys = listCreate(); 202 listSetFreeMethod(c->reply,decrRefCount); 203 listSetDupMethod(c->reply,dupClientReplyValue); 204 initClientMultiState(c); 205 return c; 206 } 207 208 void freeFakeClient(struct redisClient *c) { 209 sdsfree(c->querybuf); 210 listRelease(c->reply); 211 listRelease(c->watched_keys); 212 freeClientMultiState(c); 213 zfree(c); 214 } 215 216 /* Replay the append log file. On error REDIS_OK is returned. On non fatal 217 * error (the append only file is zero-length) REDIS_ERR is returned. On 218 * fatal error an error message is logged and the program exists. */ 219 int loadAppendOnlyFile(char *filename) { 220 struct redisClient *fakeClient; 221 FILE *fp = fopen(filename,"r"); 222 struct redis_stat sb; 223 int appendonly = server.appendonly; 224 long loops = 0; 225 226 if (fp && redis_fstat(fileno(fp),&sb) != -1 && sb.st_size == 0) { 227 server.appendonly_current_size = 0; 228 fclose(fp); 229 return REDIS_ERR; 230 } 231 232 if (fp == NULL) { 233 redisLog(REDIS_WARNING,"Fatal error: can't open the append log file for reading: %s",strerror(errno)); 234 exit(1); 235 } 236 237 /* Temporarily disable AOF, to prevent EXEC from feeding a MULTI 238 * to the same file we're about to read. */ 239 server.appendonly = 0; 240 241 fakeClient = createFakeClient(); 242 startLoading(fp); 243 244 while(1) { 245 int argc, j; 246 unsigned long len; 247 robj **argv; 248 char buf[128]; 249 sds argsds; 250 struct redisCommand *cmd; 251 252 /* Serve the clients from time to time */ 253 if (!(loops++ % 1000)) { 254 loadingProgress(ftello(fp)); 255 aeProcessEvents(server.el, AE_FILE_EVENTS|AE_DONT_WAIT); 256 } 257 258 if (fgets(buf,sizeof(buf),fp) == NULL) { 259 if (feof(fp)) 260 break; 261 else 262 goto readerr; 263 } 264 if (buf[0] != '*') goto fmterr; 265 argc = atoi(buf+1); 266 argv = zmalloc(sizeof(robj*)*argc); 267 for (j = 0; j < argc; j++) { 268 if (fgets(buf,sizeof(buf),fp) == NULL) goto readerr; 269 if (buf[0] != '$') goto fmterr; 270 len = strtol(buf+1,NULL,10); 271 argsds = sdsnewlen(NULL,len); 272 if (len && fread(argsds,len,1,fp) == 0) goto fmterr; 273 argv[j] = createObject(REDIS_STRING,argsds); 274 if (fread(buf,2,1,fp) == 0) goto fmterr; /* discard CRLF */ 275 } 276 277 /* Command lookup */ 278 cmd = lookupCommand(argv[0]->ptr); 279 if (!cmd) { 280 redisLog(REDIS_WARNING,"Unknown command '%s' reading the append only file", argv[0]->ptr); 281 exit(1); 282 } 283 /* Run the command in the context of a fake client */ 284 fakeClient->argc = argc; 285 fakeClient->argv = argv; 286 cmd->proc(fakeClient); 287 288 /* The fake client should not have a reply */ 289 redisAssert(fakeClient->bufpos == 0 && listLength(fakeClient->reply) == 0); 290 291 /* Clean up. Command code may have changed argv/argc so we use the 292 * argv/argc of the client instead of the local variables. */ 293 for (j = 0; j < fakeClient->argc; j++) 294 decrRefCount(fakeClient->argv[j]); 295 zfree(fakeClient->argv); 296 } 297 298 /* This point can only be reached when EOF is reached without errors. 299 * If the client is in the middle of a MULTI/EXEC, log error and quit. */ 300 if (fakeClient->flags & REDIS_MULTI) goto readerr; 301 302 fclose(fp); 303 freeFakeClient(fakeClient); 304 server.appendonly = appendonly; 305 stopLoading(); 306 aofUpdateCurrentSize(); 307 server.auto_aofrewrite_base_size = server.appendonly_current_size; 308 return REDIS_OK; 309 310 readerr: 311 if (feof(fp)) { 312 redisLog(REDIS_WARNING,"Unexpected end of file reading the append only file"); 313 } else { 314 redisLog(REDIS_WARNING,"Unrecoverable error reading the append only file: %s", strerror(errno)); 315 } 316 exit(1); 317 fmterr: 318 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>"); 319 exit(1); 320 } 321 322 /* Write a sequence of commands able to fully rebuild the dataset into 323 * "filename". Used both by REWRITEAOF and BGREWRITEAOF. */ 324 int rewriteAppendOnlyFile(char *filename) { 325 dictIterator *di = NULL; 326 dictEntry *de; 327 FILE *fp; 328 char tmpfile[256]; 329 int j; 330 time_t now = time(NULL); 331 332 /* Note that we have to use a different temp name here compared to the 333 * one used by rewriteAppendOnlyFileBackground() function. */ 334 snprintf(tmpfile,256,"temp-rewriteaof-%d.aof", (int) getpid()); 335 fp = fopen(tmpfile,"w"); 336 if (!fp) { 337 redisLog(REDIS_WARNING, "Failed rewriting the append only file: %s", strerror(errno)); 338 return REDIS_ERR; 339 } 340 for (j = 0; j < server.dbnum; j++) { 341 char selectcmd[] = "*2\r\n$6\r\nSELECT\r\n"; 342 redisDb *db = server.db+j; 343 dict *d = db->dict; 344 if (dictSize(d) == 0) continue; 345 di = dictGetSafeIterator(d); 346 if (!di) { 347 fclose(fp); 348 return REDIS_ERR; 349 } 350 351 /* SELECT the new DB */ 352 if (fwrite(selectcmd,sizeof(selectcmd)-1,1,fp) == 0) goto werr; 353 if (fwriteBulkLongLong(fp,j) == 0) goto werr; 354 355 /* Iterate this DB writing every entry */ 356 while((de = dictNext(di)) != NULL) { 357 sds keystr; 358 robj key, *o; 359 time_t expiretime; 360 361 keystr = dictGetEntryKey(de); 362 o = dictGetEntryVal(de); 363 initStaticStringObject(key,keystr); 364 365 expiretime = getExpire(db,&key); 366 367 /* Save the key and associated value */ 368 if (o->type == REDIS_STRING) { 369 /* Emit a SET command */ 370 char cmd[]="*3\r\n$3\r\nSET\r\n"; 371 if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr; 372 /* Key and value */ 373 if (fwriteBulkObject(fp,&key) == 0) goto werr; 374 if (fwriteBulkObject(fp,o) == 0) goto werr; 375 } else if (o->type == REDIS_LIST) { 376 /* Emit the RPUSHes needed to rebuild the list */ 377 char cmd[]="*3\r\n$5\r\nRPUSH\r\n"; 378 if (o->encoding == REDIS_ENCODING_ZIPLIST) { 379 unsigned char *zl = o->ptr; 380 unsigned char *p = ziplistIndex(zl,0); 381 unsigned char *vstr; 382 unsigned int vlen; 383 long long vlong; 384 385 while(ziplistGet(p,&vstr,&vlen,&vlong)) { 386 if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr; 387 if (fwriteBulkObject(fp,&key) == 0) goto werr; 388 if (vstr) { 389 if (fwriteBulkString(fp,(char*)vstr,vlen) == 0) 390 goto werr; 391 } else { 392 if (fwriteBulkLongLong(fp,vlong) == 0) 393 goto werr; 394 } 395 p = ziplistNext(zl,p); 396 } 397 } else if (o->encoding == REDIS_ENCODING_LINKEDLIST) { 398 list *list = o->ptr; 399 listNode *ln; 400 listIter li; 401 402 listRewind(list,&li); 403 while((ln = listNext(&li))) { 404 robj *eleobj = listNodeValue(ln); 405 406 if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr; 407 if (fwriteBulkObject(fp,&key) == 0) goto werr; 408 if (fwriteBulkObject(fp,eleobj) == 0) goto werr; 409 } 410 } else { 411 redisPanic("Unknown list encoding"); 412 } 413 } else if (o->type == REDIS_SET) { 414 char cmd[]="*3\r\n$4\r\nSADD\r\n"; 415 416 /* Emit the SADDs needed to rebuild the set */ 417 if (o->encoding == REDIS_ENCODING_INTSET) { 418 int ii = 0; 419 int64_t llval; 420 while(intsetGet(o->ptr,ii++,&llval)) { 421 if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr; 422 if (fwriteBulkObject(fp,&key) == 0) goto werr; 423 if (fwriteBulkLongLong(fp,llval) == 0) goto werr; 424 } 425 } else if (o->encoding == REDIS_ENCODING_HT) { 426 dictIterator *di = dictGetIterator(o->ptr); 427 dictEntry *de; 428 while((de = dictNext(di)) != NULL) { 429 robj *eleobj = dictGetEntryKey(de); 430 if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr; 431 if (fwriteBulkObject(fp,&key) == 0) goto werr; 432 if (fwriteBulkObject(fp,eleobj) == 0) goto werr; 433 } 434 dictReleaseIterator(di); 435 } else { 436 redisPanic("Unknown set encoding"); 437 } 438 } else if (o->type == REDIS_ZSET) { 439 /* Emit the ZADDs needed to rebuild the sorted set */ 440 char cmd[]="*4\r\n$4\r\nZADD\r\n"; 441 442 if (o->encoding == REDIS_ENCODING_ZIPLIST) { 443 unsigned char *zl = o->ptr; 444 unsigned char *eptr, *sptr; 445 unsigned char *vstr; 446 unsigned int vlen; 447 long long vll; 448 double score; 449 450 eptr = ziplistIndex(zl,0); 451 redisAssert(eptr != NULL); 452 sptr = ziplistNext(zl,eptr); 453 redisAssert(sptr != NULL); 454 455 while (eptr != NULL) { 456 redisAssert(ziplistGet(eptr,&vstr,&vlen,&vll)); 457 score = zzlGetScore(sptr); 458 459 if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr; 460 if (fwriteBulkObject(fp,&key) == 0) goto werr; 461 if (fwriteBulkDouble(fp,score) == 0) goto werr; 462 if (vstr != NULL) { 463 if (fwriteBulkString(fp,(char*)vstr,vlen) == 0) 464 goto werr; 465 } else { 466 if (fwriteBulkLongLong(fp,vll) == 0) 467 goto werr; 468 } 469 zzlNext(zl,&eptr,&sptr); 470 } 471 } else if (o->encoding == REDIS_ENCODING_SKIPLIST) { 472 zset *zs = o->ptr; 473 dictIterator *di = dictGetIterator(zs->dict); 474 dictEntry *de; 475 476 while((de = dictNext(di)) != NULL) { 477 robj *eleobj = dictGetEntryKey(de); 478 double *score = dictGetEntryVal(de); 479 480 if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr; 481 if (fwriteBulkObject(fp,&key) == 0) goto werr; 482 if (fwriteBulkDouble(fp,*score) == 0) goto werr; 483 if (fwriteBulkObject(fp,eleobj) == 0) goto werr; 484 } 485 dictReleaseIterator(di); 486 } else { 487 redisPanic("Unknown sorted set encoding"); 488 } 489 } else if (o->type == REDIS_HASH) { 490 char cmd[]="*4\r\n$4\r\nHSET\r\n"; 491 492 /* Emit the HSETs needed to rebuild the hash */ 493 if (o->encoding == REDIS_ENCODING_ZIPMAP) { 494 unsigned char *p = zipmapRewind(o->ptr); 495 unsigned char *field, *val; 496 unsigned int flen, vlen; 497 498 while((p = zipmapNext(p,&field,&flen,&val,&vlen)) != NULL) { 499 if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr; 500 if (fwriteBulkObject(fp,&key) == 0) goto werr; 501 if (fwriteBulkString(fp,(char*)field,flen) == 0) 502 goto werr; 503 if (fwriteBulkString(fp,(char*)val,vlen) == 0) 504 goto werr; 505 } 506 } else { 507 dictIterator *di = dictGetIterator(o->ptr); 508 dictEntry *de; 509 510 while((de = dictNext(di)) != NULL) { 511 robj *field = dictGetEntryKey(de); 512 robj *val = dictGetEntryVal(de); 513 514 if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr; 515 if (fwriteBulkObject(fp,&key) == 0) goto werr; 516 if (fwriteBulkObject(fp,field) == 0) goto werr; 517 if (fwriteBulkObject(fp,val) == 0) goto werr; 518 } 519 dictReleaseIterator(di); 520 } 521 } else { 522 redisPanic("Unknown object type"); 523 } 524 /* Save the expire time */ 525 if (expiretime != -1) { 526 char cmd[]="*3\r\n$8\r\nEXPIREAT\r\n"; 527 /* If this key is already expired skip it */ 528 if (expiretime < now) continue; 529 if (fwrite(cmd,sizeof(cmd)-1,1,fp) == 0) goto werr; 530 if (fwriteBulkObject(fp,&key) == 0) goto werr; 531 if (fwriteBulkLongLong(fp,expiretime) == 0) goto werr; 532 } 533 } 534 dictReleaseIterator(di); 535 } 536 537 /* Make sure data will not remain on the OS's output buffers */ 538 fflush(fp); 539 aof_fsync(fileno(fp)); 540 fclose(fp); 541 542 /* Use RENAME to make sure the DB file is changed atomically only 543 * if the generate DB file is ok. */ 544 if (rename(tmpfile,filename) == -1) { 545 redisLog(REDIS_WARNING,"Error moving temp append only file on the final destination: %s", strerror(errno)); 546 unlink(tmpfile); 547 return REDIS_ERR; 548 } 549 redisLog(REDIS_NOTICE,"SYNC append only file rewrite performed"); 550 return REDIS_OK; 551 552 werr: 553 fclose(fp); 554 unlink(tmpfile); 555 redisLog(REDIS_WARNING,"Write error writing append only file on disk: %s", strerror(errno)); 556 if (di) dictReleaseIterator(di); 557 return REDIS_ERR; 558 } 559 560 /* This is how rewriting of the append only file in background works: 561 * 562 * 1) The user calls BGREWRITEAOF 563 * 2) Redis calls this function, that forks(): 564 * 2a) the child rewrite the append only file in a temp file. 565 * 2b) the parent accumulates differences in server.bgrewritebuf. 566 * 3) When the child finished '2a' exists. 567 * 4) The parent will trap the exit code, if it's OK, will append the 568 * data accumulated into server.bgrewritebuf into the temp file, and 569 * finally will rename(2) the temp file in the actual file name. 570 * The the new file is reopened as the new append only file. Profit! 571 */ 572 int rewriteAppendOnlyFileBackground(void) { 573 pid_t childpid; 574 long long start; 575 576 if (server.bgrewritechildpid != -1) return REDIS_ERR; 577 start = ustime(); 578 if ((childpid = fork()) == 0) { 579 char tmpfile[256]; 580 581 /* Child */ 582 if (server.ipfd > 0) close(server.ipfd); 583 if (server.sofd > 0) close(server.sofd); 584 snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", (int) getpid()); 585 if (rewriteAppendOnlyFile(tmpfile) == REDIS_OK) { 586 _exit(0); 587 } else { 588 _exit(1); 589 } 590 } else { 591 /* Parent */ 592 server.stat_fork_time = ustime()-start; 593 if (childpid == -1) { 594 redisLog(REDIS_WARNING, 595 "Can't rewrite append only file in background: fork: %s", 596 strerror(errno)); 597 return REDIS_ERR; 598 } 599 redisLog(REDIS_NOTICE, 600 "Background append only file rewriting started by pid %d",childpid); 601 server.bgrewritechildpid = childpid; 602 updateDictResizePolicy(); 603 /* We set appendseldb to -1 in order to force the next call to the 604 * feedAppendOnlyFile() to issue a SELECT command, so the differences 605 * accumulated by the parent into server.bgrewritebuf will start 606 * with a SELECT statement and it will be safe to merge. */ 607 server.appendseldb = -1; 608 return REDIS_OK; 609 } 610 return REDIS_OK; /* unreached */ 611 } 612 613 void bgrewriteaofCommand(redisClient *c) { 614 if (server.bgrewritechildpid != -1) { 615 addReplyError(c,"Background append only file rewriting already in progress"); 616 } else if (server.bgsavechildpid != -1) { 617 server.aofrewrite_scheduled = 1; 618 addReplyStatus(c,"Background append only file rewriting scheduled"); 619 } else if (rewriteAppendOnlyFileBackground() == REDIS_OK) { 620 addReplyStatus(c,"Background append only file rewriting started"); 621 } else { 622 addReply(c,shared.err); 623 } 624 } 625 626 void aofRemoveTempFile(pid_t childpid) { 627 char tmpfile[256]; 628 629 snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", (int) childpid); 630 unlink(tmpfile); 631 } 632 633 /* Update the server.appendonly_current_size filed explicitly using stat(2) 634 * to check the size of the file. This is useful after a rewrite or after 635 * a restart, normally the size is updated just adding the write length 636 * to the current lenght, that is much faster. */ 637 void aofUpdateCurrentSize(void) { 638 struct redis_stat sb; 639 640 if (redis_fstat(server.appendfd,&sb) == -1) { 641 redisLog(REDIS_WARNING,"Unable to check the AOF length: %s", 642 strerror(errno)); 643 } else { 644 server.appendonly_current_size = sb.st_size; 645 } 646 } 647 648 /* A background append only file rewriting (BGREWRITEAOF) terminated its work. 649 * Handle this. */ 650 void backgroundRewriteDoneHandler(int exitcode, int bysignal) { 651 if (!bysignal && exitcode == 0) { 652 int fd; 653 char tmpfile[256]; 654 655 redisLog(REDIS_NOTICE, 656 "Background append only file rewriting terminated with success"); 657 /* Now it's time to flush the differences accumulated by the parent */ 658 snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", (int) server.bgrewritechildpid); 659 fd = open(tmpfile,O_WRONLY|O_APPEND); 660 if (fd == -1) { 661 redisLog(REDIS_WARNING, "Not able to open the temp append only file produced by the child: %s", strerror(errno)); 662 goto cleanup; 663 } 664 /* Flush our data... */ 665 if (write(fd,server.bgrewritebuf,sdslen(server.bgrewritebuf)) != 666 (signed) sdslen(server.bgrewritebuf)) { 667 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)); 668 close(fd); 669 goto cleanup; 670 } 671 redisLog(REDIS_NOTICE,"Parent diff flushed into the new append log file with success (%lu bytes)",sdslen(server.bgrewritebuf)); 672 /* Now our work is to rename the temp file into the stable file. And 673 * switch the file descriptor used by the server for append only. */ 674 if (rename(tmpfile,server.appendfilename) == -1) { 675 redisLog(REDIS_WARNING,"Can't rename the temp append only file into the stable one: %s", strerror(errno)); 676 close(fd); 677 goto cleanup; 678 } 679 /* Mission completed... almost */ 680 redisLog(REDIS_NOTICE,"Append only file successfully rewritten."); 681 if (server.appendfd != -1) { 682 /* If append only is actually enabled... */ 683 close(server.appendfd); 684 server.appendfd = fd; 685 if (server.appendfsync != APPENDFSYNC_NO) aof_fsync(fd); 686 server.appendseldb = -1; /* Make sure it will issue SELECT */ 687 redisLog(REDIS_NOTICE,"The new append only file was selected for future appends."); 688 aofUpdateCurrentSize(); 689 server.auto_aofrewrite_base_size = server.appendonly_current_size; 690 } else { 691 /* If append only is disabled we just generate a dump in this 692 * format. Why not? */ 693 close(fd); 694 } 695 } else if (!bysignal && exitcode != 0) { 696 redisLog(REDIS_WARNING, "Background append only file rewriting error"); 697 } else { 698 redisLog(REDIS_WARNING, 699 "Background append only file rewriting terminated by signal %d", 700 bysignal); 701 } 702 cleanup: 703 sdsfree(server.bgrewritebuf); 704 server.bgrewritebuf = sdsempty(); 705 aofRemoveTempFile(server.bgrewritechildpid); 706 server.bgrewritechildpid = -1; 707 } 708