Permalink
Browse files

Add block semantics to WATCH.

  • Loading branch information...
1 parent 61d98e5 commit a65bba80becf3eb5fd51681f7ec32d68091aa1b7 @djanowski djanowski committed Jun 7, 2010
Showing with 107 additions and 4 deletions.
  1. +15 −2 lib/redis.rb
  2. +92 −2 test/redis_test.rb
View
@@ -9,6 +9,12 @@ def initialize(reply_type)
end
end
+ class CannotExec < RuntimeError
+ def initialize
+ super "EXEC failed because any of the WATCHed keys has been modified."
+ end
+ end
+
def self.deprecate(message, trace = caller[0])
$stderr.puts "\n#{message} (in #{trace})"
end
@@ -463,7 +469,14 @@ def pipelined
end
def watch(*keys)
- @client.call(:watch, *keys)
+ result = @client.call(:watch, *keys)
+ return result unless block_given?
+
+ begin
+ yield
+ ensure
+ unwatch
+ end
end
def unwatch
@@ -486,7 +499,7 @@ def multi(&block)
raise e
end
- exec
+ exec || (raise CannotExec)
end
def publish(channel, message)
View
@@ -1140,8 +1140,12 @@ def redis.call(*attrs)
test "WATCH with a modified key" do
@r.watch "foo"
@r.set "foo", "s1"
- res = @r.multi do |multi|
- multi.set "foo", "s2"
+
+ begin
+ res = @r.multi do |multi|
+ multi.set "foo", "s2"
+ end
+ rescue Redis::CannotExec
end
assert_nil res
@@ -1158,6 +1162,92 @@ def redis.call(*attrs)
assert_equal "s2", @r.get("foo")
end
+
+ test "WATCH with a block" do
+ @r.set "foo", "s1"
+
+ @r.watch "foo" do
+ value = @r.get("foo").succ
+
+ @r.multi do
+ @r.set "foo", value
+ end
+ end
+
+ assert_equal "s2", @r.get("foo")
+ end
+
+ test "WATCH with a block and a modified key" do
+ @r.set "foo", "s1"
+
+ begin
+ @r.watch "foo" do
+ @r.set "foo", "s3"
+
+ @r.multi do
+ @r.set "foo", "s2"
+ end
+ end
+ rescue Redis::CannotExec
+ end
+
+ assert_equal "s3", @r.get("foo")
+ end
+
+ test "WATCH with a block raises on a failed EXEC" do
+ @r.set "foo", "s1"
+
+ assert_raises(Redis::CannotExec) do
+ @r.watch "foo" do
+ @r.set "foo", "s3"
+
+ @r.multi do
+ @r.set "foo", "s2"
+ end
+ end
+ end
+ end
+
+ test "WATCH a failed EXEC can be retried" do
+ @r.set "foo", "s1"
+
+ times = 0
+
+ begin
+ @r.watch "foo" do
+ @r.set "foo", "s3" if times == 0
+
+ @r.multi do
+ @r.set "foo", "s2"
+ end
+ end
+ rescue Redis::CannotExec
+ times += 1
+ retry
+ end
+
+ assert_equal 1, times
+ assert_equal "s2", @r.get("foo")
+ end
+
+ test "subsequent WATCHes do not mix" do
+ @r.set "foo", "s1"
+ @r.set "bar", "s2"
+
+ @r.watch "foo" do
+ # ...
+ end
+
+ @r.watch "bar" do
+ @r.set "foo", "s3"
+
+ @r.multi do
+ @r.set "bar", "s4"
+ end
+ end
+
+ assert_equal "s4", @r.get("bar")
+ end
end
context "Publish/Subscribe" do

0 comments on commit a65bba8

Please sign in to comment.