]> git.saurik.com Git - redis.git/blobdiff - redis.c
persistent EXPIRE
[redis.git] / redis.c
diff --git a/redis.c b/redis.c
index f91616157df99fb3528b9141d805bb0b03a838e0..3d225ba93c76315e41b0b8cc1e95b10520a65b91 100644 (file)
--- 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;
 }