]> git.saurik.com Git - redis.git/blobdiff - redis.c
Ruby client updated
[redis.git] / redis.c
diff --git a/redis.c b/redis.c
index d7b128f66d93eb5ba64a6c1d4299829ea00734ab..05a623d56086ae2d2ef7e30d3e9c9fb932b0442d 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_HASH 3
 
 /* Object types only used for dumping to disk */
+#define REDIS_EXPIRETIME 253
 #define REDIS_SELECTDB 254
 #define REDIS_EOF 255
 
 #define REDIS_SELECTDB 254
 #define REDIS_EOF 255
 
 
 /* A redis object, that is a type able to hold a string / list / set */
 typedef struct redisObject {
 
 /* A redis object, that is a type able to hold a string / list / set */
 typedef struct redisObject {
-    int type;
     void *ptr;
     void *ptr;
+    int type;
     int refcount;
 } robj;
 
     int refcount;
 } robj;
 
@@ -204,7 +205,7 @@ struct redisServer {
     int cronloops;              /* number of times the cron function run */
     list *objfreelist;          /* A list of freed objects to avoid malloc() */
     time_t lastsave;            /* Unix time of last save succeeede */
     int cronloops;              /* number of times the cron function run */
     list *objfreelist;          /* A list of freed objects to avoid malloc() */
     time_t lastsave;            /* Unix time of last save succeeede */
-    int usedmemory;             /* Used memory in megabytes */
+    size_t usedmemory;             /* Used memory in megabytes */
     /* Fields used only for stats */
     time_t stat_starttime;         /* server start time */
     long long stat_numcommands;    /* number of processed commands */
     /* Fields used only for stats */
     time_t stat_starttime;         /* server start time */
     long long stat_numcommands;    /* number of processed commands */
@@ -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 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);
 
 static void authCommand(redisClient *c);
 static void pingCommand(redisClient *c);
@@ -661,7 +664,7 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
 
     /* Show information about connected clients */
     if (!(loops % 5)) {
 
     /* Show information about connected clients */
     if (!(loops % 5)) {
-        redisLog(REDIS_DEBUG,"%d clients connected (%d slaves), %d bytes in use",
+        redisLog(REDIS_DEBUG,"%d clients connected (%d slaves), %zu bytes in use",
             listLength(server.clients)-listLength(server.slaves),
             listLength(server.slaves),
             server.usedmemory,
             listLength(server.clients)-listLength(server.slaves),
             listLength(server.slaves),
             server.usedmemory,
@@ -1585,6 +1588,12 @@ static int rdbSaveType(FILE *fp, unsigned char type) {
     return 0;
 }
 
     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];
 /* check rdbLoadLen() comments for more info */
 static int rdbSaveLen(FILE *fp, uint32_t len) {
     unsigned char buf[2];
@@ -1713,6 +1722,7 @@ static int rdbSave(char *filename) {
     FILE *fp;
     char tmpfile[256];
     int j;
     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");
 
     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++) {
     }
     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) {
         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);
         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) {
             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;
 }
 
     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.
  *
 /* 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.
  *
@@ -1942,11 +1968,12 @@ static int rdbLoad(char *filename) {
     FILE *fp;
     robj *keyobj = NULL;
     uint32_t dbid;
     FILE *fp;
     robj *keyobj = NULL;
     uint32_t dbid;
-    int type;
-    int retval;
+    int type, retval, rdbver;
     dict *d = server.db[0].dict;
     dict *d = server.db[0].dict;
+    redisDb *db = server.db+0;
     char buf[1024];
     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;
     fp = fopen(filename,"r");
     if (!fp) return REDIS_ERR;
     if (fread(buf,9,1,fp) == 0) goto eoferr;
@@ -1967,6 +1994,11 @@ static int rdbLoad(char *filename) {
 
         /* Read type. */
         if ((type = rdbLoadType(fp)) == -1) goto eoferr;
 
         /* 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) {
         if (type == REDIS_EOF) break;
         /* Handle SELECT DB opcode as a special case */
         if (type == REDIS_SELECTDB) {
@@ -1976,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);
             }
                 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 */
             continue;
         }
         /* Read key */
@@ -2014,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);
         }
             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);
         keyobj = o = NULL;
     }
     fclose(fp);
@@ -2197,9 +2237,10 @@ static void randomkeyCommand(redisClient *c) {
    
     while(1) {
         de = dictGetRandomKey(c->db->dict);
    
     while(1) {
         de = dictGetRandomKey(c->db->dict);
-        if (expireIfNeeded(c->db,dictGetEntryKey(de)) == 0) break;
+        if (!de || expireIfNeeded(c->db,dictGetEntryKey(de)) == 0) break;
     }
     if (de == NULL) {
     }
     if (de == NULL) {
+        addReply(c,shared.plus);
         addReply(c,shared.crlf);
     } else {
         addReply(c,shared.plus);
         addReply(c,shared.crlf);
     } else {
         addReply(c,shared.plus);
@@ -2270,6 +2311,10 @@ static void typeCommand(redisClient *c) {
 }
 
 static void saveCommand(redisClient *c) {
 }
 
 static void saveCommand(redisClient *c) {
+    if (server.bgsaveinprogress) {
+        addReplySds(c,sdsnew("-ERR background save in progress\r\n"));
+        return;
+    }
     if (rdbSave(server.dbfilename) == REDIS_OK) {
         addReply(c,shared.ok);
     } else {
     if (rdbSave(server.dbfilename) == REDIS_OK) {
         addReply(c,shared.ok);
     } else {
@@ -3157,7 +3202,7 @@ static void infoCommand(redisClient *c) {
         "redis_version:%s\r\n"
         "connected_clients:%d\r\n"
         "connected_slaves:%d\r\n"
         "redis_version:%s\r\n"
         "connected_clients:%d\r\n"
         "connected_slaves:%d\r\n"
-        "used_memory:%d\r\n"
+        "used_memory:%zu\r\n"
         "changes_since_last_save:%lld\r\n"
         "last_save_time:%d\r\n"
         "total_connections_received:%lld\r\n"
         "changes_since_last_save:%lld\r\n"
         "last_save_time:%d\r\n"
         "total_connections_received:%lld\r\n"
@@ -3208,6 +3253,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;
 static int expireIfNeeded(redisDb *db, robj *key) {
     time_t when;
     dictEntry *de;