X-Git-Url: https://git.saurik.com/redis.git/blobdiff_plain/bad7d097e95ec0735c41f85b50ace65bec6134ae..4abd096f5188448a2c0e75511a049b3a3fd8e9b1:/src/rdb.c diff --git a/src/rdb.c b/src/rdb.c index 12221b9f..a2471aef 100644 --- a/src/rdb.c +++ b/src/rdb.c @@ -7,39 +7,51 @@ #include #include #include +#include + +/* Convenience wrapper around fwrite, that returns the number of bytes written + * to the file instead of the number of objects (see fwrite(3)) and -1 in the + * case of an error. It also supports a NULL *fp to skip writing altogether + * instead of writing to /dev/null. */ +static int rdbWriteRaw(FILE *fp, void *p, size_t len) { + if (fp != NULL && fwrite(p,len,1,fp) == 0) return -1; + return len; +} int rdbSaveType(FILE *fp, unsigned char type) { - if (fwrite(&type,1,1,fp) == 0) return -1; - return 0; + return rdbWriteRaw(fp,&type,1); } int rdbSaveTime(FILE *fp, time_t t) { int32_t t32 = (int32_t) t; - if (fwrite(&t32,4,1,fp) == 0) return -1; - return 0; + return rdbWriteRaw(fp,&t32,4); } /* check rdbLoadLen() comments for more info */ int rdbSaveLen(FILE *fp, uint32_t len) { unsigned char buf[2]; + int nwritten; if (len < (1<<6)) { /* Save a 6 bit len */ buf[0] = (len&0xFF)|(REDIS_RDB_6BITLEN<<6); - if (fwrite(buf,1,1,fp) == 0) return -1; + if (rdbWriteRaw(fp,buf,1) == -1) return -1; + nwritten = 1; } else if (len < (1<<14)) { /* Save a 14 bit len */ buf[0] = ((len>>8)&0xFF)|(REDIS_RDB_14BITLEN<<6); buf[1] = len&0xFF; - if (fwrite(buf,2,1,fp) == 0) return -1; + if (rdbWriteRaw(fp,buf,2) == -1) return -1; + nwritten = 2; } else { /* Save a 32 bit len */ buf[0] = (REDIS_RDB_32BITLEN<<6); - if (fwrite(buf,1,1,fp) == 0) return -1; + if (rdbWriteRaw(fp,buf,1) == -1) return -1; len = htonl(len); - if (fwrite(&len,4,1,fp) == 0) return -1; + if (rdbWriteRaw(fp,&len,4) == -1) return -1; + nwritten = 1+4; } - return 0; + return nwritten; } /* Encode 'value' as an integer if possible (if integer will fit the @@ -92,6 +104,7 @@ int rdbTryIntegerEncoding(char *s, size_t len, unsigned char *enc) { int rdbSaveLzfStringObject(FILE *fp, unsigned char *s, size_t len) { size_t comprlen, outlen; unsigned char byte; + int n, nwritten = 0; void *out; /* We require at least four bytes compression for this to be worth it */ @@ -105,12 +118,20 @@ int rdbSaveLzfStringObject(FILE *fp, unsigned char *s, size_t len) { } /* Data compressed! Let's save it on disk */ byte = (REDIS_RDB_ENCVAL<<6)|REDIS_RDB_ENC_LZF; - if (fwrite(&byte,1,1,fp) == 0) goto writeerr; - if (rdbSaveLen(fp,comprlen) == -1) goto writeerr; - if (rdbSaveLen(fp,len) == -1) goto writeerr; - if (fwrite(out,comprlen,1,fp) == 0) goto writeerr; + if ((n = rdbWriteRaw(fp,&byte,1)) == -1) goto writeerr; + nwritten += n; + + if ((n = rdbSaveLen(fp,comprlen)) == -1) goto writeerr; + nwritten += n; + + if ((n = rdbSaveLen(fp,len)) == -1) goto writeerr; + nwritten += n; + + if ((n = rdbWriteRaw(fp,out,comprlen)) == -1) goto writeerr; + nwritten += n; + zfree(out); - return comprlen; + return nwritten; writeerr: zfree(out); @@ -118,50 +139,56 @@ writeerr: } /* Save a string objet as [len][data] on disk. If the object is a string - * representation of an integer value we try to safe it in a special form */ + * representation of an integer value we try to save it in a special form */ int rdbSaveRawString(FILE *fp, unsigned char *s, size_t len) { int enclen; + int n, nwritten = 0; /* Try integer encoding */ if (len <= 11) { unsigned char buf[5]; if ((enclen = rdbTryIntegerEncoding((char*)s,len,buf)) > 0) { - if (fwrite(buf,enclen,1,fp) == 0) return -1; - return 0; + if (rdbWriteRaw(fp,buf,enclen) == -1) return -1; + return enclen; } } /* Try LZF compression - under 20 bytes it's unable to compress even * aaaaaaaaaaaaaaaaaa so skip it */ if (server.rdbcompression && len > 20) { - int retval; - - retval = rdbSaveLzfStringObject(fp,s,len); - if (retval == -1) return -1; - if (retval > 0) return 0; - /* retval == 0 means data can't be compressed, save the old way */ + n = rdbSaveLzfStringObject(fp,s,len); + if (n == -1) return -1; + if (n > 0) return n; + /* Return value of 0 means data can't be compressed, save the old way */ } /* Store verbatim */ - if (rdbSaveLen(fp,len) == -1) return -1; - if (len && fwrite(s,len,1,fp) == 0) return -1; - return 0; + if ((n = rdbSaveLen(fp,len)) == -1) return -1; + nwritten += n; + if (len > 0) { + if (rdbWriteRaw(fp,s,len) == -1) return -1; + nwritten += len; + } + return nwritten; } /* Save a long long value as either an encoded string or a string. */ int rdbSaveLongLongAsStringObject(FILE *fp, long long value) { unsigned char buf[32]; + int n, nwritten = 0; int enclen = rdbEncodeInteger(value,buf); if (enclen > 0) { - if (fwrite(buf,enclen,1,fp) == 0) return -1; + return rdbWriteRaw(fp,buf,enclen); } else { /* Encode as string */ enclen = ll2string((char*)buf,32,value); redisAssert(enclen < 32); - if (rdbSaveLen(fp,enclen) == -1) return -1; - if (fwrite(buf,enclen,1,fp) == 0) return -1; + if ((n = rdbSaveLen(fp,enclen)) == -1) return -1; + nwritten += n; + if ((n = rdbWriteRaw(fp,buf,enclen)) == -1) return -1; + nwritten += n; } - return 0; + return nwritten; } /* Like rdbSaveStringObjectRaw() but handle encoded objects */ @@ -215,45 +242,37 @@ int rdbSaveDoubleValue(FILE *fp, double val) { buf[0] = strlen((char*)buf+1); len = buf[0]+1; } - if (fwrite(buf,len,1,fp) == 0) return -1; - return 0; + return rdbWriteRaw(fp,buf,len); } -/* Save a Redis object. */ +/* Save a Redis object. Returns -1 on error, 0 on success. */ int rdbSaveObject(FILE *fp, robj *o) { + int n, nwritten = 0; + if (o->type == REDIS_STRING) { /* Save a string value */ - if (rdbSaveStringObject(fp,o) == -1) return -1; + if ((n = rdbSaveStringObject(fp,o)) == -1) return -1; + nwritten += n; } else if (o->type == REDIS_LIST) { /* Save a list value */ if (o->encoding == REDIS_ENCODING_ZIPLIST) { - unsigned char *p; - unsigned char *vstr; - unsigned int vlen; - long long vlong; - - if (rdbSaveLen(fp,ziplistLen(o->ptr)) == -1) return -1; - p = ziplistIndex(o->ptr,0); - while(ziplistGet(p,&vstr,&vlen,&vlong)) { - if (vstr) { - if (rdbSaveRawString(fp,vstr,vlen) == -1) - return -1; - } else { - if (rdbSaveLongLongAsStringObject(fp,vlong) == -1) - return -1; - } - p = ziplistNext(o->ptr,p); - } + size_t l = ziplistBlobLen((unsigned char*)o->ptr); + + if ((n = rdbSaveRawString(fp,o->ptr,l)) == -1) return -1; + nwritten += n; } else if (o->encoding == REDIS_ENCODING_LINKEDLIST) { list *list = o->ptr; listIter li; listNode *ln; - if (rdbSaveLen(fp,listLength(list)) == -1) return -1; + if ((n = rdbSaveLen(fp,listLength(list))) == -1) return -1; + nwritten += n; + listRewind(list,&li); while((ln = listNext(&li))) { robj *eleobj = listNodeValue(ln); - if (rdbSaveStringObject(fp,eleobj) == -1) return -1; + if ((n = rdbSaveStringObject(fp,eleobj)) == -1) return -1; + nwritten += n; } } else { redisPanic("Unknown list encoding"); @@ -265,88 +284,124 @@ int rdbSaveObject(FILE *fp, robj *o) { dictIterator *di = dictGetIterator(set); dictEntry *de; - if (rdbSaveLen(fp,dictSize(set)) == -1) return -1; + if ((n = rdbSaveLen(fp,dictSize(set))) == -1) return -1; + nwritten += n; + while((de = dictNext(di)) != NULL) { robj *eleobj = dictGetEntryKey(de); - if (rdbSaveStringObject(fp,eleobj) == -1) return -1; + if ((n = rdbSaveStringObject(fp,eleobj)) == -1) return -1; + nwritten += n; } dictReleaseIterator(di); } else if (o->encoding == REDIS_ENCODING_INTSET) { - intset *is = o->ptr; - long long llval; - int i = 0; + size_t l = intsetBlobLen((intset*)o->ptr); - if (rdbSaveLen(fp,intsetLen(is)) == -1) return -1; - while(intsetGet(is,i++,&llval)) { - if (rdbSaveLongLongAsStringObject(fp,llval) == -1) return -1; - } + if ((n = rdbSaveRawString(fp,o->ptr,l)) == -1) return -1; + nwritten += n; } else { redisPanic("Unknown set encoding"); } } else if (o->type == REDIS_ZSET) { - /* Save a set value */ - zset *zs = o->ptr; - dictIterator *di = dictGetIterator(zs->dict); - dictEntry *de; + /* Save a sorted set value */ + if (o->encoding == REDIS_ENCODING_ZIPLIST) { + size_t l = ziplistBlobLen((unsigned char*)o->ptr); - if (rdbSaveLen(fp,dictSize(zs->dict)) == -1) return -1; - while((de = dictNext(di)) != NULL) { - robj *eleobj = dictGetEntryKey(de); - double *score = dictGetEntryVal(de); + if ((n = rdbSaveRawString(fp,o->ptr,l)) == -1) return -1; + nwritten += n; + } else if (o->encoding == REDIS_ENCODING_SKIPLIST) { + zset *zs = o->ptr; + dictIterator *di = dictGetIterator(zs->dict); + dictEntry *de; - if (rdbSaveStringObject(fp,eleobj) == -1) return -1; - if (rdbSaveDoubleValue(fp,*score) == -1) return -1; + if ((n = rdbSaveLen(fp,dictSize(zs->dict))) == -1) return -1; + nwritten += n; + + while((de = dictNext(di)) != NULL) { + robj *eleobj = dictGetEntryKey(de); + double *score = dictGetEntryVal(de); + + if ((n = rdbSaveStringObject(fp,eleobj)) == -1) return -1; + nwritten += n; + if ((n = rdbSaveDoubleValue(fp,*score)) == -1) return -1; + nwritten += n; + } + dictReleaseIterator(di); + } else { + redisPanic("Unknown sorted set encoding"); } - dictReleaseIterator(di); } else if (o->type == REDIS_HASH) { /* Save a hash value */ if (o->encoding == REDIS_ENCODING_ZIPMAP) { - unsigned char *p = zipmapRewind(o->ptr); - unsigned int count = zipmapLen(o->ptr); - unsigned char *key, *val; - unsigned int klen, vlen; - - if (rdbSaveLen(fp,count) == -1) return -1; - while((p = zipmapNext(p,&key,&klen,&val,&vlen)) != NULL) { - if (rdbSaveRawString(fp,key,klen) == -1) return -1; - if (rdbSaveRawString(fp,val,vlen) == -1) return -1; - } + size_t l = zipmapBlobLen((unsigned char*)o->ptr); + + if ((n = rdbSaveRawString(fp,o->ptr,l)) == -1) return -1; + nwritten += n; } else { dictIterator *di = dictGetIterator(o->ptr); dictEntry *de; - if (rdbSaveLen(fp,dictSize((dict*)o->ptr)) == -1) return -1; + if ((n = rdbSaveLen(fp,dictSize((dict*)o->ptr))) == -1) return -1; + nwritten += n; + while((de = dictNext(di)) != NULL) { robj *key = dictGetEntryKey(de); robj *val = dictGetEntryVal(de); - if (rdbSaveStringObject(fp,key) == -1) return -1; - if (rdbSaveStringObject(fp,val) == -1) return -1; + if ((n = rdbSaveStringObject(fp,key)) == -1) return -1; + nwritten += n; + if ((n = rdbSaveStringObject(fp,val)) == -1) return -1; + nwritten += n; } dictReleaseIterator(di); } } else { redisPanic("Unknown object type"); } - return 0; + return nwritten; } /* Return the length the object will have on disk if saved with * the rdbSaveObject() function. Currently we use a trick to get * this length with very little changes to the code. In the future * we could switch to a faster solution. */ -off_t rdbSavedObjectLen(robj *o, FILE *fp) { - if (fp == NULL) fp = server.devnull; - rewind(fp); - redisAssert(rdbSaveObject(fp,o) != 1); - return ftello(fp); +off_t rdbSavedObjectLen(robj *o) { + int len = rdbSaveObject(NULL,o); + redisAssert(len != -1); + return len; } -/* Return the number of pages required to save this object in the swap file */ -off_t rdbSavedObjectPages(robj *o, FILE *fp) { - off_t bytes = rdbSavedObjectLen(o,fp); - - return (bytes+(server.vm_page_size-1))/server.vm_page_size; +/* Save a key-value pair, with expire time, type, key, value. + * On error -1 is returned. + * On success if the key was actaully saved 1 is returned, otherwise 0 + * is returned (the key was already expired). */ +int rdbSaveKeyValuePair(FILE *fp, robj *key, robj *val, + time_t expiretime, time_t now) +{ + int vtype; + + /* Save the expire time */ + if (expiretime != -1) { + /* If this key is already expired skip it */ + if (expiretime < now) return 0; + if (rdbSaveType(fp,REDIS_EXPIRETIME) == -1) return -1; + if (rdbSaveTime(fp,expiretime) == -1) return -1; + } + /* Fix the object type if needed, to support saving zipmaps, ziplists, + * and intsets, directly as blobs of bytes: they are already serialized. */ + vtype = val->type; + if (vtype == REDIS_HASH && val->encoding == REDIS_ENCODING_ZIPMAP) + vtype = REDIS_HASH_ZIPMAP; + else if (vtype == REDIS_LIST && val->encoding == REDIS_ENCODING_ZIPLIST) + vtype = REDIS_LIST_ZIPLIST; + else if (vtype == REDIS_SET && val->encoding == REDIS_ENCODING_INTSET) + vtype = REDIS_SET_INTSET; + else if (vtype == REDIS_ZSET && val->encoding == REDIS_ENCODING_ZIPLIST) + vtype = REDIS_ZSET_ZIPLIST; + /* Save type, key, value */ + if (rdbSaveType(fp,vtype) == -1) return -1; + if (rdbSaveStringObject(fp,key) == -1) return -1; + if (rdbSaveObject(fp,val) == -1) return -1; + return 1; } /* Save the DB on disk. Return REDIS_ERR on error, REDIS_OK on success */ @@ -358,19 +413,19 @@ int rdbSave(char *filename) { int j; time_t now = time(NULL); - /* Wait for I/O therads to terminate, just in case this is a - * foreground-saving, to avoid seeking the swap file descriptor at the - * same time. */ - if (server.vm_enabled) - waitEmptyIOJobsQueue(); + if (server.ds_enabled) { + cacheForcePointInTime(); + return dsRdbSave(filename); + } snprintf(tmpfile,256,"temp-%d.rdb", (int) getpid()); fp = fopen(tmpfile,"w"); if (!fp) { - redisLog(REDIS_WARNING, "Failed saving the DB: %s", strerror(errno)); + redisLog(REDIS_WARNING, "Failed opening .rdb for saving: %s", + strerror(errno)); return REDIS_ERR; } - if (fwrite("REDIS0001",9,1,fp) == 0) goto werr; + if (fwrite("REDIS0002",9,1,fp) == 0) goto werr; for (j = 0; j < server.dbnum; j++) { redisDb *db = server.db+j; dict *d = db->dict; @@ -389,38 +444,11 @@ int rdbSave(char *filename) { while((de = dictNext(di)) != NULL) { sds keystr = dictGetEntryKey(de); robj key, *o = dictGetEntryVal(de); - time_t expiretime; + time_t expire; initStaticStringObject(key,keystr); - expiretime = getExpire(db,&key); - - /* Save the expire time */ - if (expiretime != -1) { - /* If this key is already expired skip it */ - if (expiretime < now) continue; - if (rdbSaveType(fp,REDIS_EXPIRETIME) == -1) goto werr; - if (rdbSaveTime(fp,expiretime) == -1) goto werr; - } - /* Save the key and associated value. This requires special - * handling if the value is swapped out. */ - if (!server.vm_enabled || o->storage == REDIS_VM_MEMORY || - o->storage == REDIS_VM_SWAPPING) { - /* Save type, key, value */ - if (rdbSaveType(fp,o->type) == -1) goto werr; - if (rdbSaveStringObject(fp,&key) == -1) goto werr; - if (rdbSaveObject(fp,o) == -1) goto werr; - } else { - /* REDIS_VM_SWAPPED or REDIS_VM_LOADING */ - robj *po; - /* Get a preview of the object in memory */ - po = vmPreviewObject(o); - /* Save type, key, value */ - if (rdbSaveType(fp,po->type) == -1) goto werr; - if (rdbSaveStringObject(fp,&key) == -1) goto werr; - if (rdbSaveObject(fp,po) == -1) goto werr; - /* Remove the loaded object from memory */ - decrRefCount(po); - } + expire = getExpire(db,&key); + if (rdbSaveKeyValuePair(fp,&key,o,expire,now) == -1) goto werr; } dictReleaseIterator(di); } @@ -454,20 +482,30 @@ werr: int rdbSaveBackground(char *filename) { pid_t childpid; + long long start; + + if (server.bgsavechildpid != -1 || + server.bgsavethread != (pthread_t) -1) return REDIS_ERR; + + server.dirty_before_bgsave = server.dirty; - if (server.bgsavechildpid != -1) return REDIS_ERR; - if (server.vm_enabled) waitEmptyIOJobsQueue(); + if (server.ds_enabled) { + cacheForcePointInTime(); + return dsRdbSaveBackground(filename); + } + + start = ustime(); if ((childpid = fork()) == 0) { + int retval; + /* Child */ - if (server.vm_enabled) vmReopenSwapFile(); - close(server.fd); - if (rdbSave(filename) == REDIS_OK) { - _exit(0); - } else { - _exit(1); - } + if (server.ipfd > 0) close(server.ipfd); + if (server.sofd > 0) close(server.sofd); + retval = rdbSave(filename); + _exit((retval == REDIS_OK) ? 0 : 1); } else { /* Parent */ + server.stat_fork_time = ustime()-start; if (childpid == -1) { redisLog(REDIS_WARNING,"Can't save in background: fork: %s", strerror(errno)); @@ -702,7 +740,7 @@ robj *rdbLoadObject(int type, FILE *fp) { if (o->encoding == REDIS_ENCODING_INTSET) { /* Fetch integer value from element */ - if (getLongLongFromObject(ele,&llval) == REDIS_OK) { + if (isObjectRepresentableAsLongLong(ele,&llval) == REDIS_OK) { o->ptr = intsetAdd(o->ptr,llval,NULL); } else { setTypeConvert(o,REDIS_ENCODING_HT); @@ -721,23 +759,37 @@ robj *rdbLoadObject(int type, FILE *fp) { } else if (type == REDIS_ZSET) { /* Read list/set value */ size_t zsetlen; + size_t maxelelen = 0; zset *zs; if ((zsetlen = rdbLoadLen(fp,NULL)) == REDIS_RDB_LENERR) return NULL; o = createZsetObject(); zs = o->ptr; + /* Load every single element of the list/set */ while(zsetlen--) { robj *ele; - double *score = zmalloc(sizeof(double)); + double score; + zskiplistNode *znode; if ((ele = rdbLoadEncodedStringObject(fp)) == NULL) return NULL; ele = tryObjectEncoding(ele); - if (rdbLoadDoubleValue(fp,score) == -1) return NULL; - dictAdd(zs->dict,ele,score); - zslInsert(zs->zsl,*score,ele); + if (rdbLoadDoubleValue(fp,&score) == -1) return NULL; + + /* Don't care about integer-encoded strings. */ + if (ele->encoding == REDIS_ENCODING_RAW && + sdslen(ele->ptr) > maxelelen) + maxelelen = sdslen(ele->ptr); + + znode = zslInsert(zs->zsl,score,ele); + dictAdd(zs->dict,ele,&znode->score); incrRefCount(ele); /* added to skiplist */ } + + /* Convert *after* loading, since sorted sets are not stored ordered. */ + if (zsetLength(o) <= server.zset_max_ziplist_entries && + maxelelen <= server.zset_max_ziplist_value) + zsetConvert(o,REDIS_ENCODING_ZIPLIST); } else if (type == REDIS_HASH) { size_t hashlen; @@ -784,20 +836,93 @@ robj *rdbLoadObject(int type, FILE *fp) { dictAdd((dict*)o->ptr,key,val); } } + } else if (type == REDIS_HASH_ZIPMAP || + type == REDIS_LIST_ZIPLIST || + type == REDIS_SET_INTSET || + type == REDIS_ZSET_ZIPLIST) + { + robj *aux = rdbLoadStringObject(fp); + + if (aux == NULL) return NULL; + o = createObject(REDIS_STRING,NULL); /* string is just placeholder */ + o->ptr = zmalloc(sdslen(aux->ptr)); + memcpy(o->ptr,aux->ptr,sdslen(aux->ptr)); + decrRefCount(aux); + + /* Fix the object encoding, and make sure to convert the encoded + * data type into the base type if accordingly to the current + * configuration there are too many elements in the encoded data + * type. Note that we only check the length and not max element + * size as this is an O(N) scan. Eventually everything will get + * converted. */ + switch(type) { + case REDIS_HASH_ZIPMAP: + o->type = REDIS_HASH; + o->encoding = REDIS_ENCODING_ZIPMAP; + if (zipmapLen(o->ptr) > server.hash_max_zipmap_entries) + convertToRealHash(o); + break; + case REDIS_LIST_ZIPLIST: + o->type = REDIS_LIST; + o->encoding = REDIS_ENCODING_ZIPLIST; + if (ziplistLen(o->ptr) > server.list_max_ziplist_entries) + listTypeConvert(o,REDIS_ENCODING_LINKEDLIST); + break; + case REDIS_SET_INTSET: + o->type = REDIS_SET; + o->encoding = REDIS_ENCODING_INTSET; + if (intsetLen(o->ptr) > server.set_max_intset_entries) + setTypeConvert(o,REDIS_ENCODING_HT); + break; + case REDIS_ZSET_ZIPLIST: + o->type = REDIS_ZSET; + o->encoding = REDIS_ENCODING_ZIPLIST; + if (zsetLength(o) > server.zset_max_ziplist_entries) + zsetConvert(o,REDIS_ENCODING_SKIPLIST); + break; + default: + redisPanic("Unknown encoding"); + break; + } } else { redisPanic("Unknown object type"); } return o; } +/* Mark that we are loading in the global state and setup the fields + * needed to provide loading stats. */ +void startLoading(FILE *fp) { + struct stat sb; + + /* Load the DB */ + server.loading = 1; + server.loading_start_time = time(NULL); + if (fstat(fileno(fp), &sb) == -1) { + server.loading_total_bytes = 1; /* just to avoid division by zero */ + } else { + server.loading_total_bytes = sb.st_size; + } +} + +/* Refresh the loading progress info */ +void loadingProgress(off_t pos) { + server.loading_loaded_bytes = pos; +} + +/* Loading finished */ +void stopLoading(void) { + server.loading = 0; +} + int rdbLoad(char *filename) { FILE *fp; uint32_t dbid; int type, retval, rdbver; - int swap_all_values = 0; redisDb *db = server.db+0; char buf[1024]; time_t expiretime, now = time(NULL); + long loops = 0; fp = fopen(filename,"r"); if (!fp) return REDIS_ERR; @@ -809,16 +934,23 @@ int rdbLoad(char *filename) { return REDIS_ERR; } rdbver = atoi(buf+5); - if (rdbver != 1) { + if (rdbver < 1 || rdbver > 2) { fclose(fp); redisLog(REDIS_WARNING,"Can't handle RDB format version %d",rdbver); return REDIS_ERR; } + + startLoading(fp); while(1) { robj *key, *val; - int force_swapout; - expiretime = -1; + + /* Serve the clients from time to time */ + if (!(loops++ % 1000)) { + loadingProgress(ftello(fp)); + aeProcessEvents(server.el, AE_FILE_EVENTS|AE_DONT_WAIT); + } + /* Read type. */ if ((type = rdbLoadType(fp)) == -1) goto eoferr; if (type == REDIS_EXPIRETIME) { @@ -857,46 +989,10 @@ int rdbLoad(char *filename) { /* Set the expire time if needed */ if (expiretime != -1) setExpire(db,key,expiretime); - /* Handle swapping while loading big datasets when VM is on */ - - /* If we detecter we are hopeless about fitting something in memory - * we just swap every new key on disk. Directly... - * Note that's important to check for this condition before resorting - * to random sampling, otherwise we may try to swap already - * swapped keys. */ - if (swap_all_values) { - dictEntry *de = dictFind(db->dict,key->ptr); - - /* de may be NULL since the key already expired */ - if (de) { - vmpointer *vp; - val = dictGetEntryVal(de); - - if (val->refcount == 1 && - (vp = vmSwapObjectBlocking(val)) != NULL) - dictGetEntryVal(de) = vp; - } - decrRefCount(key); - continue; - } decrRefCount(key); - - /* Flush data on disk once 32 MB of additional RAM are used... */ - force_swapout = 0; - if ((zmalloc_used_memory() - server.vm_max_memory) > 1024*1024*32) - force_swapout = 1; - - /* If we have still some hope of having some value fitting memory - * then we try random sampling. */ - if (!swap_all_values && server.vm_enabled && force_swapout) { - while (zmalloc_used_memory() > server.vm_max_memory) { - if (vmSwapOneObjectBlocking() == REDIS_ERR) break; - } - if (zmalloc_used_memory() > server.vm_max_memory) - swap_all_values = 1; /* We are already using too much mem */ - } } fclose(fp); + stopLoading(); return REDIS_OK; eoferr: /* unexpected end of file is handled here with a fatal exit */ @@ -906,24 +1002,47 @@ eoferr: /* unexpected end of file is handled here with a fatal exit */ } /* A background saving child (BGSAVE) terminated its work. Handle this. */ -void backgroundSaveDoneHandler(int statloc) { - int exitcode = WEXITSTATUS(statloc); - int bysignal = WIFSIGNALED(statloc); - +void backgroundSaveDoneHandler(int exitcode, int bysignal) { if (!bysignal && exitcode == 0) { redisLog(REDIS_NOTICE, "Background saving terminated with success"); - server.dirty = 0; + server.dirty = server.dirty - server.dirty_before_bgsave; server.lastsave = time(NULL); } else if (!bysignal && exitcode != 0) { redisLog(REDIS_WARNING, "Background saving error"); } else { redisLog(REDIS_WARNING, - "Background saving terminated by signal %d", WTERMSIG(statloc)); + "Background saving terminated by signal %d", bysignal); rdbRemoveTempFile(server.bgsavechildpid); } server.bgsavechildpid = -1; + server.bgsavethread = (pthread_t) -1; + server.bgsavethread_state = REDIS_BGSAVE_THREAD_UNACTIVE; /* Possibly there are slaves waiting for a BGSAVE in order to be served * (the first stage of SYNC is a bulk transfer of dump.rdb) */ updateSlavesWaitingBgsave(exitcode == 0 ? REDIS_OK : REDIS_ERR); } + +void saveCommand(redisClient *c) { + if (server.bgsavechildpid != -1 || server.bgsavethread != (pthread_t)-1) { + addReplyError(c,"Background save already in progress"); + return; + } + if (rdbSave(server.dbfilename) == REDIS_OK) { + addReply(c,shared.ok); + } else { + addReply(c,shared.err); + } +} + +void bgsaveCommand(redisClient *c) { + if (server.bgsavechildpid != -1 || server.bgsavethread != (pthread_t)-1) { + addReplyError(c,"Background save already in progress"); + } else if (server.bgrewritechildpid != -1) { + addReplyError(c,"Can't BGSAVE while AOF log rewriting is in progress"); + } else if (rdbSaveBackground(server.dbfilename) == REDIS_OK) { + addReplyStatus(c,"Background saving started"); + } else { + addReply(c,shared.err); + } +}