Skip to content

Commit

Permalink
SPOP implemented. Hash table resizing for Sets and Expires too. Chang…
Browse files Browse the repository at this point in the history
…ed the resize policy to play better with RANDOMKEY and SPOP.
  • Loading branch information
antirez committed Jun 8, 2009
1 parent d76412d commit 12fea92
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 12 deletions.
2 changes: 1 addition & 1 deletion dict.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down
2 changes: 1 addition & 1 deletion doc/Credits.html
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ <h1 class="wikiname">Credits</h1>
</div>

<div class="narrow">
<h1><a name="Credits">Credits</a></h1><ul><li> The Redis server was designed and written by <a href="http://invece.org" target="_blank">Salvatore Sanfilippo (aka antirez)</a></li><li> <a href="http://brainspl.at/" target="_blank">Ezra Zygmuntowicz (aka ezmobius)</a> - Ruby client lib initial version and hacking</li><li> <a href="http://qix.it" target="_blank">Ludovico Magnocavallo (aka ludo)</a> - Python clinet lib</li><li> <a href="http://www.adroll.com/" target="_blank">Valentino Volonghi of Adroll</a> - Erlang client lib</li><li> <b>brettbender</b> - found and fixed a bug in sds.c that caused the server to crash at least on 64 bit systems, and anyway to be buggy since we used the same vararg thing against vsprintf without to call va_start and va_end every time.</li><li> <a href="http://www.rot13.org/~dpavlin" target="_blank">Dobrica Pavlinusic</a> - Perl client lib</li><li> Brian Hammond - AUTH command implementation, C++ client lib</li><li> <a href="http://www.clorophilla.net/" target="_blank">Daniele Alessandri</a> - Lua client lib</li><li> Corey Stup - C99 cleanups</li><li> Taylor Weibley - Ruby client improvements</li><li> Bob Potter - Rearrange redisObject struct to reduce memory usage in 64bit environments</li><li> Luca Guidi and Brian McKinney - Ruby client improvements</li><li> Aman Gupta - SDIFF / SDIFFSTORE, other Set operations improvements, ability to disable clients timeout.</li></ul>
<h1><a name="Credits">Credits</a></h1><ul><li> The Redis server was designed and written by <a href="http://invece.org" target="_blank">Salvatore Sanfilippo (aka antirez)</a></li><li> <a href="http://brainspl.at/" target="_blank">Ezra Zygmuntowicz (aka ezmobius)</a> - Ruby client lib initial version and hacking</li><li> <a href="http://qix.it" target="_blank">Ludovico Magnocavallo (aka ludo)</a> - Python clinet lib</li><li> <a href="http://www.adroll.com/" target="_blank">Valentino Volonghi of Adroll</a> - Erlang client lib</li><li> <b>brettbender</b> - found and fixed a bug in sds.c that caused the server to crash at least on 64 bit systems, and anyway to be buggy since we used the same vararg thing against vsprintf without to call va_start and va_end every time.</li><li> <a href="http://www.rot13.org/~dpavlin" target="_blank">Dobrica Pavlinusic</a> - Perl client lib</li><li> Brian Hammond - AUTH command implementation, C++ client lib</li><li> <a href="http://www.clorophilla.net/" target="_blank">Daniele Alessandri</a> - Lua client lib</li><li> Corey Stup - C99 cleanups</li><li> Taylor Weibley - Ruby client improvements</li><li> Bob Potter - Rearrange redisObject struct to reduce memory usage in 64bit environments</li><li> Luca Guidi and Brian McKinney - Ruby client improvements</li><li> Aman Gupta - SDIFF / SDIFFSTORE, other Set operations improvements, ability to disable clients timeout.</li><li> Diego Rosario Brogna - Code and ideas about dumping backtrace on sigsegv and similar error conditions.</li></ul>
p.s. sorry to take this file in sync is hard in this early days. Please drop me an email if I forgot to add your name here!

</div>
Expand Down
2 changes: 1 addition & 1 deletion doc/SinterCommand.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ <h1 class="wikiname">SinterCommand</h1>

<div class="narrow">
<h1><a name="SINTER _key1_ _key2_ ... _keyN_">SINTER _key1_ _key2_ ... _keyN_</a></h1>
<i>Time complexity O(N<b>M) worst case where N is the cardinality of the smallest set and M the number of sets_<br/><br/><blockquote>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.</blockquote>
<i>Time complexity O(N<b>M) worst case where N is the cardinality of the smallest set and M the number of sets_<br/><br/><blockquote>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.</blockquote>
<blockquote>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).</blockquote>
<h2><a name="Return value">Return value</a></h2><a href="ReplyTypes.html">Multi bulk reply</a>, specifically the list of common elements.<h2><a name="See also">See also</a></h2>
<blockquote>* <a href="SaddCommand.html">SADD</a>* <a href="SremCommand.html">SREM</a>* <a href="SismemberCommand.html">SISMEMBER</a>* <a href="ScardCommand.html">SCARD</a>* <a href="SmembersCommand.html">SMEMBERS</a>* <a href="SinterstoreCommand.html">SINTERSTORE</a>* <a href="SunionCommand.html">SUNION</a>* <a href="SunionstoreCommand.html">SUNIONSTORE</a>* <a href="SmoveCommand.html">SMOVE</a></blockquote></b></i>
Expand Down
1 change: 1 addition & 0 deletions redis-cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down
58 changes: 49 additions & 9 deletions redis.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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},
Expand Down Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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},
Expand Down Expand Up @@ -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 */
Expand Down
8 changes: 8 additions & 0 deletions test-redis.tcl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 12fea92

Please sign in to comment.