diff --git a/dict.h b/dict.h index 90d46f488d..bd935d5f4e 100644 --- a/dict.h +++ b/dict.h @@ -73,7 +73,7 @@ typedef struct dictIterator { } dictIterator; /* This is the initial size of every hash table */ -#define DICT_HT_INITIAL_SIZE 16 +#define DICT_HT_INITIAL_SIZE 4 /* ------------------------------- Macros ------------------------------------*/ #define dictFreeEntryVal(ht, entry) \ diff --git a/doc/Credits.html b/doc/Credits.html index e4e35fa432..fefc444003 100644 --- a/doc/Credits.html +++ b/doc/Credits.html @@ -26,7 +26,7 @@
Return the members of a set resulting from the intersection of all thesets hold at the specified keys. Like in LRANGE the result is sent tothe client as a multi-bulk reply (see the protocol specification formore information). If just a single key is specified, then this commandproduces the same result as SELEMENTS. Actually SELEMENTS is just syntaxsugar for SINTERSECT.+Time complexity O(NM) worst case where N is the cardinality of the smallest set and M the number of sets_
Return the members of a set resulting from the intersection of all thesets hold at the specified keys. Like in LRANGE the result is sent tothe client as a multi-bulk reply (see the protocol specification formore information). If just a single key is specified, then this commandproduces the same result as SMEMBERS. Actually SMEMBERS is just syntaxsugar for SINTERSECT.
Non existing keys are considered like empty sets, so if one of the keys ismissing an empty set is returned (since the intersection with an emptyset always is an empty set).
* SADD* SREM* SISMEMBER* SCARD* SMEMBERS* SINTERSTORE* SUNION* SUNIONSTORE* SMOVEdiff --git a/redis-cli.c b/redis-cli.c index 343ee0a144..9acf92dc2f 100644 --- a/redis-cli.c +++ b/redis-cli.c @@ -79,6 +79,7 @@ static struct redisCommand cmdTable[] = { {"smove",4,REDIS_CMD_BULK}, {"sismember",3,REDIS_CMD_BULK}, {"scard",2,REDIS_CMD_INLINE}, + {"spop",2,REDIS_CMD_INLINE}, {"sinter",-2,REDIS_CMD_INLINE}, {"sinterstore",-3,REDIS_CMD_INLINE}, {"sunion",-2,REDIS_CMD_INLINE}, diff --git a/redis.c b/redis.c index 2caf06c34e..fa653d21e9 100644 --- a/redis.c +++ b/redis.c @@ -85,7 +85,6 @@ /* Hash table parameters */ #define REDIS_HT_MINFILL 10 /* Minimal hash table fill 10% */ -#define REDIS_HT_MINSLOTS 16384 /* Never resize the HT under this */ /* Command flags */ #define REDIS_CMD_BULK 1 /* Bulk write command */ @@ -370,6 +369,7 @@ static void sremCommand(redisClient *c); static void smoveCommand(redisClient *c); static void sismemberCommand(redisClient *c); static void scardCommand(redisClient *c); +static void spopCommand(redisClient *c); static void sinterCommand(redisClient *c); static void sinterstoreCommand(redisClient *c); static void sunionCommand(redisClient *c); @@ -417,6 +417,7 @@ static struct redisCommand cmdTable[] = { {"smove",smoveCommand,4,REDIS_CMD_BULK}, {"sismember",sismemberCommand,3,REDIS_CMD_BULK}, {"scard",scardCommand,2,REDIS_CMD_INLINE}, + {"spop",spopCommand,2,REDIS_CMD_INLINE}, {"sinter",sinterCommand,-2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM}, {"sinterstore",sinterstoreCommand,-3,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM}, {"sunion",sunionCommand,-2,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM}, @@ -691,22 +692,28 @@ static void closeTimedoutClients(void) { } } +static int htNeedsResize(dict *dict) { + long long size, used; + + size = dictSlots(dict); + used = dictSize(dict); + return (size && used && size > DICT_HT_INITIAL_SIZE && + (used*100/size < REDIS_HT_MINFILL)); +} + /* If the percentage of used slots in the HT reaches REDIS_HT_MINFILL * we resize the hash table to save memory */ static void tryResizeHashTables(void) { int j; for (j = 0; j < server.dbnum; j++) { - long long size, used; - - size = dictSlots(server.db[j].dict); - used = dictSize(server.db[j].dict); - if (size && used && size > REDIS_HT_MINSLOTS && - (used*100/size < REDIS_HT_MINFILL)) { - redisLog(REDIS_NOTICE,"The hash table %d is too sparse, resize it...",j); + if (htNeedsResize(server.db[j].dict)) { + redisLog(REDIS_DEBUG,"The hash table %d is too sparse, resize it...",j); dictResize(server.db[j].dict); - redisLog(REDIS_NOTICE,"Hash table %d resized.",j); + redisLog(REDIS_DEBUG,"Hash table %d resized.",j); } + if (htNeedsResize(server.db[j].expires)) + dictResize(server.db[j].expires); } } @@ -2961,6 +2968,7 @@ static void sremCommand(redisClient *c) { } if (dictDelete(set->ptr,c->argv[2]) == DICT_OK) { server.dirty++; + if (htNeedsResize(set->ptr)) dictResize(set->ptr); addReply(c,shared.cone); } else { addReply(c,shared.czero); @@ -3040,6 +3048,34 @@ static void scardCommand(redisClient *c) { } } +static void spopCommand(redisClient *c) { + robj *set; + dictEntry *de; + + set = lookupKeyWrite(c->db,c->argv[1]); + if (set == NULL) { + addReply(c,shared.nullbulk); + } else { + if (set->type != REDIS_SET) { + addReply(c,shared.wrongtypeerr); + return; + } + de = dictGetRandomKey(set->ptr); + if (de == NULL) { + addReply(c,shared.nullbulk); + } else { + robj *ele = dictGetEntryKey(de); + + addReplySds(c,sdscatprintf(sdsempty(),"$%d\r\n",sdslen(ele->ptr))); + addReply(c,ele); + addReply(c,shared.crlf); + dictDelete(set->ptr,ele); + if (htNeedsResize(set->ptr)) dictResize(set->ptr); + server.dirty++; + } + } +} + static int qsortCompareSetsByCardinality(const void *s1, const void *s2) { dict **d1 = (void*) s1, **d2 = (void*) s2; @@ -4170,6 +4206,7 @@ static struct redisFunctionSym symsTable[] = { {"smoveCommand", (unsigned long)smoveCommand}, {"sismemberCommand", (unsigned long)sismemberCommand}, {"scardCommand", (unsigned long)scardCommand}, +{"spopCommand", (unsigned long)spopCommand}, {"sinterCommand", (unsigned long)sinterCommand}, {"sinterstoreCommand", (unsigned long)sinterstoreCommand}, {"sunionCommand", (unsigned long)sunionCommand}, @@ -4296,6 +4333,9 @@ static void setupSigSegvAction(void) { act.sa_sigaction = segvHandler; sigaction (SIGSEGV, &act, NULL); sigaction (SIGBUS, &act, NULL); + sigaction (SIGFPE, &act, NULL); + sigaction (SIGILL, &act, NULL); + sigaction (SIGBUS, &act, NULL); return; } #else /* HAVE_BACKTRACE */ diff --git a/test-redis.tcl b/test-redis.tcl index bd58cb2e71..f5a03161ab 100644 --- a/test-redis.tcl +++ b/test-redis.tcl @@ -515,6 +515,14 @@ proc main {server port} { lsort [$r smembers sres] } {1 2 3 4} + test {SPOP basics} { + $r del myset + $r sadd myset 1 + $r sadd myset 2 + $r sadd myset 3 + list [lsort [list [$r spop myset] [$r spop myset] [$r spop myset]]] [$r scard myset] + } {{1 2 3} 0} + test {SAVE - make sure there are all the types as values} { $r lpush mysavelist hello $r lpush mysavelist world