X-Git-Url: https://git.saurik.com/redis.git/blobdiff_plain/5e2022bb484791b1b3099b7ce304f02b1f4a3212..1763929f2379b8f4a5cb380987ff1cdb1f762f51:/redis.c?ds=sidebyside diff --git a/redis.c b/redis.c index e0486dc8..6b16d857 100644 --- 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'"); }