]> git.saurik.com Git - redis.git/blobdiff - redis.c
the Cron timer function is now called 10 times per second instead of 1 time per secon...
[redis.git] / redis.c
diff --git a/redis.c b/redis.c
index e0486dc85a6eaf0a125467ccbf0099de9d654e60..6b16d85781fbcdb23ac82e7fc7568022c2cb6fcf 100644 (file)
--- a/redis.c
+++ b/redis.c
@@ -91,7 +91,7 @@
 #define REDIS_CONFIGLINE_MAX    1024
 #define REDIS_OBJFREELIST_MAX   1000000 /* Max number of objects to cache */
 #define REDIS_MAX_SYNC_TIME     60      /* Slave can't take more to sync */
-#define REDIS_EXPIRELOOKUPS_PER_CRON    100 /* try to expire 100 keys/second */
+#define REDIS_EXPIRELOOKUPS_PER_CRON    10 /* try to expire 10 keys/loop */
 #define REDIS_MAX_WRITE_PER_EVENT (1024*64)
 #define REDIS_REQUEST_MAX_SIZE (1024*1024*256) /* max bytes in inline command */
 
@@ -799,6 +799,8 @@ static struct redisCommand cmdTable[] = {
     {NULL,NULL,0,0,NULL,0,0,0}
 };
 
+static void usage();
+
 /*============================ Utility functions ============================ */
 
 /* Glob-style pattern matching. */
@@ -1271,7 +1273,7 @@ static int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientD
         size = dictSlots(server.db[j].dict);
         used = dictSize(server.db[j].dict);
         vkeys = dictSize(server.db[j].expires);
-        if (!(loops % 5) && (used || vkeys)) {
+        if (!(loops % 50) && (used || vkeys)) {
             redisLog(REDIS_VERBOSE,"DB %d: %lld keys (%lld volatile) in %lld slots HT.",j,used,vkeys,size);
             /* dictPrintStats(server.dict); */
         }
@@ -1283,10 +1285,10 @@ static int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientD
      * if we resize the HT while there is the saving child at work actually
      * a lot of memory movements in the parent will cause a lot of pages
      * copied. */
-    if (server.bgsavechildpid == -1) tryResizeHashTables();
+    if (server.bgsavechildpid == -1 && !(loops % 10)) tryResizeHashTables();
 
     /* Show information about connected clients */
-    if (!(loops % 5)) {
+    if (!(loops % 50)) {
         redisLog(REDIS_VERBOSE,"%d clients connected (%d slaves), %zu bytes in use, %d shared objects",
             listLength(server.clients)-listLength(server.slaves),
             listLength(server.slaves),
@@ -1295,7 +1297,7 @@ static int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientD
     }
 
     /* Close connections of timedout clients */
-    if ((server.maxidletime && !(loops % 10)) || server.blpop_blocked_clients)
+    if ((server.maxidletime && !(loops % 100)) || server.blpop_blocked_clients)
         closeTimedoutClients();
 
     /* Check if a background saving or AOF rewrite in progress terminated */
@@ -1370,7 +1372,7 @@ static int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientD
             retval = (server.vm_max_threads == 0) ?
                         vmSwapOneObjectBlocking() :
                         vmSwapOneObjectThreaded();
-            if (retval == REDIS_ERR && (loops % 30) == 0 &&
+            if (retval == REDIS_ERR && !(loops % 300) &&
                 zmalloc_used_memory() >
                 (server.vm_max_memory+server.vm_max_memory/10))
             {
@@ -1385,13 +1387,13 @@ static int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientD
     }
 
     /* Check if we should connect to a MASTER */
-    if (server.replstate == REDIS_REPL_CONNECT) {
+    if (server.replstate == REDIS_REPL_CONNECT && !(loops % 10)) {
         redisLog(REDIS_NOTICE,"Connecting to MASTER...");
         if (syncWithMaster() == REDIS_OK) {
             redisLog(REDIS_NOTICE,"MASTER <-> SLAVE sync succeeded");
         }
     }
-    return 1000;
+    return 100;
 }
 
 /* This function gets called every time Redis is entering the
@@ -1616,12 +1618,15 @@ static void loadServerConfig(char *filename) {
     char buf[REDIS_CONFIGLINE_MAX+1], *err = NULL;
     int linenum = 0;
     sds line = NULL;
+    char *errormsg = "Fatal error, can't open config file '%s'";
+    char *errorbuf = zmalloc(sizeof(char)*(strlen(errormsg)+strlen(filename)));
+    sprintf(errorbuf, errormsg, filename);
 
     if (filename[0] == '-' && filename[1] == '\0')
         fp = stdin;
     else {
         if ((fp = fopen(filename,"r")) == NULL) {
-            redisLog(REDIS_WARNING,"Fatal error, can't open config file");
+            redisLog(REDIS_WARNING, errorbuf);
             exit(1);
         }
     }
@@ -4368,6 +4373,7 @@ static void popGenericCommand(redisClient *c, int where) {
         robj *ele = listNodeValue(ln);
         addReplyBulk(c,ele);
         listDelNode(list,ln);
+        if (listLength(list) == 0) deleteKey(c->db,c->argv[1]);
         server.dirty++;
     }
 }
@@ -4460,6 +4466,7 @@ static void ltrimCommand(redisClient *c) {
         ln = listLast(list);
         listDelNode(list,ln);
     }
+    if (listLength(list) == 0) deleteKey(c->db,c->argv[1]);
     server.dirty++;
     addReply(c,shared.ok);
 }
@@ -4493,6 +4500,7 @@ static void lremCommand(redisClient *c) {
         }
         ln = next;
     }
+    if (listLength(list) == 0) deleteKey(c->db,c->argv[1]);
     addReplySds(c,sdscatprintf(sdsempty(),":%d\r\n",removed));
 }
 
@@ -4552,6 +4560,7 @@ static void rpoplpushcommand(redisClient *c) {
 
         /* Finally remove the element from the source list */
         listDelNode(srclist,ln);
+        if (listLength(srclist) == 0) deleteKey(c->db,c->argv[1]);
         server.dirty++;
     }
 }
@@ -4590,6 +4599,7 @@ static void sremCommand(redisClient *c) {
     if (dictDelete(set->ptr,c->argv[2]) == DICT_OK) {
         server.dirty++;
         if (htNeedsResize(set->ptr)) dictResize(set->ptr);
+        if (dictSize((dict*)set->ptr) == 0) deleteKey(c->db,c->argv[1]);
         addReply(c,shared.cone);
     } else {
         addReply(c,shared.czero);
@@ -4619,6 +4629,8 @@ static void smoveCommand(redisClient *c) {
         addReply(c,shared.czero);
         return;
     }
+    if (dictSize((dict*)srcset->ptr) == 0 && srcset != dstset)
+        deleteKey(c->db,c->argv[1]);
     server.dirty++;
     /* Add the element to the destination set */
     if (!dstset) {
@@ -4670,6 +4682,7 @@ static void spopCommand(redisClient *c) {
         addReplyBulk(c,ele);
         dictDelete(set->ptr,ele);
         if (htNeedsResize(set->ptr)) dictResize(set->ptr);
+        if (dictSize((dict*)set->ptr) == 0) deleteKey(c->db,c->argv[1]);
         server.dirty++;
     }
 }
@@ -4771,18 +4784,20 @@ static void sinterGenericCommand(redisClient *c, robj **setskeys, unsigned long
     dictReleaseIterator(di);
 
     if (dstkey) {
-        /* Store the resulting set into the target */
+        /* Store the resulting set into the target, if the intersection
+         * is not an empty set. */
         deleteKey(c->db,dstkey);
-        dictAdd(c->db->dict,dstkey,dstset);
-        incrRefCount(dstkey);
-    }
-
-    if (!dstkey) {
-        lenobj->ptr = sdscatprintf(sdsempty(),"*%lu\r\n",cardinality);
-    } else {
-        addReplySds(c,sdscatprintf(sdsempty(),":%lu\r\n",
-            dictSize((dict*)dstset->ptr)));
+        if (dictSize((dict*)dstset->ptr) > 0) {
+            dictAdd(c->db->dict,dstkey,dstset);
+            incrRefCount(dstkey);
+            addReplyLong(c,dictSize((dict*)dstset->ptr));
+        } else {
+            decrRefCount(dstset);
+            addReply(c,shared.czero);
+        }
         server.dirty++;
+    } else {
+        lenobj->ptr = sdscatprintf(sdsempty(),"*%lu\r\n",cardinality);
     }
     zfree(dv);
 }
@@ -4855,7 +4870,8 @@ static void sunionDiffGenericCommand(redisClient *c, robj **setskeys, int setsnu
         }
         dictReleaseIterator(di);
 
-        if (op == REDIS_OP_DIFF && cardinality == 0) break; /* result set is empty */
+        /* result set is empty? Exit asap. */
+        if (op == REDIS_OP_DIFF && cardinality == 0) break;
     }
 
     /* Output the content of the resulting set, if not in STORE mode */
@@ -4869,20 +4885,19 @@ static void sunionDiffGenericCommand(redisClient *c, robj **setskeys, int setsnu
             addReplyBulk(c,ele);
         }
         dictReleaseIterator(di);
+        decrRefCount(dstset);
     } else {
         /* If we have a target key where to store the resulting set
          * create this key with the result set inside */
         deleteKey(c->db,dstkey);
-        dictAdd(c->db->dict,dstkey,dstset);
-        incrRefCount(dstkey);
-    }
-
-    /* Cleanup */
-    if (!dstkey) {
-        decrRefCount(dstset);
-    } else {
-        addReplySds(c,sdscatprintf(sdsempty(),":%lu\r\n",
-            dictSize((dict*)dstset->ptr)));
+        if (dictSize((dict*)dstset->ptr) > 0) {
+            dictAdd(c->db->dict,dstkey,dstset);
+            incrRefCount(dstkey);
+            addReplyLong(c,dictSize((dict*)dstset->ptr));
+        } else {
+            decrRefCount(dstset);
+            addReply(c,shared.czero);
+        }
         server.dirty++;
     }
     zfree(dv);
@@ -5334,6 +5349,7 @@ static void zremCommand(redisClient *c) {
     /* Delete from the hash table */
     dictDelete(zs->dict,c->argv[2]);
     if (htNeedsResize(zs->dict)) dictResize(zs->dict);
+    if (dictSize(zs->dict) == 0) deleteKey(c->db,c->argv[1]);
     server.dirty++;
     addReply(c,shared.cone);
 }
@@ -5351,6 +5367,7 @@ static void zremrangebyscoreCommand(redisClient *c) {
     zs = zsetobj->ptr;
     deleted = zslDeleteRangeByScore(zs->zsl,min,max,zs->dict);
     if (htNeedsResize(zs->dict)) dictResize(zs->dict);
+    if (dictSize(zs->dict) == 0) deleteKey(c->db,c->argv[1]);
     server.dirty += deleted;
     addReplyLong(c,deleted);
 }
@@ -5385,6 +5402,7 @@ static void zremrangebyrankCommand(redisClient *c) {
      * use 1-based rank */
     deleted = zslDeleteRangeByRank(zs->zsl,start+1,end+1,zs->dict);
     if (htNeedsResize(zs->dict)) dictResize(zs->dict);
+    if (dictSize(zs->dict) == 0) deleteKey(c->db,c->argv[1]);
     server.dirty += deleted;
     addReplyLong(c, deleted);
 }
@@ -5568,11 +5586,15 @@ static void zunionInterGenericCommand(redisClient *c, robj *dstkey, int op) {
     }
 
     deleteKey(c->db,dstkey);
-    dictAdd(c->db->dict,dstkey,dstobj);
-    incrRefCount(dstkey);
-
-    addReplyLong(c, dstzset->zsl->length);
-    server.dirty++;
+    if (dstzset->zsl->length) {
+        dictAdd(c->db->dict,dstkey,dstobj);
+        incrRefCount(dstkey);
+        addReplyLong(c, dstzset->zsl->length);
+        server.dirty++;
+    } else {
+        decrRefCount(dstzset);
+        addReply(c, shared.czero);
+    }
     zfree(src);
 }
 
@@ -5957,8 +5979,12 @@ static void hdelCommand(redisClient *c) {
             (unsigned char*) field->ptr,
             sdslen(field->ptr), &deleted);
         decrRefCount(field);
+        if (zipmapLen((unsigned char*) o->ptr) == 0)
+            deleteKey(c->db,c->argv[1]);
     } else {
         deleted = dictDelete((dict*)o->ptr,c->argv[2]) == DICT_OK;
+        if (htNeedsResize(o->ptr)) dictResize(o->ptr);
+        if (dictSize((dict*)o->ptr) == 0) deleteKey(c->db,c->argv[1]);
     }
     if (deleted) server.dirty++;
     addReply(c,deleted ? shared.cone : shared.czero);
@@ -9165,19 +9191,23 @@ static void version() {
     exit(0);
 }
 
+static void usage() {
+    fprintf(stderr,"Usage: ./redis-server [/path/to/redis.conf]\n");
+    exit(1);
+}
+
 int main(int argc, char **argv) {
     time_t start;
 
     initServerConfig();
     if (argc == 2) {
-        if ((strcmp(argv[1], "-v") == 0) || (strcmp(argv[1], "--version") == 0)) {
-            version();
-        }
+        if (strcmp(argv[1], "-v") == 0 ||
+            strcmp(argv[1], "--version") == 0) version();
+        if (strcmp(argv[1], "--help") == 0) usage();
         resetServerSaveParams();
         loadServerConfig(argv[1]);
-    } else if (argc > 2) {
-        fprintf(stderr,"Usage: ./redis-server [/path/to/redis.conf]\n");
-        exit(1);
+    } else if ((argc > 2)) {
+        usage();
     } else {
         redisLog(REDIS_WARNING,"Warning: no config file specified, using the default config. In order to specify a config file use 'redis-server /path/to/redis.conf'");
     }