redis 3.0.0.rc1 → 3.0.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. data/.travis.yml +50 -0
  2. data/.travis/Gemfile +11 -0
  3. data/CHANGELOG.md +47 -19
  4. data/README.md +160 -149
  5. data/Rakefile +15 -50
  6. data/examples/pubsub.rb +1 -1
  7. data/examples/unicorn/config.ru +1 -1
  8. data/examples/unicorn/unicorn.rb +1 -1
  9. data/lib/redis.rb +790 -390
  10. data/lib/redis/client.rb +137 -49
  11. data/lib/redis/connection/hiredis.rb +26 -15
  12. data/lib/redis/connection/ruby.rb +170 -53
  13. data/lib/redis/connection/synchrony.rb +23 -35
  14. data/lib/redis/distributed.rb +92 -32
  15. data/lib/redis/errors.rb +4 -2
  16. data/lib/redis/pipeline.rb +17 -6
  17. data/lib/redis/version.rb +1 -1
  18. data/redis.gemspec +4 -6
  19. data/test/blocking_commands_test.rb +42 -0
  20. data/test/command_map_test.rb +18 -17
  21. data/test/commands_on_hashes_test.rb +13 -12
  22. data/test/commands_on_lists_test.rb +35 -45
  23. data/test/commands_on_sets_test.rb +55 -54
  24. data/test/commands_on_sorted_sets_test.rb +106 -105
  25. data/test/commands_on_strings_test.rb +64 -55
  26. data/test/commands_on_value_types_test.rb +66 -54
  27. data/test/connection_handling_test.rb +136 -151
  28. data/test/distributed_blocking_commands_test.rb +33 -40
  29. data/test/distributed_commands_on_hashes_test.rb +6 -7
  30. data/test/distributed_commands_on_lists_test.rb +13 -14
  31. data/test/distributed_commands_on_sets_test.rb +57 -58
  32. data/test/distributed_commands_on_sorted_sets_test.rb +11 -12
  33. data/test/distributed_commands_on_strings_test.rb +31 -32
  34. data/test/distributed_commands_on_value_types_test.rb +61 -46
  35. data/test/distributed_commands_requiring_clustering_test.rb +108 -108
  36. data/test/distributed_connection_handling_test.rb +14 -15
  37. data/test/distributed_internals_test.rb +7 -19
  38. data/test/distributed_key_tags_test.rb +36 -36
  39. data/test/distributed_persistence_control_commands_test.rb +17 -14
  40. data/test/distributed_publish_subscribe_test.rb +61 -69
  41. data/test/distributed_remote_server_control_commands_test.rb +39 -28
  42. data/test/distributed_sorting_test.rb +12 -13
  43. data/test/distributed_test.rb +40 -41
  44. data/test/distributed_transactions_test.rb +20 -21
  45. data/test/encoding_test.rb +12 -9
  46. data/test/error_replies_test.rb +42 -36
  47. data/test/helper.rb +118 -85
  48. data/test/helper_test.rb +20 -6
  49. data/test/internals_test.rb +167 -103
  50. data/test/lint/blocking_commands.rb +124 -0
  51. data/test/lint/hashes.rb +115 -93
  52. data/test/lint/lists.rb +86 -80
  53. data/test/lint/sets.rb +68 -62
  54. data/test/lint/sorted_sets.rb +200 -195
  55. data/test/lint/strings.rb +112 -94
  56. data/test/lint/value_types.rb +76 -55
  57. data/test/persistence_control_commands_test.rb +17 -12
  58. data/test/pipelining_commands_test.rb +135 -126
  59. data/test/publish_subscribe_test.rb +105 -110
  60. data/test/remote_server_control_commands_test.rb +74 -58
  61. data/test/sorting_test.rb +31 -29
  62. data/test/support/connection/hiredis.rb +1 -0
  63. data/test/support/connection/ruby.rb +1 -0
  64. data/test/support/connection/synchrony.rb +17 -0
  65. data/test/{redis_mock.rb → support/redis_mock.rb} +24 -21
  66. data/test/support/wire/synchrony.rb +24 -0
  67. data/test/support/wire/thread.rb +5 -0
  68. data/test/synchrony_driver.rb +9 -9
  69. data/test/test.conf +1 -1
  70. data/test/thread_safety_test.rb +21 -19
  71. data/test/transactions_test.rb +189 -118
  72. data/test/unknown_commands_test.rb +9 -8
  73. data/test/url_param_test.rb +46 -41
  74. metadata +28 -43
  75. data/TODO.md +0 -4
  76. data/benchmarking/thread_safety.rb +0 -38
  77. data/test/lint/internals.rb +0 -36
@@ -0,0 +1 @@
1
+ require "support/wire/thread"
@@ -0,0 +1 @@
1
+ require "support/wire/thread"
@@ -0,0 +1,17 @@
1
+ require "support/wire/synchrony"
2
+
3
+ module Helper
4
+ def around
5
+ rv = nil
6
+
7
+ EM.synchrony do
8
+ begin
9
+ rv = yield
10
+ ensure
11
+ EM.stop
12
+ end
13
+ end
14
+
15
+ rv
16
+ end
17
+ end
@@ -4,9 +4,12 @@ module RedisMock
4
4
  class Server
5
5
  VERBOSE = false
6
6
 
7
- def initialize(port = 6380, &block)
7
+ def initialize(port, &block)
8
8
  @server = TCPServer.new("127.0.0.1", port)
9
9
  @server.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
10
+ end
11
+
12
+ def start(&block)
10
13
  @thread = Thread.new { run(&block) }
11
14
  end
12
15
 
@@ -61,29 +64,29 @@ module RedisMock
61
64
  end
62
65
  end
63
66
 
64
- module Helper
65
- # Starts a mock Redis server in a thread on port 6380.
66
- #
67
- # The server will reply with a `+OK` to all commands, but you can
68
- # customize it by providing a hash. For example:
69
- #
70
- # redis_mock(:ping => lambda { "+PONG" }) do
71
- # assert_equal "PONG", Redis.new(:port => 6380).ping
72
- # end
73
- #
74
- def redis_mock(replies = {})
75
- begin
76
- server = Server.new do |command, *args|
77
- (replies[command.to_sym] || lambda { |*_| "+OK" }).call(*args)
78
- end
79
-
80
- sleep 0.1 # Give time for the socket to start listening.
67
+ MOCK_PORT = 6382
81
68
 
82
- yield
69
+ # Starts a mock Redis server in a thread.
70
+ #
71
+ # The server will reply with a `+OK` to all commands, but you can
72
+ # customize it by providing a hash. For example:
73
+ #
74
+ # RedisMock.start(:ping => lambda { "+PONG" }) do
75
+ # assert_equal "PONG", Redis.new(:port => MOCK_PORT).ping
76
+ # end
77
+ #
78
+ def self.start(commands = {})
79
+ server = Server.new(MOCK_PORT)
83
80
 
84
- ensure
85
- server.shutdown
81
+ begin
82
+ server.start do |command, *args|
83
+ (commands[command.to_sym] || lambda { |*_| "+OK" }).call(*args)
86
84
  end
85
+
86
+ yield(MOCK_PORT)
87
+
88
+ ensure
89
+ server.shutdown
87
90
  end
88
91
  end
89
92
  end
@@ -0,0 +1,24 @@
1
+ class Wire < Fiber
2
+ # We cannot run this fiber explicitly because EM schedules it. Resuming the
3
+ # current fiber on the next tick to let the reactor do work.
4
+ def self.pass
5
+ f = Fiber.current
6
+ EM.next_tick { f.resume }
7
+ Fiber.yield
8
+ end
9
+
10
+ def self.sleep(sec)
11
+ EM::Synchrony.sleep(sec)
12
+ end
13
+
14
+ def initialize(&blk)
15
+ super
16
+
17
+ # Schedule run in next tick
18
+ EM.next_tick { resume }
19
+ end
20
+
21
+ def join
22
+ self.class.pass while alive?
23
+ end
24
+ end
@@ -0,0 +1,5 @@
1
+ class Wire < Thread
2
+ def self.sleep(sec)
3
+ Kernel.sleep(sec)
4
+ end
5
+ end
@@ -20,13 +20,13 @@ EM.synchrony do
20
20
  r.rpush "foo", "s1"
21
21
  r.rpush "foo", "s2"
22
22
 
23
- assert 2 == r.llen("foo")
24
- assert "s2" == r.rpop("foo")
23
+ assert_equal 2, r.llen("foo")
24
+ assert_equal "s2", r.rpop("foo")
25
25
 
26
26
  r.set("foo", "bar")
27
27
 
28
- assert "bar" == r.getset("foo", "baz")
29
- assert "baz" == r.get("foo")
28
+ assert_equal "bar", r.getset("foo", "baz")
29
+ assert_equal "baz", r.get("foo")
30
30
 
31
31
  r.set("foo", "a")
32
32
 
@@ -46,12 +46,12 @@ EM.synchrony do
46
46
  r.lpush "foo", "s2"
47
47
  end
48
48
 
49
- assert 2 == r.llen("foo")
50
- assert "s2" == r.lpop("foo")
51
- assert "s1" == r.lpop("foo")
49
+ assert_equal 2, r.llen("foo")
50
+ assert_equal "s2", r.lpop("foo")
51
+ assert_equal "s1", r.lpop("foo")
52
52
 
53
- assert "OK" == r.client.call(:quit)
54
- assert "PONG" == r.ping
53
+ assert_equal "OK", r.client.call(:quit)
54
+ assert_equal "PONG", r.ping
55
55
 
56
56
  EM.stop
57
57
  end
@@ -1,6 +1,6 @@
1
1
  dir ./test/db
2
2
  pidfile ./redis.pid
3
- port 6379
3
+ port 6381
4
4
  unixsocket /tmp/redis.sock
5
5
  timeout 300
6
6
  loglevel debug
@@ -1,30 +1,32 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require File.expand_path("./helper", File.dirname(__FILE__))
3
+ require "helper"
4
4
 
5
- setup do
6
- init Redis.new(OPTIONS)
7
- end
5
+ class TestThreadSafety < Test::Unit::TestCase
8
6
 
9
- test "thread safety" do
10
- redis = Redis.connect(OPTIONS.merge(:thread_safe => true))
7
+ include Helper::Client
11
8
 
12
- redis.set "foo", 1
13
- redis.set "bar", 2
9
+ driver(:ruby, :hiredis) do
10
+ def test_thread_safety
11
+ redis = Redis.new(OPTIONS)
12
+ redis.set "foo", 1
13
+ redis.set "bar", 2
14
14
 
15
- sample = 100
15
+ sample = 100
16
16
 
17
- t1 = Thread.new do
18
- $foos = Array.new(sample) { redis.get "foo" }
19
- end
17
+ t1 = Thread.new do
18
+ $foos = Array.new(sample) { redis.get "foo" }
19
+ end
20
20
 
21
- t2 = Thread.new do
22
- $bars = Array.new(sample) { redis.get "bar" }
23
- end
21
+ t2 = Thread.new do
22
+ $bars = Array.new(sample) { redis.get "bar" }
23
+ end
24
24
 
25
- t1.join
26
- t2.join
25
+ t1.join
26
+ t2.join
27
27
 
28
- assert_equal ["1"], $foos.uniq
29
- assert_equal ["2"], $bars.uniq
28
+ assert_equal ["1"], $foos.uniq
29
+ assert_equal ["2"], $bars.uniq
30
+ end
31
+ end
30
32
  end
@@ -1,173 +1,244 @@
1
1
  # encoding: UTF-8
2
2
 
3
- require File.expand_path("./helper", File.dirname(__FILE__))
3
+ require "helper"
4
4
 
5
- setup do
6
- init Redis.new(OPTIONS)
7
- end
5
+ class TestTransactions < Test::Unit::TestCase
8
6
 
9
- test "MULTI/DISCARD" do |r|
10
- r.multi
7
+ include Helper::Client
11
8
 
12
- assert "QUEUED" == r.set("foo", "1")
13
- assert "QUEUED" == r.get("foo")
9
+ def test_multi_discard
10
+ r.multi
14
11
 
15
- r.discard
12
+ assert_equal "QUEUED", r.set("foo", "1")
13
+ assert_equal "QUEUED", r.get("foo")
16
14
 
17
- assert nil == r.get("foo")
18
- end
15
+ r.discard
19
16
 
20
- test "MULTI/EXEC with a block" do |r|
21
- r.multi do |multi|
22
- multi.set "foo", "s1"
17
+ assert_equal nil, r.get("foo")
23
18
  end
24
19
 
25
- assert "s1" == r.get("foo")
26
- end
20
+ def test_multi_exec_with_a_block
21
+ r.multi do |multi|
22
+ multi.set "foo", "s1"
23
+ end
27
24
 
28
- test "MULTI/EXEC with a block doesn't return replies for MULTI and EXEC" do |r|
29
- r1, r2, nothing_else = r.multi do |multi|
30
- multi.set "foo", "s1"
31
- multi.get "foo"
25
+ assert_equal "s1", r.get("foo")
32
26
  end
33
27
 
34
- assert_equal "OK", r1
35
- assert_equal "s1", r2
36
- assert_equal nil, nothing_else
37
- end
28
+ def test_multi_exec_with_a_block_doesn_t_return_replies_for_multi_and_exec
29
+ r1, r2, nothing_else = r.multi do |multi|
30
+ multi.set "foo", "s1"
31
+ multi.get "foo"
32
+ end
38
33
 
39
- test "Assignment inside MULTI/EXEC block" do |r|
40
- r.multi do |m|
41
- @first = m.sadd("foo", 1)
42
- @second = m.sadd("foo", 1)
34
+ assert_equal "OK", r1
35
+ assert_equal "s1", r2
36
+ assert_equal nil, nothing_else
43
37
  end
44
38
 
45
- assert_equal true, @first.value
46
- assert_equal false, @second.value
47
- end
48
-
49
- # Although we could support accessing the values in these futures,
50
- # it doesn't make a lot of sense.
51
- test "Assignment inside MULTI/EXEC block with delayed command errors" do |r|
52
- assert_raise do
39
+ def test_assignment_inside_multi_exec_block
53
40
  r.multi do |m|
54
- @first = m.set("foo", "s1")
55
- @second = m.incr("foo") # not an integer
56
- @third = m.lpush("foo", "value") # wrong kind of value
41
+ @first = m.sadd("foo", 1)
42
+ @second = m.sadd("foo", 1)
57
43
  end
44
+
45
+ assert_equal true, @first.value
46
+ assert_equal false, @second.value
58
47
  end
59
48
 
60
- assert_equal "OK", @first.value
61
- assert_raise { @second.value }
62
- assert_raise { @third.value }
63
- end
49
+ # Although we could support accessing the values in these futures,
50
+ # it doesn't make a lot of sense.
51
+ def test_assignment_inside_multi_exec_block_with_delayed_command_errors
52
+ assert_raise(Redis::CommandError) do
53
+ r.multi do |m|
54
+ @first = m.set("foo", "s1")
55
+ @second = m.incr("foo") # not an integer
56
+ @third = m.lpush("foo", "value") # wrong kind of value
57
+ end
58
+ end
64
59
 
65
- test "Assignment inside MULTI/EXEC block with immediate command errors" do |r|
66
- assert_raise do
67
- r.multi do |m|
68
- m.doesnt_exist
69
- @first = m.sadd("foo", 1)
70
- m.doesnt_exist
71
- @second = m.sadd("foo", 1)
72
- m.doesnt_exist
60
+ assert_equal "OK", @first.value
61
+ assert_raise(Redis::CommandError) { @second.value }
62
+ assert_raise(Redis::FutureNotReady) { @third.value }
63
+ end
64
+
65
+ def test_assignment_inside_multi_exec_block_with_immediate_command_errors
66
+ assert_raise(Redis::CommandError) do
67
+ r.multi do |m|
68
+ m.doesnt_exist
69
+ @first = m.sadd("foo", 1)
70
+ m.doesnt_exist
71
+ @second = m.sadd("foo", 1)
72
+ m.doesnt_exist
73
+ end
73
74
  end
75
+
76
+ assert_raise(Redis::FutureNotReady) { @first.value }
77
+ assert_raise(Redis::FutureNotReady) { @second.value }
74
78
  end
75
79
 
76
- assert_raise(Redis::FutureNotReady) { @first.value }
77
- assert_raise(Redis::FutureNotReady) { @second.value }
78
- end
80
+ def test_raise_immediate_errors_in_multi_exec
81
+ assert_raise(RuntimeError) do
82
+ r.multi do |multi|
83
+ multi.set "bar", "s2"
84
+ raise "Some error"
85
+ multi.set "baz", "s3"
86
+ end
87
+ end
79
88
 
80
- test "Raise immediate errors in MULTI/EXEC" do |r|
81
- assert_raise(RuntimeError) do
82
- r.multi do |multi|
83
- multi.set "bar", "s2"
84
- raise "Some error"
85
- multi.set "baz", "s3"
89
+ assert_equal nil, r.get("bar")
90
+ assert_equal nil, r.get("baz")
91
+ end
92
+
93
+ def test_transformed_replies_as_return_values_for_multi_exec_block
94
+ info, _ = r.multi do |m|
95
+ r.info
86
96
  end
97
+
98
+ assert info.kind_of?(Hash)
87
99
  end
88
100
 
89
- assert nil == r.get("bar")
90
- assert nil == r.get("baz")
91
- end
101
+ def test_transformed_replies_inside_multi_exec_block
102
+ r.multi do |m|
103
+ @info = r.info
104
+ end
92
105
 
93
- test "Transformed replies as return values for MULTI/EXEC block" do |r|
94
- info, _ = r.multi do |m|
95
- r.info
106
+ assert @info.value.kind_of?(Hash)
96
107
  end
97
108
 
98
- assert info.kind_of?(Hash)
99
- end
109
+ def test_raise_command_errors_in_multi_exec
110
+ assert_raise(Redis::CommandError) do
111
+ r.multi do |m|
112
+ m.set("foo", "s1")
113
+ m.incr("foo") # not an integer
114
+ m.lpush("foo", "value") # wrong kind of value
115
+ end
116
+ end
100
117
 
101
- test "Transformed replies inside MULTI/EXEC block" do |r|
102
- r.multi do |m|
103
- @info = r.info
118
+ assert_equal "s1", r.get("foo")
104
119
  end
105
120
 
106
- assert @info.value.kind_of?(Hash)
107
- end
121
+ def test_raise_command_errors_when_accessing_futures_after_multi_exec
122
+ begin
123
+ r.multi do |m|
124
+ m.set("foo", "s1")
125
+ @counter = m.incr("foo") # not an integer
126
+ end
127
+ rescue Exception
128
+ # Not gonna deal with it
129
+ end
108
130
 
109
- test "Raise command errors in MULTI/EXEC" do |r|
110
- assert_raise do
111
- r.multi do |m|
112
- m.set("foo", "s1")
113
- m.incr("foo") # not an integer
114
- m.lpush("foo", "value") # wrong kind of value
131
+ # We should test for Redis::Error here, but hiredis doesn't yet do
132
+ # custom error classes.
133
+ err = nil
134
+ begin
135
+ @counter.value
136
+ rescue => err
115
137
  end
138
+
139
+ assert err.kind_of?(RuntimeError)
116
140
  end
117
141
 
118
- assert "s1" == r.get("foo")
119
- end
142
+ def test_multi_with_a_block_yielding_the_client
143
+ r.multi do |multi|
144
+ multi.set "foo", "s1"
145
+ end
120
146
 
121
- test "Raise command errors when accessing futures after MULTI/EXEC" do |r|
122
- begin
123
- r.multi do |m|
124
- m.set("foo", "s1")
125
- @counter = m.incr("foo") # not an integer
147
+ assert_equal "s1", r.get("foo")
148
+ end
149
+
150
+ def test_watch_with_an_unmodified_key
151
+ r.watch "foo"
152
+ r.multi do |multi|
153
+ multi.set "foo", "s1"
126
154
  end
127
- rescue Exception
128
- # Not gonna deal with it
155
+
156
+ assert_equal "s1", r.get("foo")
129
157
  end
130
158
 
131
- # We should test for Redis::Error here, but hiredis doesn't yet do
132
- # custom error classes.
133
- assert_raise(RuntimeError) { @counter.value }
134
- end
159
+ def test_watch_with_an_unmodified_key_passed_as_array
160
+ r.watch ["foo", "bar"]
161
+ r.multi do |multi|
162
+ multi.set "foo", "s1"
163
+ end
135
164
 
136
- test "MULTI with a block yielding the client" do |r|
137
- r.multi do |multi|
138
- multi.set "foo", "s1"
165
+ assert_equal "s1", r.get("foo")
139
166
  end
140
167
 
141
- assert "s1" == r.get("foo")
142
- end
168
+ def test_watch_with_a_modified_key
169
+ r.watch "foo"
170
+ r.set "foo", "s1"
171
+ res = r.multi do |multi|
172
+ multi.set "foo", "s2"
173
+ end
143
174
 
144
- test "WATCH with an unmodified key" do |r|
145
- r.watch "foo"
146
- r.multi do |multi|
147
- multi.set "foo", "s1"
175
+ assert_equal nil, res
176
+ assert_equal "s1", r.get("foo")
148
177
  end
149
178
 
150
- assert "s1" == r.get("foo")
151
- end
179
+ def test_watch_with_a_modified_key_passed_as_array
180
+ r.watch ["foo", "bar"]
181
+ r.set "foo", "s1"
182
+ res = r.multi do |multi|
183
+ multi.set "foo", "s2"
184
+ end
152
185
 
153
- test "WATCH with a modified key" do |r|
154
- r.watch "foo"
155
- r.set "foo", "s1"
156
- res = r.multi do |multi|
157
- multi.set "foo", "s2"
186
+ assert_equal nil, res
187
+ assert_equal "s1", r.get("foo")
158
188
  end
159
189
 
160
- assert nil == res
161
- assert "s1" == r.get("foo")
162
- end
190
+ def test_watch_with_a_block_and_an_unmodified_key
191
+ result = r.watch "foo" do
192
+ r.multi do |multi|
193
+ multi.set "foo", "s1"
194
+ end
195
+ end
163
196
 
164
- test "UNWATCH with a modified key" do |r|
165
- r.watch "foo"
166
- r.set "foo", "s1"
167
- r.unwatch
168
- r.multi do |multi|
169
- multi.set "foo", "s2"
197
+ assert_equal ["OK"], result
198
+ assert_equal "s1", r.get("foo")
170
199
  end
171
200
 
172
- assert "s2" == r.get("foo")
201
+ def test_watch_with_a_block_and_a_modified_key
202
+ result = r.watch "foo" do
203
+ r.set "foo", "s1"
204
+ r.multi do |multi|
205
+ multi.set "foo", "s2"
206
+ end
207
+ end
208
+
209
+ assert_equal nil, result
210
+ assert_equal "s1", r.get("foo")
211
+ end
212
+
213
+ def test_watch_with_a_block_that_raises_an_exception
214
+ r.set("foo", "s1")
215
+
216
+ begin
217
+ r.watch "foo" do
218
+ raise "test"
219
+ end
220
+ rescue RuntimeError
221
+ end
222
+
223
+ r.set("foo", "s2")
224
+
225
+ # If the watch was still set from within the block above, this multi/exec
226
+ # would fail. This proves that raising an exception above unwatches.
227
+ r.multi do |multi|
228
+ multi.set "foo", "s3"
229
+ end
230
+
231
+ assert_equal "s3", r.get("foo")
232
+ end
233
+
234
+ def test_unwatch_with_a_modified_key
235
+ r.watch "foo"
236
+ r.set "foo", "s1"
237
+ r.unwatch
238
+ r.multi do |multi|
239
+ multi.set "foo", "s2"
240
+ end
241
+
242
+ assert_equal "s2", r.get("foo")
243
+ end
173
244
  end