X-Git-Url: https://git.saurik.com/redis.git/blobdiff_plain/774e30478070d780f739db047e5551690a090db1..bb32ede52e8e222735d12d7b350532a06b648c8d:/redis.c diff --git a/redis.c b/redis.c index f9161615..3d225ba9 100644 --- a/redis.c +++ b/redis.c @@ -89,6 +89,7 @@ #define REDIS_HASH 3 /* Object types only used for dumping to disk */ +#define REDIS_EXPIRETIME 253 #define REDIS_SELECTDB 254 #define REDIS_EOF 255 @@ -288,6 +289,8 @@ static int removeExpire(redisDb *db, robj *key); static int expireIfNeeded(redisDb *db, robj *key); static int deleteIfVolatile(redisDb *db, robj *key); static int deleteKey(redisDb *db, robj *key); +static time_t getExpire(redisDb *db, robj *key); +static int setExpire(redisDb *db, robj *key, time_t when); static void authCommand(redisClient *c); static void pingCommand(redisClient *c); @@ -1585,6 +1588,12 @@ static int rdbSaveType(FILE *fp, unsigned char type) { return 0; } +static int rdbSaveTime(FILE *fp, time_t t) { + int32_t t32 = (int32_t) t; + if (fwrite(&t32,4,1,fp) == 0) return -1; + return 0; +} + /* check rdbLoadLen() comments for more info */ static int rdbSaveLen(FILE *fp, uint32_t len) { unsigned char buf[2]; @@ -1657,7 +1666,7 @@ static int rdbSaveLzfStringObject(FILE *fp, robj *obj) { if ((out = zmalloc(outlen)) == NULL) return 0; comprlen = lzf_compress(obj->ptr, sdslen(obj->ptr), out, outlen); if (comprlen == 0) { - free(out); + zfree(out); return 0; } /* Data compressed! Let's save it on disk */ @@ -1666,11 +1675,11 @@ static int rdbSaveLzfStringObject(FILE *fp, robj *obj) { if (rdbSaveLen(fp,comprlen) == -1) goto writeerr; if (rdbSaveLen(fp,sdslen(obj->ptr)) == -1) goto writeerr; if (fwrite(out,comprlen,1,fp) == 0) goto writeerr; - free(out); + zfree(out); return comprlen; writeerr: - free(out); + zfree(out); return -1; } @@ -1690,7 +1699,7 @@ static int rdbSaveStringObject(FILE *fp, robj *obj) { } /* Try LZF compression - under 20 bytes it's unable to compress even - * aaaaaaaaaaaaaaaaaa so to try is just useful to make the CPU hot */ + * aaaaaaaaaaaaaaaaaa so skip it */ if (len > 20) { int retval; @@ -1713,6 +1722,7 @@ static int rdbSave(char *filename) { FILE *fp; char tmpfile[256]; int j; + time_t now = time(NULL); snprintf(tmpfile,256,"temp-%d.%ld.rdb",(int)time(NULL),(long int)random()); fp = fopen(tmpfile,"w"); @@ -1722,7 +1732,8 @@ static int rdbSave(char *filename) { } if (fwrite("REDIS0001",9,1,fp) == 0) goto werr; for (j = 0; j < server.dbnum; j++) { - dict *d = server.db[j].dict; + redisDb *db = server.db+j; + dict *d = db->dict; if (dictSize(d) == 0) continue; di = dictGetIterator(d); if (!di) { @@ -1738,7 +1749,16 @@ static int rdbSave(char *filename) { while((de = dictNext(di)) != NULL) { robj *key = dictGetEntryKey(de); robj *o = dictGetEntryVal(de); - + time_t 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 */ if (rdbSaveType(fp,o->type) == -1) goto werr; if (rdbSaveStringObject(fp,key) == -1) goto werr; if (o->type == REDIS_STRING) { @@ -1831,6 +1851,12 @@ static int rdbLoadType(FILE *fp) { return type; } +static time_t rdbLoadTime(FILE *fp) { + int32_t t32; + if (fread(&t32,4,1,fp) == 0) return -1; + return (time_t) t32; +} + /* Load an encoded length from the DB, see the REDIS_RDB_* defines on the top * of this file for a description of how this are stored on disk. * @@ -1892,6 +1918,24 @@ static robj *rdbLoadIntegerObject(FILE *fp, int enctype) { return createObject(REDIS_STRING,sdscatprintf(sdsempty(),"%lld",val)); } +static robj *rdbLoadLzfStringObject(FILE*fp, int rdbver) { + unsigned int len, clen; + unsigned char *c = NULL; + sds val = NULL; + + if ((clen = rdbLoadLen(fp,rdbver,NULL)) == REDIS_RDB_LENERR) return NULL; + if ((len = rdbLoadLen(fp,rdbver,NULL)) == REDIS_RDB_LENERR) return NULL; + if ((c = zmalloc(clen)) == NULL) goto err; + if ((val = sdsnewlen(NULL,len)) == NULL) goto err; + if (fread(c,clen,1,fp) == 0) goto err; + if (lzf_decompress(c,clen,val,len) == 0) goto err; + return createObject(REDIS_STRING,val); +err: + zfree(c); + sdsfree(val); + return NULL; +} + static robj *rdbLoadStringObject(FILE*fp, int rdbver) { int isencoded; uint32_t len; @@ -1904,6 +1948,8 @@ static robj *rdbLoadStringObject(FILE*fp, int rdbver) { case REDIS_RDB_ENC_INT16: case REDIS_RDB_ENC_INT32: return tryObjectSharing(rdbLoadIntegerObject(fp,len)); + case REDIS_RDB_ENC_LZF: + return tryObjectSharing(rdbLoadLzfStringObject(fp,rdbver)); default: assert(0!=0); } @@ -1922,11 +1968,12 @@ static int rdbLoad(char *filename) { FILE *fp; robj *keyobj = NULL; uint32_t dbid; - int type; - int retval; + int type, retval, rdbver; dict *d = server.db[0].dict; + redisDb *db = server.db+0; char buf[1024]; - int rdbver; + time_t expiretime = -1, now = time(NULL); + fp = fopen(filename,"r"); if (!fp) return REDIS_ERR; if (fread(buf,9,1,fp) == 0) goto eoferr; @@ -1947,6 +1994,11 @@ static int rdbLoad(char *filename) { /* Read type. */ if ((type = rdbLoadType(fp)) == -1) goto eoferr; + if (type == REDIS_EXPIRETIME) { + if ((expiretime = rdbLoadTime(fp)) == -1) goto eoferr; + /* We read the time so we need to read the object type again */ + if ((type = rdbLoadType(fp)) == -1) goto eoferr; + } if (type == REDIS_EOF) break; /* Handle SELECT DB opcode as a special case */ if (type == REDIS_SELECTDB) { @@ -1956,7 +2008,8 @@ static int rdbLoad(char *filename) { redisLog(REDIS_WARNING,"FATAL: Data file was created with a Redis server configured to handle more than %d databases. Exiting\n", server.dbnum); exit(1); } - d = server.db[dbid].dict; + db = server.db+dbid; + d = db->dict; continue; } /* Read key */ @@ -1994,6 +2047,13 @@ static int rdbLoad(char *filename) { redisLog(REDIS_WARNING,"Loading DB, duplicated key (%s) found! Unrecoverable error, exiting now.", keyobj->ptr); exit(1); } + /* Set the expire time if needed */ + if (expiretime != -1) { + setExpire(db,keyobj,expiretime); + /* Delete this key if already expired */ + if (expiretime < now) deleteKey(db,keyobj); + expiretime = -1; + } keyobj = o = NULL; } fclose(fp); @@ -2009,7 +2069,7 @@ eoferr: /* unexpected end of file is handled here with a fatal exit */ /*================================== Commands =============================== */ static void authCommand(redisClient *c) { - if (!strcmp(c->argv[1]->ptr, server.requirepass)) { + if (!server.requirepass || !strcmp(c->argv[1]->ptr, server.requirepass)) { c->authenticated = 1; addReply(c,shared.ok); } else { @@ -2791,6 +2851,7 @@ static void sinterGenericCommand(redisClient *c, robj **setskeys, int setsnum, r deleteKey(c->db,dstkey); dictAdd(c->db->dict,dstkey,dstset); incrRefCount(dstkey); + server.dirty++; } /* Iterate all the elements of the first (smallest) set, and test @@ -2815,6 +2876,7 @@ static void sinterGenericCommand(redisClient *c, robj **setskeys, int setsnum, r } else { dictAdd(dstset->ptr,ele,NULL); incrRefCount(ele); + server.dirty++; } } dictReleaseIterator(di); @@ -2837,12 +2899,14 @@ static void sinterstoreCommand(redisClient *c) { static void flushdbCommand(redisClient *c) { dictEmpty(c->db->dict); dictEmpty(c->db->expires); + server.dirty++; addReply(c,shared.ok); rdbSave(server.dbfilename); } static void flushallCommand(redisClient *c) { emptyDb(); + server.dirty++; addReply(c,shared.ok); rdbSave(server.dbfilename); } @@ -3184,6 +3248,18 @@ static int setExpire(redisDb *db, robj *key, time_t when) { } } +/* Return the expire time of the specified key, or -1 if no expire + * is associated with this key (i.e. the key is non volatile) */ +static time_t getExpire(redisDb *db, robj *key) { + dictEntry *de; + + /* No expire? return ASAP */ + if (dictSize(db->expires) == 0 || + (de = dictFind(db->expires,key)) == NULL) return -1; + + return (time_t) dictGetEntryVal(de); +} + static int expireIfNeeded(redisDb *db, robj *key) { time_t when; dictEntry *de; @@ -3209,6 +3285,7 @@ static int deleteIfVolatile(redisDb *db, robj *key) { (de = dictFind(db->expires,key)) == NULL) return 0; /* Delete the key */ + server.dirty++; dictDelete(db->expires,key); return dictDelete(db->dict,key) == DICT_OK; }