]> git.saurik.com Git - redis.git/commitdiff
BRPOPLPUSH.
authorDamian Janowski & Michel Martens <damian.janowski+soveran@gmail.com>
Mon, 8 Nov 2010 18:25:59 +0000 (15:25 -0300)
committerMichel Martens <michel@soveran.com>
Tue, 30 Nov 2010 02:52:07 +0000 (23:52 -0300)
src/redis.c
src/redis.h
src/t_list.c
tests/unit/type/list.tcl

index 1f7e7d2662844c61e8cfd427c9ee2498279b9cd9..58a796c037e1fe9477f4487228f6f555e41fd39b 100644 (file)
@@ -89,6 +89,7 @@ struct redisCommand readonlyCommandTable[] = {
     {"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},
index 6bd0fd5dfe96f82f644f469dc08b5727eb32f87c..83a94483fc7bb2a1a70d1a0f75cc82ee64f4042a 100644 (file)
@@ -321,6 +321,7 @@ typedef struct redisClient {
     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 */
@@ -961,6 +962,7 @@ void execCommand(redisClient *c);
 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);
index 10e7f72c7137a336c4ec576aa21551d976c5f5e5..437f4004be1845f87ac0f0d0c5a162ba59970602 100644 (file)
@@ -777,9 +777,28 @@ int handleClientsWaitingListPush(redisClient *c, robj *key, robj *ele) {
     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;
 }
@@ -814,26 +833,36 @@ void blockingPopGenericCommand(redisClient *c, int where) {
                     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;
                 }
             }
@@ -860,3 +889,11 @@ void blpopCommand(redisClient *c) {
 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);
+}
index 4c131fc3774eb2953102dbbc5e6b89fe42288488..85cbe88e7223f4b21c5ab4649d4498a2112a26ee 100644 (file)
@@ -127,6 +127,55 @@ start_server {
             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} {