server.appendfd = -1;
server.appendseldb = -1;
server.appendonly = 0;
+ server.aof_wait_rewrite = 0;
/* rewrite operation in progress? kill it, wait child exit */
if (server.bgrewritechildpid != -1) {
int statloc;
/* reset the buffer accumulating changes while the child saves */
sdsfree(server.bgrewritebuf);
server.bgrewritebuf = sdsempty();
+ aofRemoveTempFile(server.bgrewritechildpid);
server.bgrewritechildpid = -1;
}
}
server.lastfsync = time(NULL);
server.appendfd = open(server.appendfilename,O_WRONLY|O_APPEND|O_CREAT,0644);
if (server.appendfd == -1) {
- redisLog(REDIS_WARNING,"Used tried to switch on AOF via CONFIG, but I can't open the AOF file: %s",strerror(errno));
+ redisLog(REDIS_WARNING,"Redis needs to enable the AOF but can't open the append only file: %s",strerror(errno));
return REDIS_ERR;
}
if (rewriteAppendOnlyFileBackground() == REDIS_ERR) {
server.appendonly = 0;
close(server.appendfd);
- redisLog(REDIS_WARNING,"User tried turning on AOF with CONFIG SET but I can't trigger a background AOF rewrite operation. Check the above logs for more info about the error.");
+ redisLog(REDIS_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 REDIS_ERR;
}
+ /* We correctly switched on AOF, now wait for the rerwite to be complete
+ * in order to append data on disk. */
+ server.aof_wait_rewrite = 1;
return REDIS_OK;
}
/* 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. */
- server.aofbuf = sdscatlen(server.aofbuf,buf,sdslen(buf));
+ * positive reply about the operation performed.
+ *
+ * Note, we don't add stuff in the AOF buffer if aof_wait_rewrite is
+ * non zero, as this means we are starting with a new AOF and the
+ * current one is meaningless (this happens for instance after
+ * a slave resyncs with its master). */
+ if (!server.aof_wait_rewrite) {
+ server.aofbuf = sdscatlen(server.aofbuf,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
return 1;
}
+/* Emit the commands needed to rebuild a sorted set object.
+ * The function returns 0 on error, 1 on success. */
+int rewriteSortedSetObject(rio *r, robj *key, robj *o) {
+ long long count = 0, items = zsetLength(o);
+
+ if (o->encoding == REDIS_ENCODING_ZIPLIST) {
+ unsigned char *zl = o->ptr;
+ unsigned char *eptr, *sptr;
+ unsigned char *vstr;
+ unsigned int vlen;
+ long long vll;
+ double score;
+
+ eptr = ziplistIndex(zl,0);
+ redisAssert(eptr != NULL);
+ sptr = ziplistNext(zl,eptr);
+ redisAssert(sptr != NULL);
+
+ while (eptr != NULL) {
+ redisAssert(ziplistGet(eptr,&vstr,&vlen,&vll));
+ score = zzlGetScore(sptr);
+
+ if (count == 0) {
+ int cmd_items = (items > REDIS_AOFREWRITE_ITEMS_PER_CMD) ?
+ REDIS_AOFREWRITE_ITEMS_PER_CMD : items;
+
+ 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 (vstr != NULL) {
+ if (rioWriteBulkString(r,(char*)vstr,vlen) == 0) return 0;
+ } else {
+ if (rioWriteBulkLongLong(r,vll) == 0) return 0;
+ }
+ zzlNext(zl,&eptr,&sptr);
+ if (++count == REDIS_AOFREWRITE_ITEMS_PER_CMD) count = 0;
+ items--;
+ }
+ } else if (o->encoding == REDIS_ENCODING_SKIPLIST) {
+ zset *zs = o->ptr;
+ dictIterator *di = dictGetIterator(zs->dict);
+ dictEntry *de;
+
+ while((de = dictNext(di)) != NULL) {
+ robj *eleobj = dictGetKey(de);
+ double *score = dictGetVal(de);
+
+ if (count == 0) {
+ int cmd_items = (items > REDIS_AOFREWRITE_ITEMS_PER_CMD) ?
+ REDIS_AOFREWRITE_ITEMS_PER_CMD : items;
+
+ 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 (++count == REDIS_AOFREWRITE_ITEMS_PER_CMD) count = 0;
+ items--;
+ }
+ dictReleaseIterator(di);
+ } else {
+ redisPanic("Unknown sorted zset encoding");
+ }
+ return 1;
+}
+
+/* Emit the commands needed to rebuild a hash object.
+ * The function returns 0 on error, 1 on success. */
+int rewriteHashObject(rio *r, robj *key, robj *o) {
+ long long count = 0, items = hashTypeLength(o);
+
+ if (o->encoding == REDIS_ENCODING_ZIPMAP) {
+ unsigned char *p = zipmapRewind(o->ptr);
+ unsigned char *field, *val;
+ unsigned int flen, vlen;
+
+ while((p = zipmapNext(p,&field,&flen,&val,&vlen)) != NULL) {
+ if (count == 0) {
+ int cmd_items = (items > REDIS_AOFREWRITE_ITEMS_PER_CMD) ?
+ REDIS_AOFREWRITE_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 (rioWriteBulkString(r,(char*)field,flen) == 0) return 0;
+ if (rioWriteBulkString(r,(char*)val,vlen) == 0) return 0;
+ if (++count == REDIS_AOFREWRITE_ITEMS_PER_CMD) count = 0;
+ items--;
+ }
+ } else {
+ dictIterator *di = dictGetIterator(o->ptr);
+ dictEntry *de;
+
+ while((de = dictNext(di)) != NULL) {
+ robj *field = dictGetKey(de);
+ robj *val = dictGetVal(de);
+
+ if (count == 0) {
+ int cmd_items = (items > REDIS_AOFREWRITE_ITEMS_PER_CMD) ?
+ REDIS_AOFREWRITE_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 (rioWriteBulkObject(r,field) == 0) return 0;
+ if (rioWriteBulkObject(r,val) == 0) return 0;
+ if (++count == REDIS_AOFREWRITE_ITEMS_PER_CMD) count = 0;
+ items--;
+ }
+ dictReleaseIterator(di);
+ }
+ return 1;
+}
+
/* Write a sequence of commands able to fully rebuild the dataset into
* "filename". Used both by REWRITEAOF and BGREWRITEAOF.
*
} else if (o->type == REDIS_SET) {
if (rewriteSetObject(&aof,&key,o) == 0) goto werr;
} else if (o->type == REDIS_ZSET) {
- /* Emit the ZADDs needed to rebuild the sorted set */
- char cmd[]="*4\r\n$4\r\nZADD\r\n";
-
- if (o->encoding == REDIS_ENCODING_ZIPLIST) {
- unsigned char *zl = o->ptr;
- unsigned char *eptr, *sptr;
- unsigned char *vstr;
- unsigned int vlen;
- long long vll;
- double score;
-
- eptr = ziplistIndex(zl,0);
- redisAssert(eptr != NULL);
- sptr = ziplistNext(zl,eptr);
- redisAssert(sptr != NULL);
-
- while (eptr != NULL) {
- redisAssert(ziplistGet(eptr,&vstr,&vlen,&vll));
- score = zzlGetScore(sptr);
-
- if (rioWrite(&aof,cmd,sizeof(cmd)-1) == 0) goto werr;
- if (rioWriteBulkObject(&aof,&key) == 0) goto werr;
- if (rioWriteBulkDouble(&aof,score) == 0) goto werr;
- if (vstr != NULL) {
- if (rioWriteBulkString(&aof,(char*)vstr,vlen) == 0)
- goto werr;
- } else {
- if (rioWriteBulkLongLong(&aof,vll) == 0)
- goto werr;
- }
- zzlNext(zl,&eptr,&sptr);
- }
- } else if (o->encoding == REDIS_ENCODING_SKIPLIST) {
- zset *zs = o->ptr;
- dictIterator *di = dictGetIterator(zs->dict);
- dictEntry *de;
-
- while((de = dictNext(di)) != NULL) {
- robj *eleobj = dictGetKey(de);
- double *score = dictGetVal(de);
-
- if (rioWrite(&aof,cmd,sizeof(cmd)-1) == 0) goto werr;
- if (rioWriteBulkObject(&aof,&key) == 0) goto werr;
- if (rioWriteBulkDouble(&aof,*score) == 0) goto werr;
- if (rioWriteBulkObject(&aof,eleobj) == 0) goto werr;
- }
- dictReleaseIterator(di);
- } else {
- redisPanic("Unknown sorted set encoding");
- }
+ if (rewriteSortedSetObject(&aof,&key,o) == 0) goto werr;
} else if (o->type == REDIS_HASH) {
- char cmd[]="*4\r\n$4\r\nHSET\r\n";
-
- /* Emit the HSETs needed to rebuild the hash */
- if (o->encoding == REDIS_ENCODING_ZIPMAP) {
- unsigned char *p = zipmapRewind(o->ptr);
- unsigned char *field, *val;
- unsigned int flen, vlen;
-
- while((p = zipmapNext(p,&field,&flen,&val,&vlen)) != NULL) {
- if (rioWrite(&aof,cmd,sizeof(cmd)-1) == 0) goto werr;
- if (rioWriteBulkObject(&aof,&key) == 0) goto werr;
- if (rioWriteBulkString(&aof,(char*)field,flen) == 0)
- goto werr;
- if (rioWriteBulkString(&aof,(char*)val,vlen) == 0)
- goto werr;
- }
- } else {
- dictIterator *di = dictGetIterator(o->ptr);
- dictEntry *de;
-
- while((de = dictNext(di)) != NULL) {
- robj *field = dictGetKey(de);
- robj *val = dictGetVal(de);
-
- if (rioWrite(&aof,cmd,sizeof(cmd)-1) == 0) goto werr;
- if (rioWriteBulkObject(&aof,&key) == 0) goto werr;
- if (rioWriteBulkObject(&aof,field) == 0) goto werr;
- if (rioWriteBulkObject(&aof,val) == 0) goto werr;
- }
- dictReleaseIterator(di);
- }
+ if (rewriteHashObject(&aof,&key,o) == 0) goto werr;
} else {
redisPanic("Unknown object type");
}
}
redisLog(REDIS_NOTICE, "Background AOF rewrite successful");
+ server.aof_wait_rewrite = 0;
/* Asynchronously close the overwritten AOF. */
if (oldfd != -1) bioCreateBackgroundJob(REDIS_BIO_CLOSE_FILE,(void*)(long)oldfd,NULL,NULL);
server.bgrewritebuf = sdsempty();
aofRemoveTempFile(server.bgrewritechildpid);
server.bgrewritechildpid = -1;
+ /* If we were waiting for an AOF rewrite before to start appending
+ * to the AOF again (this happens both when the user switches on
+ * AOF with CONFIG SET, and after a slave with AOF enabled syncs with
+ * the master), but the rewrite failed (otherwise aof_wait_rewrite
+ * would be zero), we need to schedule a new one. */
+ if (server.aof_wait_rewrite) server.aofrewrite_scheduled = 1;
}