X-Git-Url: https://git.saurik.com/redis.git/blobdiff_plain/cea8c5cd75b88cd5a13d9b253d792cc045d28b62..26117e84f01022b261452d6cae54616d08a31074:/src/rdb.c?ds=inline diff --git a/src/rdb.c b/src/rdb.c index acec829a..fcf97d33 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; @@ -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,17 +294,10 @@ 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"); } @@ -347,20 +323,10 @@ int rdbSaveObject(FILE *fp, robj *o) { } 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; @@ -395,6 +361,36 @@ off_t rdbSavedObjectLen(robj *o) { return len; } +/* 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; + /* 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 */ int rdbSave(char *filename) { dictIterator *di = NULL; @@ -404,13 +400,16 @@ int rdbSave(char *filename) { int j; time_t now = time(NULL); - /* FIXME: implement .rdb save for disk store properly */ - redisAssert(server.ds_enabled == 0); + 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; @@ -432,22 +431,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 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; + expire = getExpire(db,&key); + if (rdbSaveKeyValuePair(fp,&key,o,expire,now) == -1) goto werr; } dictReleaseIterator(di); } @@ -482,18 +470,24 @@ werr: int rdbSaveBackground(char *filename) { pid_t childpid; - if (server.bgsavechildpid != -1) return REDIS_ERR; - redisAssert(server.ds_enabled == 0); + if (server.bgsavechildpid != -1 || + server.bgsavethread != (pthread_t) -1) return REDIS_ERR; + server.dirty_before_bgsave = server.dirty; + + if (server.ds_enabled) { + cacheForcePointInTime(); + return dsRdbSaveBackground(filename); + } + if ((childpid = fork()) == 0) { + int retval; + /* Child */ if (server.ipfd > 0) close(server.ipfd); if (server.sofd > 0) close(server.sofd); - if (rdbSave(filename) == REDIS_OK) { - _exit(0); - } else { - _exit(1); - } + retval = rdbSave(filename); + _exit((retval == REDIS_OK) ? 0 : 1); } else { /* Parent */ if (childpid == -1) { @@ -813,6 +807,47 @@ 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) + { + 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; + default: + redisPanic("Unknown enoding"); + break; + } } else { redisPanic("Unknown object type"); } @@ -931,10 +966,7 @@ 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"); @@ -944,11 +976,37 @@ void backgroundSaveDoneHandler(int statloc) { 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"); + return; + } + if (rdbSaveBackground(server.dbfilename) == REDIS_OK) { + addReplyStatus(c,"Background saving started"); + } else { + addReply(c,shared.err); + } +}