ZPOP: renaming to have explicit MIN/MAX score idea.

This commit also adds a top comment about a subtle behavior of mixing
blocking operations of different types in the same key.
This commit is contained in:
antirez 2018-05-11 17:31:46 +02:00
parent 6b026b70a8
commit 6efb6c1e06
5 changed files with 111 additions and 100 deletions

View File

@ -204,14 +204,25 @@ void disconnectAllBlockedClients(void) {
/* This function should be called by Redis every time a single command, /* This function should be called by Redis every time a single command,
* a MULTI/EXEC block, or a Lua script, terminated its execution after * a MULTI/EXEC block, or a Lua script, terminated its execution after
* being called by a client. * being called by a client. It handles serving clients blocked in
* lists, streams, and sorted sets, via a blocking commands.
* *
* All the keys with at least one client blocked that received at least * All the keys with at least one client blocked that received at least
* one new element via some PUSH/XADD operation are accumulated into * one new element via some write operation are accumulated into
* the server.ready_keys list. This function will run the list and will * the server.ready_keys list. This function will run the list and will
* serve clients accordingly. Note that the function will iterate again and * serve clients accordingly. Note that the function will iterate again and
* again as a result of serving BRPOPLPUSH we can have new blocking clients * again as a result of serving BRPOPLPUSH we can have new blocking clients
* to serve because of the PUSH side of BRPOPLPUSH. */ * to serve because of the PUSH side of BRPOPLPUSH.
*
* This function is normally "fair", that is, it will server clients
* using a FIFO behavior. However this fairness is violated in certain
* edge cases, that is, when we have clients blocked at the same time
* in a sorted set and in a list, for the same key (a very odd thing to
* do client side, indeed!). Because mismatching clients (blocking for
* a different type compared to the current key type) are moved in the
* other side of the linked list. However as long as the key starts to
* be used only for a single type, like virtually any Redis application will
* do, the function is already fair. */
void handleClientsBlockedOnKeys(void) { void handleClientsBlockedOnKeys(void) {
while(listLength(server.ready_keys) != 0) { while(listLength(server.ready_keys) != 0) {
list *l; list *l;
@ -316,14 +327,14 @@ void handleClientsBlockedOnKeys(void) {
continue; continue;
} }
int reverse = (receiver->lastcmd && int where = (receiver->lastcmd &&
receiver->lastcmd->proc == bzpopCommand) ? receiver->lastcmd->proc == bzpopminCommand)
0 : 1; ? ZSET_MIN : ZSET_MAX;
unblockClient(receiver); unblockClient(receiver);
genericZpopCommand(receiver,&rl->key,1,reverse); genericZpopCommand(receiver,&rl->key,1,where);
propagate(reverse ? propagate(where == ZSET_MIN ?
server.zrevpopCommand : server.zpopCommand, server.zpopminCommand : server.zpopmaxCommand,
receiver->db->id,receiver->argv,receiver->argc, receiver->db->id,receiver->argv,receiver->argc,
PROPAGATE_AOF|PROPAGATE_REPL); PROPAGATE_AOF|PROPAGATE_REPL);
} }

View File

@ -198,10 +198,10 @@ struct redisCommand redisCommandTable[] = {
{"zrank",zrankCommand,3,"rF",0,NULL,1,1,1,0,0}, {"zrank",zrankCommand,3,"rF",0,NULL,1,1,1,0,0},
{"zrevrank",zrevrankCommand,3,"rF",0,NULL,1,1,1,0,0}, {"zrevrank",zrevrankCommand,3,"rF",0,NULL,1,1,1,0,0},
{"zscan",zscanCommand,-3,"rR",0,NULL,1,1,1,0,0}, {"zscan",zscanCommand,-3,"rR",0,NULL,1,1,1,0,0},
{"zpop",zpopCommand,-2,"wF",0,NULL,1,-1,1,0,0}, {"zpopmin",zpopminCommand,-2,"wF",0,NULL,1,-1,1,0,0},
{"zrevpop",zrevpopCommand,-2,"wF",0,NULL,1,-1,1,0,0}, {"zpopmax",zpopmaxCommand,-2,"wF",0,NULL,1,-1,1,0,0},
{"bzpop",bzpopCommand,-2,"wsF",0,NULL,1,-2,1,0,0}, {"bzpopmin",bzpopminCommand,-2,"wsF",0,NULL,1,-2,1,0,0},
{"bzrevpop",bzrevpopCommand,-2,"wsF",0,NULL,1,-2,1,0,0}, {"bzpopmax",bzpopmaxCommand,-2,"wsF",0,NULL,1,-2,1,0,0},
{"hset",hsetCommand,-4,"wmF",0,NULL,1,1,1,0,0}, {"hset",hsetCommand,-4,"wmF",0,NULL,1,1,1,0,0},
{"hsetnx",hsetnxCommand,4,"wmF",0,NULL,1,1,1,0,0}, {"hsetnx",hsetnxCommand,4,"wmF",0,NULL,1,1,1,0,0},
{"hget",hgetCommand,3,"rF",0,NULL,1,1,1,0,0}, {"hget",hgetCommand,3,"rF",0,NULL,1,1,1,0,0},
@ -1373,8 +1373,8 @@ void createSharedObjects(void) {
shared.rpop = createStringObject("RPOP",4); shared.rpop = createStringObject("RPOP",4);
shared.lpop = createStringObject("LPOP",4); shared.lpop = createStringObject("LPOP",4);
shared.lpush = createStringObject("LPUSH",5); shared.lpush = createStringObject("LPUSH",5);
shared.zpop = createStringObject("ZPOP",4); shared.zpopmin = createStringObject("ZPOPMIN",7);
shared.zrevpop = createStringObject("ZREVPOP",7); shared.zpopmax = createStringObject("ZPOPMAX",7);
for (j = 0; j < OBJ_SHARED_INTEGERS; j++) { for (j = 0; j < OBJ_SHARED_INTEGERS; j++) {
shared.integers[j] = shared.integers[j] =
makeObjectShared(createObject(OBJ_STRING,(void*)(long)j)); makeObjectShared(createObject(OBJ_STRING,(void*)(long)j));
@ -1568,8 +1568,8 @@ void initServerConfig(void) {
server.lpushCommand = lookupCommandByCString("lpush"); server.lpushCommand = lookupCommandByCString("lpush");
server.lpopCommand = lookupCommandByCString("lpop"); server.lpopCommand = lookupCommandByCString("lpop");
server.rpopCommand = lookupCommandByCString("rpop"); server.rpopCommand = lookupCommandByCString("rpop");
server.zpopCommand = lookupCommandByCString("zpop"); server.zpopminCommand = lookupCommandByCString("zpopmin");
server.zrevpopCommand = lookupCommandByCString("zrevpop"); server.zpopmaxCommand = lookupCommandByCString("zpopmax");
server.sremCommand = lookupCommandByCString("srem"); server.sremCommand = lookupCommandByCString("srem");
server.execCommand = lookupCommandByCString("exec"); server.execCommand = lookupCommandByCString("exec");
server.expireCommand = lookupCommandByCString("expire"); server.expireCommand = lookupCommandByCString("expire");

View File

@ -316,6 +316,8 @@ typedef long long mstime_t; /* millisecond time type. */
/* List related stuff */ /* List related stuff */
#define LIST_HEAD 0 #define LIST_HEAD 0
#define LIST_TAIL 1 #define LIST_TAIL 1
#define ZSET_MIN 0
#define ZSET_MAX 1
/* Sort operations */ /* Sort operations */
#define SORT_OP_GET 0 #define SORT_OP_GET 0
@ -763,7 +765,7 @@ struct sharedObjectsStruct {
*masterdownerr, *roslaveerr, *execaborterr, *noautherr, *noreplicaserr, *masterdownerr, *roslaveerr, *execaborterr, *noautherr, *noreplicaserr,
*busykeyerr, *oomerr, *plus, *messagebulk, *pmessagebulk, *subscribebulk, *busykeyerr, *oomerr, *plus, *messagebulk, *pmessagebulk, *subscribebulk,
*unsubscribebulk, *psubscribebulk, *punsubscribebulk, *del, *unlink, *unsubscribebulk, *psubscribebulk, *punsubscribebulk, *del, *unlink,
*rpop, *lpop, *lpush, *zpop, *zrevpop, *emptyscan, *rpop, *lpop, *lpush, *zpopmin, *zpopmax, *emptyscan,
*select[PROTO_SHARED_SELECT_CMDS], *select[PROTO_SHARED_SELECT_CMDS],
*integers[OBJ_SHARED_INTEGERS], *integers[OBJ_SHARED_INTEGERS],
*mbulkhdr[OBJ_SHARED_BULKHDR_LEN], /* "*<value>\r\n" */ *mbulkhdr[OBJ_SHARED_BULKHDR_LEN], /* "*<value>\r\n" */
@ -960,9 +962,10 @@ struct redisServer {
time_t loading_start_time; time_t loading_start_time;
off_t loading_process_events_interval_bytes; off_t loading_process_events_interval_bytes;
/* Fast pointers to often looked up command */ /* Fast pointers to often looked up command */
struct redisCommand *delCommand, *multiCommand, *lpushCommand, *lpopCommand, struct redisCommand *delCommand, *multiCommand, *lpushCommand,
*rpopCommand, *zpopCommand, *zrevpopCommand, *sremCommand, *lpopCommand, *rpopCommand, *zpopminCommand,
*execCommand, *expireCommand, *pexpireCommand, *xclaimCommand; *zpopmaxCommand, *sremCommand, *execCommand,
*expireCommand, *pexpireCommand, *xclaimCommand;
/* Fields used only for stats */ /* Fields used only for stats */
time_t stat_starttime; /* Server start time */ time_t stat_starttime; /* Server start time */
long long stat_numcommands; /* Number of processed commands */ long long stat_numcommands; /* Number of processed commands */
@ -1970,10 +1973,10 @@ void zremCommand(client *c);
void zscoreCommand(client *c); void zscoreCommand(client *c);
void zremrangebyscoreCommand(client *c); void zremrangebyscoreCommand(client *c);
void zremrangebylexCommand(client *c); void zremrangebylexCommand(client *c);
void zpopCommand(client *c); void zpopminCommand(client *c);
void zrevpopCommand(client *c); void zpopmaxCommand(client *c);
void bzpopCommand(client *c); void bzpopminCommand(client *c);
void bzrevpopCommand(client *c); void bzpopmaxCommand(client *c);
void multiCommand(client *c); void multiCommand(client *c);
void execCommand(client *c); void execCommand(client *c);
void discardCommand(client *c); void discardCommand(client *c);

View File

@ -3070,16 +3070,15 @@ void zscanCommand(client *c) {
} }
/* This command implements the generic zpop operation, used by: /* This command implements the generic zpop operation, used by:
* ZPOP, ZREVPOP, BZPOP and BZREVPOP */ * ZPOPMIN, ZPOPMAX, BZPOPMIN and BZPOPMAX */
void genericZpopCommand(client *c, robj **keyv, int keyc, int reverse) { void genericZpopCommand(client *c, robj **keyv, int keyc, int where) {
int idx; int idx;
robj *key; robj *key;
robj *zobj; robj *zobj;
sds ele; sds ele;
double score; double score;
char *events[2] = {"zpop","zrevpop"};
// Check type and break on the first error, otherwise identify candidate /* Check type and break on the first error, otherwise identify candidate. */
idx = 0; idx = 0;
while (idx < keyc) { while (idx < keyc) {
key = keyv[idx++]; key = keyv[idx++];
@ -3089,7 +3088,7 @@ void genericZpopCommand(client *c, robj **keyv, int keyc, int reverse) {
break; break;
} }
// No candidate for zpopping, return empty /* No candidate for zpopping, return empty. */
if (!zobj) { if (!zobj) {
addReply(c,shared.emptymultibulk); addReply(c,shared.emptymultibulk);
return; return;
@ -3102,11 +3101,8 @@ void genericZpopCommand(client *c, robj **keyv, int keyc, int reverse) {
unsigned int vlen; unsigned int vlen;
long long vlong; long long vlong;
// Get the first or last element in the sorted set /* Get the first or last element in the sorted set. */
eptr = ziplistIndex(zl,reverse ? -2 : 0); eptr = ziplistIndex(zl,where == ZSET_MAX ? -2 : 0);
serverAssertWithInfo(c,zobj,eptr != NULL);
// There must be an element in the sorted set
serverAssertWithInfo(c,zobj,eptr != NULL); serverAssertWithInfo(c,zobj,eptr != NULL);
serverAssertWithInfo(c,zobj,ziplistGet(eptr,&vstr,&vlen,&vlong)); serverAssertWithInfo(c,zobj,ziplistGet(eptr,&vstr,&vlen,&vlong));
if (vstr == NULL) if (vstr == NULL)
@ -3114,22 +3110,22 @@ void genericZpopCommand(client *c, robj **keyv, int keyc, int reverse) {
else else
ele = sdsnewlen(vstr,vlen); ele = sdsnewlen(vstr,vlen);
// Get the score /* Get the score. */
sptr = ziplistNext(zl,eptr); sptr = ziplistNext(zl,eptr);
serverAssertWithInfo(c,zobj,sptr != NULL); serverAssertWithInfo(c,zobj,sptr != NULL);
score = zzlGetScore(sptr); score = zzlGetScore(sptr);
} else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) { } else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {
zset *zs = zobj->ptr; zset *zs = zobj->ptr;
zskiplist *zsl = zs->zsl; zskiplist *zsl = zs->zsl;
zskiplistNode *ln; zskiplistNode *zln;
// Get the first or last element in the sorted set // Get the first or last element in the sorted set
ln = (reverse ? zsl->tail : zsl->header->level[0].forward); zln = (where == ZSET_MAX ? zsl->tail : zsl->header->level[0].forward);
// There must be an element in the sorted set // There must be an element in the sorted set
serverAssertWithInfo(c,zobj,ln != NULL); serverAssertWithInfo(c,zobj,zln != NULL);
ele = sdsdup(ln->ele); ele = sdsdup(zln->ele);
score = ln->score; score = zln->score;
} else { } else {
serverPanic("Unknown sorted set encoding"); serverPanic("Unknown sorted set encoding");
} }
@ -3138,7 +3134,8 @@ void genericZpopCommand(client *c, robj **keyv, int keyc, int reverse) {
serverAssertWithInfo(c,zobj,zsetDel(zobj,ele)); serverAssertWithInfo(c,zobj,zsetDel(zobj,ele));
server.dirty++; server.dirty++;
signalModifiedKey(c->db,key); signalModifiedKey(c->db,key);
notifyKeyspaceEvent(NOTIFY_ZSET,events[reverse],key,c->db->id); char *events[2] = {"zpopmin","zpopmax"};
notifyKeyspaceEvent(NOTIFY_ZSET,events[where],key,c->db->id);
// Remove the key, if indeed needed // Remove the key, if indeed needed
if (zsetLength(zobj) == 0) { if (zsetLength(zobj) == 0) {
@ -3153,18 +3150,18 @@ void genericZpopCommand(client *c, robj **keyv, int keyc, int reverse) {
sdsfree(ele); sdsfree(ele);
} }
// ZPOP key [key ...] // ZPOPMIN key [key ...]
void zpopCommand(client *c) { void zpopminCommand(client *c) {
genericZpopCommand(c,&c->argv[1],c->argc-1,0); genericZpopCommand(c,&c->argv[1],c->argc-1,ZSET_MIN);
} }
// ZREVPOP key [key ...] // ZMAXPOP key [key ...]
void zrevpopCommand(client *c) { void zpopmaxCommand(client *c) {
genericZpopCommand(c,&c->argv[1],c->argc-1,1); genericZpopCommand(c,&c->argv[1],c->argc-1,ZSET_MAX);
} }
/* Blocking Z[REV]POP */ /* BZPOPMIN / BZPOPMAX actual implementation. */
void blockingGenericZpopCommand(client *c, int reverse) { void blockingGenericZpopCommand(client *c, int where) {
robj *o; robj *o;
mstime_t timeout; mstime_t timeout;
int j; int j;
@ -3181,10 +3178,10 @@ void blockingGenericZpopCommand(client *c, int reverse) {
} else { } else {
if (zsetLength(o) != 0) { if (zsetLength(o) != 0) {
/* Non empty zset, this is like a normal Z[REV]POP. */ /* Non empty zset, this is like a normal Z[REV]POP. */
genericZpopCommand(c,&c->argv[j],1,reverse); genericZpopCommand(c,&c->argv[j],1,where);
/* Replicate it as an Z[REV]POP instead of BZ[REV]POP. */ /* Replicate it as an Z[REV]POP instead of BZ[REV]POP. */
rewriteClientCommandVector(c,2, rewriteClientCommandVector(c,2,
reverse ? shared.zrevpop : shared.zpop, where == ZSET_MAX ? shared.zpopmax : shared.zpopmin,
c->argv[j]); c->argv[j]);
return; return;
} }
@ -3203,12 +3200,12 @@ void blockingGenericZpopCommand(client *c, int reverse) {
blockForKeys(c,BLOCKED_ZSET,c->argv + 1,c->argc - 2,timeout,NULL,NULL); blockForKeys(c,BLOCKED_ZSET,c->argv + 1,c->argc - 2,timeout,NULL,NULL);
} }
// BZPOP key [key ...] timeout // BZPOPMIN key [key ...] timeout
void bzpopCommand(client *c) { void bzpopminCommand(client *c) {
blockingGenericZpopCommand(c,0); blockingGenericZpopCommand(c,ZSET_MIN);
} }
// BZREVPOP key [key ...] timeout // BZPOPMAX key [key ...] timeout
void bzrevpopCommand(client *c) { void bzpopmaxCommand(client *c) {
blockingGenericZpopCommand(c,1); blockingGenericZpopCommand(c,ZSET_MAX);
} }

View File

@ -649,74 +649,74 @@ start_server {tags {"zset"}} {
} }
} }
test "Basic Z\[REV\]POP with a single key - $encoding" { test "Basic ZPOP with a single key - $encoding" {
r del zset r del zset
assert_equal {} [r zpop zset] assert_equal {} [r zpopmin zset]
create_zset zset {-1 a 1 b 2 c 3 d 4 e} create_zset zset {-1 a 1 b 2 c 3 d 4 e}
assert_equal {zset -1 a} [r zpop zset] assert_equal {zset -1 a} [r zpopmin zset]
assert_equal {zset 1 b} [r zpop zset] assert_equal {zset 1 b} [r zpopmin zset]
assert_equal {zset 4 e} [r zrevpop zset] assert_equal {zset 4 e} [r zpopmax zset]
assert_equal {zset 3 d} [r zrevpop zset] assert_equal {zset 3 d} [r zpopmax zset]
assert_equal {zset 2 c} [r zpop zset] assert_equal {zset 2 c} [r zpopmin zset]
assert_equal 0 [r exists zset] assert_equal 0 [r exists zset]
r set foo bar r set foo bar
assert_error "*WRONGTYPE*" {r zpop foo} assert_error "*WRONGTYPE*" {r zpopmin foo}
} }
test "Z\[REV\]POP with multiple keys - $encoding" { test "ZPOP with multiple keys - $encoding" {
r del z1 z2 z3 foo r del z1 z2 z3 foo
r set foo bar r set foo bar
assert_equal {} [r zpop z1 z2 z3] assert_equal {} [r zpopmin z1 z2 z3]
assert_error "*WRONGTYPE*" {r zpop z1 foo} assert_error "*WRONGTYPE*" {r zpopmin z1 foo}
create_zset z1 {0 a 1 b 2 c} create_zset z1 {0 a 1 b 2 c}
assert_equal {z1 0 a} [r zpop z1 z2 z3] assert_equal {z1 0 a} [r zpopmin z1 z2 z3]
assert_equal {z1 1 b} [r zpop z3 z2 z1] assert_equal {z1 1 b} [r zpopmin z3 z2 z1]
create_zset z3 {0 a 1 b 2 c} create_zset z3 {0 a 1 b 2 c}
assert_equal {z3 2 c} [r zrevpop z3 z2 z1] assert_equal {z3 2 c} [r zpopmax z3 z2 z1]
assert_equal 1 [r exists z1] assert_equal 1 [r exists z1]
assert_equal 1 [r exists z3] assert_equal 1 [r exists z3]
} }
test "BZ\[REV\]POP with a single existing sorted set - $encoding" { test "BZPOP with a single existing sorted set - $encoding" {
set rd [redis_deferring_client] set rd [redis_deferring_client]
create_zset zset {0 a 1 b 2 c} create_zset zset {0 a 1 b 2 c}
$rd bzpop zset 5 $rd bzpopmin zset 5
assert_equal {zset 0 a} [$rd read] assert_equal {zset 0 a} [$rd read]
$rd bzpop zset 5 $rd bzpopmin zset 5
assert_equal {zset 1 b} [$rd read] assert_equal {zset 1 b} [$rd read]
$rd bzrevpop zset 5 $rd bzpopmax zset 5
assert_equal {zset 2 c} [$rd read] assert_equal {zset 2 c} [$rd read]
assert_equal 0 [r exists zset] assert_equal 0 [r exists zset]
} }
test "BZ\[REV\]POP with multiple existing sorted sets - $encoding" { test "BZPOP with multiple existing sorted sets - $encoding" {
set rd [redis_deferring_client] set rd [redis_deferring_client]
create_zset z1 {0 a 1 b 2 c} create_zset z1 {0 a 1 b 2 c}
create_zset z2 {3 d 4 e 5 f} create_zset z2 {3 d 4 e 5 f}
$rd bzpop z1 z2 5 $rd bzpopmin z1 z2 5
assert_equal {z1 0 a} [$rd read] assert_equal {z1 0 a} [$rd read]
$rd bzrevpop z1 z2 5 $rd bzpopmax z1 z2 5
assert_equal {z1 2 c} [$rd read] assert_equal {z1 2 c} [$rd read]
assert_equal 1 [r zcard z1] assert_equal 1 [r zcard z1]
assert_equal 3 [r zcard z2] assert_equal 3 [r zcard z2]
$rd bzrevpop z2 z1 5 $rd bzpopmax z2 z1 5
assert_equal {z2 5 f} [$rd read] assert_equal {z2 5 f} [$rd read]
$rd bzpop z2 z1 5 $rd bzpopmin z2 z1 5
assert_equal {z2 3 d} [$rd read] assert_equal {z2 3 d} [$rd read]
assert_equal 1 [r zcard z1] assert_equal 1 [r zcard z1]
assert_equal 1 [r zcard z2] assert_equal 1 [r zcard z2]
} }
test "BZ\[REV\]POP second sorted set has members - $encoding" { test "BZPOP second sorted set has members - $encoding" {
set rd [redis_deferring_client] set rd [redis_deferring_client]
r del z1 r del z1
create_zset z2 {3 d 4 e 5 f} create_zset z2 {3 d 4 e 5 f}
$rd bzrevpop z1 z2 5 $rd bzpopmax z1 z2 5
assert_equal {z2 5 f} [$rd read] assert_equal {z2 5 f} [$rd read]
$rd bzpop z2 z1 5 $rd bzpopmin z2 z1 5
assert_equal {z2 3 d} [$rd read] assert_equal {z2 3 d} [$rd read]
assert_equal 0 [r zcard z1] assert_equal 0 [r zcard z1]
assert_equal 1 [r zcard z2] assert_equal 1 [r zcard z2]
@ -1099,11 +1099,11 @@ start_server {tags {"zset"}} {
assert_equal {} $err assert_equal {} $err
} }
test "BZPOP, ZADD + DEL should not awake blocked client" { test "BZPOPMIN, ZADD + DEL should not awake blocked client" {
set rd [redis_deferring_client] set rd [redis_deferring_client]
r del zset r del zset
$rd bzpop zset 0 $rd bzpopmin zset 0
r multi r multi
r zadd zset 0 foo r zadd zset 0 foo
r del zset r del zset
@ -1113,13 +1113,13 @@ start_server {tags {"zset"}} {
$rd read $rd read
} {zset 1 bar} } {zset 1 bar}
test "BZPOP, ZADD + DEL + SET should not awake blocked client" { test "BZPOPMIN, ZADD + DEL + SET should not awake blocked client" {
set rd [redis_deferring_client] set rd [redis_deferring_client]
r del list r del list
r del zset r del zset
$rd bzpop zset 0 $rd bzpopmin zset 0
r multi r multi
r zadd zset 0 foo r zadd zset 0 foo
r del zset r del zset
@ -1130,31 +1130,31 @@ start_server {tags {"zset"}} {
$rd read $rd read
} {zset 1 bar} } {zset 1 bar}
test "BZPOP with same key multiple times should work" { test "BZPOPMIN with same key multiple times should work" {
set rd [redis_deferring_client] set rd [redis_deferring_client]
r del z1 z2 r del z1 z2
# Data arriving after the BZPOP. # Data arriving after the BZPOPMIN.
$rd bzpop z1 z2 z2 z1 0 $rd bzpopmin z1 z2 z2 z1 0
r zadd z1 0 a r zadd z1 0 a
assert_equal [$rd read] {z1 0 a} assert_equal [$rd read] {z1 0 a}
$rd bzpop z1 z2 z2 z1 0 $rd bzpopmin z1 z2 z2 z1 0
r zadd z2 1 b r zadd z2 1 b
assert_equal [$rd read] {z2 1 b} assert_equal [$rd read] {z2 1 b}
# Data already there. # Data already there.
r zadd z1 0 a r zadd z1 0 a
r zadd z2 1 b r zadd z2 1 b
$rd bzpop z1 z2 z2 z1 0 $rd bzpopmin z1 z2 z2 z1 0
assert_equal [$rd read] {z1 0 a} assert_equal [$rd read] {z1 0 a}
$rd bzpop z1 z2 z2 z1 0 $rd bzpopmin z1 z2 z2 z1 0
assert_equal [$rd read] {z2 1 b} assert_equal [$rd read] {z2 1 b}
} }
test "MULTI/EXEC is isolated from the point of view of BZPOP" { test "MULTI/EXEC is isolated from the point of view of BZPOPMIN" {
set rd [redis_deferring_client] set rd [redis_deferring_client]
r del zset r del zset
$rd bzpop zset 0 $rd bzpopmin zset 0
r multi r multi
r zadd zset 0 a r zadd zset 0 a
r zadd zset 1 b r zadd zset 1 b
@ -1163,11 +1163,11 @@ start_server {tags {"zset"}} {
$rd read $rd read
} {zset 0 a} } {zset 0 a}
test "BZPOP with variadic ZADD" { test "BZPOPMIN with variadic ZADD" {
set rd [redis_deferring_client] set rd [redis_deferring_client]
r del zset r del zset
if {$::valgrind} {after 100} if {$::valgrind} {after 100}
$rd bzpop zset 0 $rd bzpopmin zset 0
if {$::valgrind} {after 100} if {$::valgrind} {after 100}
assert_equal 2 [r zadd zset -1 foo 1 bar] assert_equal 2 [r zadd zset -1 foo 1 bar]
if {$::valgrind} {after 100} if {$::valgrind} {after 100}
@ -1175,10 +1175,10 @@ start_server {tags {"zset"}} {
assert_equal {bar} [r zrange zset 0 -1] assert_equal {bar} [r zrange zset 0 -1]
} }
test "BZPOP with zero timeout should block indefinitely" { test "BZPOPMIN with zero timeout should block indefinitely" {
set rd [redis_deferring_client] set rd [redis_deferring_client]
r del zset r del zset
$rd bzpop zset 0 $rd bzpopmin zset 0
after 1000 after 1000
r zadd zset 0 foo r zadd zset 0 foo
assert_equal {zset 0 foo} [$rd read] assert_equal {zset 0 foo} [$rd read]