diff options
author | Matt Stancliff <matt@genges.com> | 2014-04-07 21:22:30 -0400 |
---|---|---|
committer | Matt Stancliff <matt@genges.com> | 2014-05-21 09:52:52 -0400 |
commit | 33f943b4cddb0bf90b436ccd47d12363e13d0fce (patch) | |
tree | 23f56948ff849258a9e4e634d56b1f7be606660c /src/t_list.c | |
parent | 56161ca0a444ff6653f884561ab95bd09ecbc1af (diff) | |
download | redis-33f943b4cddb0bf90b436ccd47d12363e13d0fce.tar.gz |
Fix blocking operations from missing new lists
Behrad Zari discovered [1] and Josiah reported [2]: if you block
and wait for a list to exist, but the list creates from
a non-push command, the blocked client never gets notified.
This commit adds notification of blocked clients into
the DB layer and away from individual commands.
Lists can be created by [LR]PUSH, SORT..STORE, RENAME, MOVE,
and RESTORE. Previously, blocked client notifications were
only triggered by [LR]PUSH. Your client would never get
notified if a list were created by SORT..STORE or RENAME or
a RESTORE, etc.
Blocked client notification now happens in one unified place:
- dbAdd() triggers notification when adding a list to the DB
Two new tests are added that fail prior to this commit.
All test pass.
Fixes #1668
[1]: https://groups.google.com/forum/#!topic/redis-db/k4oWfMkN1NU
[2]: #1668
Diffstat (limited to 'src/t_list.c')
-rw-r--r-- | src/t_list.c | 16 |
1 files changed, 5 insertions, 11 deletions
diff --git a/src/t_list.c b/src/t_list.c index 70f5cf164..d03879bdd 100644 --- a/src/t_list.c +++ b/src/t_list.c @@ -29,8 +29,6 @@ #include "redis.h" -void signalListAsReady(redisClient *c, robj *key); - /*----------------------------------------------------------------------------- * List API *----------------------------------------------------------------------------*/ @@ -297,15 +295,12 @@ void listTypeConvert(robj *subject, int enc) { void pushGenericCommand(redisClient *c, int where) { int j, waiting = 0, pushed = 0; robj *lobj = lookupKeyWrite(c->db,c->argv[1]); - int may_have_waiting_clients = (lobj == NULL); if (lobj && lobj->type != REDIS_LIST) { addReply(c,shared.wrongtypeerr); return; } - if (may_have_waiting_clients) signalListAsReady(c,c->argv[1]); - for (j = 2; j < c->argc; j++) { c->argv[j] = tryObjectEncoding(c->argv[j]); if (!lobj) { @@ -709,7 +704,6 @@ void rpoplpushHandlePush(redisClient *c, robj *dstkey, robj *dstobj, robj *value if (!dstobj) { dstobj = createZiplistObject(); dbAdd(c->db,dstkey,dstobj); - signalListAsReady(c,dstkey); } signalModifiedKey(c->db,dstkey); listTypePush(dstobj,value,REDIS_HEAD); @@ -849,19 +843,19 @@ void unblockClientWaitingData(redisClient *c) { * made by a script or in the context of MULTI/EXEC. * * The list will be finally processed by handleClientsBlockedOnLists() */ -void signalListAsReady(redisClient *c, robj *key) { +void signalListAsReady(redisDb *db, robj *key) { readyList *rl; /* No clients blocking for this key? No need to queue it. */ - if (dictFind(c->db->blocking_keys,key) == NULL) return; + if (dictFind(db->blocking_keys,key) == NULL) return; /* Key was already signaled? No need to queue it again. */ - if (dictFind(c->db->ready_keys,key) != NULL) return; + if (dictFind(db->ready_keys,key) != NULL) return; /* Ok, we need to queue this key into server.ready_keys. */ rl = zmalloc(sizeof(*rl)); rl->key = key; - rl->db = c->db; + rl->db = db; incrRefCount(key); listAddNodeTail(server.ready_keys,rl); @@ -869,7 +863,7 @@ void signalListAsReady(redisClient *c, robj *key) { * to avoid adding it multiple times into a list with a simple O(1) * check. */ incrRefCount(key); - redisAssert(dictAdd(c->db->ready_keys,key,NULL) == DICT_OK); + redisAssert(dictAdd(db->ready_keys,key,NULL) == DICT_OK); } /* This is a helper function for handleClientsBlockedOnLists(). It's work |