X-Git-Url: https://git.saurik.com/redis.git/blobdiff_plain/cc2750670f1cb2fafde3e7d1a340f6674dab1749..55937b7972cb990a6f0a69927f7a28d7baf915d9:/src/rdb.c diff --git a/src/rdb.c b/src/rdb.c index 83fe81e5..a2471aef 100644 --- a/src/rdb.c +++ b/src/rdb.c @@ -139,7 +139,7 @@ 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; @@ -245,7 +245,7 @@ int rdbSaveDoubleValue(FILE *fp, double val) { 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; @@ -256,27 +256,10 @@ int rdbSaveObject(FILE *fp, robj *o) { } 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; + size_t l = ziplistBlobLen((unsigned char*)o->ptr); - if ((n = rdbSaveLen(fp,ziplistLen(o->ptr))) == -1) return -1; + if ((n = rdbSaveRawString(fp,o->ptr,l)) == -1) return -1; nwritten += n; - - p = ziplistIndex(o->ptr,0); - while(ziplistGet(p,&vstr,&vlen,&vlong)) { - if (vstr) { - if ((n = rdbSaveRawString(fp,vstr,vlen)) == -1) - return -1; - nwritten += n; - } else { - if ((n = rdbSaveLongLongAsStringObject(fp,vlong)) == -1) - return -1; - nwritten += n; - } - p = ziplistNext(o->ptr,p); - } } else if (o->encoding == REDIS_ENCODING_LINKEDLIST) { list *list = o->ptr; listIter li; @@ -311,56 +294,48 @@ int rdbSaveObject(FILE *fp, robj *o) { } dictReleaseIterator(di); } else if (o->encoding == REDIS_ENCODING_INTSET) { - intset *is = o->ptr; - int64_t llval; - int i = 0; + size_t l = intsetBlobLen((intset*)o->ptr); - if ((n = rdbSaveLen(fp,intsetLen(is))) == -1) return -1; + if ((n = rdbSaveRawString(fp,o->ptr,l)) == -1) return -1; nwritten += n; - - while(intsetGet(is,i++,&llval)) { - if ((n = rdbSaveLongLongAsStringObject(fp,llval)) == -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; - - 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); + /* Save a sorted set value */ + if (o->encoding == REDIS_ENCODING_ZIPLIST) { + size_t l = ziplistBlobLen((unsigned char*)o->ptr); - if ((n = rdbSaveStringObject(fp,eleobj)) == -1) return -1; + if ((n = rdbSaveRawString(fp,o->ptr,l)) == -1) return -1; nwritten += n; - if ((n = rdbSaveDoubleValue(fp,*score)) == -1) return -1; + } else if (o->encoding == REDIS_ENCODING_SKIPLIST) { + zset *zs = o->ptr; + dictIterator *di = dictGetIterator(zs->dict); + dictEntry *de; + + 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; + size_t l = zipmapBlobLen((unsigned char*)o->ptr); - if ((n = rdbSaveLen(fp,count)) == -1) return -1; + if ((n = rdbSaveRawString(fp,o->ptr,l)) == -1) return -1; nwritten += n; - - while((p = zipmapNext(p,&key,&klen,&val,&vlen)) != NULL) { - if ((n = rdbSaveRawString(fp,key,klen)) == -1) return -1; - nwritten += n; - if ((n = rdbSaveRawString(fp,val,vlen)) == -1) return -1; - nwritten += n; - } } else { dictIterator *di = dictGetIterator(o->ptr); dictEntry *de; @@ -399,12 +374,10 @@ off_t rdbSavedObjectLen(robj *o) { * 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, redisDb *db, robj *key, robj *val, - time_t now) +int rdbSaveKeyValuePair(FILE *fp, robj *key, robj *val, + time_t expiretime, time_t now) { - time_t expiretime; - - expiretime = getExpire(db,key); + int vtype; /* Save the expire time */ if (expiretime != -1) { @@ -413,8 +386,19 @@ int rdbSaveKeyValuePair(FILE *fp, redisDb *db, robj *key, robj *val, 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,val->type) == -1) return -1; + if (rdbSaveType(fp,vtype) == -1) return -1; if (rdbSaveStringObject(fp,key) == -1) return -1; if (rdbSaveObject(fp,val) == -1) return -1; return 1; @@ -441,7 +425,7 @@ int rdbSave(char *filename) { 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; @@ -460,9 +444,11 @@ int rdbSave(char *filename) { while((de = dictNext(di)) != NULL) { sds keystr = dictGetEntryKey(de); robj key, *o = dictGetEntryVal(de); + time_t expire; initStaticStringObject(key,keystr); - if (rdbSaveKeyValuePair(fp,db,&key,o,now) == -1) goto werr; + expire = getExpire(db,&key); + if (rdbSaveKeyValuePair(fp,&key,o,expire,now) == -1) goto werr; } dictReleaseIterator(di); } @@ -496,6 +482,7 @@ werr: int rdbSaveBackground(char *filename) { pid_t childpid; + long long start; if (server.bgsavechildpid != -1 || server.bgsavethread != (pthread_t) -1) return REDIS_ERR; @@ -507,6 +494,7 @@ int rdbSaveBackground(char *filename) { return dsRdbSaveBackground(filename); } + start = ustime(); if ((childpid = fork()) == 0) { int retval; @@ -517,6 +505,7 @@ int rdbSaveBackground(char *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)); @@ -770,11 +759,13 @@ 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; @@ -784,10 +775,21 @@ robj *rdbLoadObject(int type, FILE *fp) { if ((ele = rdbLoadEncodedStringObject(fp)) == NULL) return NULL; ele = tryObjectEncoding(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; @@ -834,6 +836,54 @@ 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"); } @@ -884,7 +934,7 @@ 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; @@ -988,9 +1038,9 @@ void saveCommand(redisClient *c) { void bgsaveCommand(redisClient *c) { if (server.bgsavechildpid != -1 || server.bgsavethread != (pthread_t)-1) { addReplyError(c,"Background save already in progress"); - return; - } - if (rdbSaveBackground(server.dbfilename) == REDIS_OK) { + } 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);