{"rpop",rpopCommand,2,0,NULL,1,1,1},
{"lpop",lpopCommand,2,0,NULL,1,1,1},
{"brpop",brpopCommand,-3,0,NULL,1,1,1},
+ {"brpoplpush",brpoplpushCommand,4,REDIS_CMD_DENYOOM,NULL,1,2,1},
{"blpop",blpopCommand,-3,0,NULL,1,1,1},
{"llen",llenCommand,2,0,NULL,1,1,1},
{"lindex",lindexCommand,3,0,NULL,1,1,1},
int blocking_keys_num; /* Number of blocking keys */
time_t blockingto; /* Blocking operation timeout. If UNIX current time
* is >= blockingto then the operation timed out. */
+ robj *blocking_target;
list *io_keys; /* Keys this client is waiting to be loaded from the
* swap file in order to continue. */
list *watched_keys; /* Keys WATCHED for MULTI/EXEC CAS */
void discardCommand(redisClient *c);
void blpopCommand(redisClient *c);
void brpopCommand(redisClient *c);
+void brpoplpushCommand(redisClient *c);
void appendCommand(redisClient *c);
void substrCommand(redisClient *c);
void strlenCommand(redisClient *c);
redisAssert(ln != NULL);
receiver = ln->value;
- addReplyMultiBulkLen(receiver,2);
- addReplyBulk(receiver,key);
- addReplyBulk(receiver,ele);
+ if (receiver->blocking_target == NULL) {
+ addReplyMultiBulkLen(receiver,2);
+ addReplyBulk(receiver,key);
+ addReplyBulk(receiver,ele);
+ }
+ else {
+ receiver->argc++;
+
+ robj *dobj = lookupKeyWrite(receiver->db,receiver->blocking_target);
+ if (dobj && checkType(receiver,dobj,REDIS_LIST)) return 0;
+
+ addReplyBulk(receiver,ele);
+
+ /* Create the list if the key does not exist */
+ if (!dobj) {
+ dobj = createZiplistObject();
+ dbAdd(receiver->db,receiver->blocking_target,dobj);
+ }
+
+ listTypePush(dobj,ele,REDIS_HEAD);
+ }
+
unblockClientWaitingData(receiver);
return 1;
}
robj *argv[2], **orig_argv;
int orig_argc;
- /* We need to alter the command arguments before to call
- * popGenericCommand() as the command takes a single key. */
- orig_argv = c->argv;
- orig_argc = c->argc;
- argv[1] = c->argv[j];
- c->argv = argv;
- c->argc = 2;
-
- /* Also the return value is different, we need to output
- * the multi bulk reply header and the key name. The
- * "real" command will add the last element (the value)
- * for us. If this souds like an hack to you it's just
- * because it is... */
- addReplyMultiBulkLen(c,2);
- addReplyBulk(c,argv[1]);
- popGenericCommand(c,where);
-
- /* Fix the client structure with the original stuff */
- c->argv = orig_argv;
- c->argc = orig_argc;
+ if (c->blocking_target == NULL) {
+ /* We need to alter the command arguments before to call
+ * popGenericCommand() as the command takes a single key. */
+ orig_argv = c->argv;
+ orig_argc = c->argc;
+ argv[1] = c->argv[j];
+ c->argv = argv;
+ c->argc = 2;
+
+ /* Also the return value is different, we need to output
+ * the multi bulk reply header and the key name. The
+ * "real" command will add the last element (the value)
+ * for us. If this souds like an hack to you it's just
+ * because it is... */
+ addReplyMultiBulkLen(c,2);
+ addReplyBulk(c,argv[1]);
+
+ popGenericCommand(c,where);
+
+ /* Fix the client structure with the original stuff */
+ c->argv = orig_argv;
+ c->argc = orig_argc;
+ }
+ else {
+ c->argv[2] = c->blocking_target;
+ c->blocking_target = NULL;
+
+ rpoplpushCommand(c);
+ }
+
return;
}
}
void brpopCommand(redisClient *c) {
blockingPopGenericCommand(c,REDIS_TAIL);
}
+
+void brpoplpushCommand(redisClient *c) {
+ c->blocking_target = c->argv[2];
+ c->argv[2] = c->argv[3];
+ c->argc--;
+
+ blockingPopGenericCommand(c,REDIS_TAIL);
+}
assert_equal 0 [r llen blist1]
assert_equal 1 [r llen blist2]
}
+
+ test "BRPOPLPUSH - $type" {
+ r del target
+
+ set rd [redis_deferring_client]
+ create_$type blist "a b $large c d"
+
+ $rd brpoplpush blist target 1
+ assert_equal d [$rd read]
+
+ assert_equal d [r rpop target]
+ assert_equal "a b $large c" [r lrange blist 0 -1]
+ }
+ }
+
+ test "BRPOPLPUSH with zero timeout should block indefinitely" {
+ set rd [redis_deferring_client]
+ r del blist target
+ $rd brpoplpush blist target 0
+ after 1000
+ r rpush blist foo
+ assert_equal foo [$rd read]
+ assert_equal {foo} [r lrange target 0 -1]
+ }
+
+ test "BRPOPLPUSH with wrong source type" {
+ set rd [redis_deferring_client]
+ r del blist target
+ r set blist nolist
+ $rd brpoplpush blist target 1
+ assert_error "ERR*wrong kind*" {$rd read}
+ }
+
+ test "BRPOPLPUSH with wrong destination type" {
+ set rd [redis_deferring_client]
+ r del blist target
+ r set target nolist
+ r lpush blist foo
+ $rd brpoplpush blist target 1
+ assert_error "ERR*wrong kind*" {$rd read}
+
+ set rd [redis_deferring_client]
+ r del blist target
+ r set target nolist
+ $rd brpoplpush blist target 0
+ after 1000
+ r rpush blist foo
+ assert_error "ERR*wrong kind*" {$rd read}
+ assert_equal {foo} [r lrange blist 0 -1]
}
foreach {pop} {BLPOP BRPOP} {