redis2-namespaced 3.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.order +170 -0
  4. data/.travis/Gemfile +11 -0
  5. data/.travis.yml +55 -0
  6. data/.yardopts +3 -0
  7. data/CHANGELOG.md +285 -0
  8. data/LICENSE +20 -0
  9. data/README.md +251 -0
  10. data/Rakefile +403 -0
  11. data/benchmarking/logging.rb +71 -0
  12. data/benchmarking/pipeline.rb +51 -0
  13. data/benchmarking/speed.rb +21 -0
  14. data/benchmarking/suite.rb +24 -0
  15. data/benchmarking/worker.rb +71 -0
  16. data/examples/basic.rb +15 -0
  17. data/examples/dist_redis.rb +43 -0
  18. data/examples/incr-decr.rb +17 -0
  19. data/examples/list.rb +26 -0
  20. data/examples/pubsub.rb +37 -0
  21. data/examples/sets.rb +36 -0
  22. data/examples/unicorn/config.ru +3 -0
  23. data/examples/unicorn/unicorn.rb +20 -0
  24. data/lib/redis2/client.rb +419 -0
  25. data/lib/redis2/connection/command_helper.rb +44 -0
  26. data/lib/redis2/connection/hiredis.rb +63 -0
  27. data/lib/redis2/connection/registry.rb +12 -0
  28. data/lib/redis2/connection/ruby.rb +322 -0
  29. data/lib/redis2/connection/synchrony.rb +124 -0
  30. data/lib/redis2/connection.rb +9 -0
  31. data/lib/redis2/distributed.rb +853 -0
  32. data/lib/redis2/errors.rb +40 -0
  33. data/lib/redis2/hash_ring.rb +131 -0
  34. data/lib/redis2/pipeline.rb +141 -0
  35. data/lib/redis2/subscribe.rb +83 -0
  36. data/lib/redis2/version.rb +3 -0
  37. data/lib/redis2.rb +2533 -0
  38. data/redis.gemspec +43 -0
  39. data/test/bitpos_test.rb +69 -0
  40. data/test/blocking_commands_test.rb +42 -0
  41. data/test/command_map_test.rb +30 -0
  42. data/test/commands_on_hashes_test.rb +21 -0
  43. data/test/commands_on_lists_test.rb +20 -0
  44. data/test/commands_on_sets_test.rb +77 -0
  45. data/test/commands_on_sorted_sets_test.rb +109 -0
  46. data/test/commands_on_strings_test.rb +101 -0
  47. data/test/commands_on_value_types_test.rb +131 -0
  48. data/test/connection_handling_test.rb +189 -0
  49. data/test/db/.gitkeep +0 -0
  50. data/test/distributed_blocking_commands_test.rb +46 -0
  51. data/test/distributed_commands_on_hashes_test.rb +10 -0
  52. data/test/distributed_commands_on_lists_test.rb +22 -0
  53. data/test/distributed_commands_on_sets_test.rb +83 -0
  54. data/test/distributed_commands_on_sorted_sets_test.rb +18 -0
  55. data/test/distributed_commands_on_strings_test.rb +59 -0
  56. data/test/distributed_commands_on_value_types_test.rb +95 -0
  57. data/test/distributed_commands_requiring_clustering_test.rb +164 -0
  58. data/test/distributed_connection_handling_test.rb +23 -0
  59. data/test/distributed_internals_test.rb +70 -0
  60. data/test/distributed_key_tags_test.rb +52 -0
  61. data/test/distributed_persistence_control_commands_test.rb +26 -0
  62. data/test/distributed_publish_subscribe_test.rb +92 -0
  63. data/test/distributed_remote_server_control_commands_test.rb +66 -0
  64. data/test/distributed_scripting_test.rb +102 -0
  65. data/test/distributed_sorting_test.rb +20 -0
  66. data/test/distributed_test.rb +58 -0
  67. data/test/distributed_transactions_test.rb +32 -0
  68. data/test/encoding_test.rb +18 -0
  69. data/test/error_replies_test.rb +59 -0
  70. data/test/helper.rb +218 -0
  71. data/test/helper_test.rb +24 -0
  72. data/test/internals_test.rb +410 -0
  73. data/test/lint/blocking_commands.rb +150 -0
  74. data/test/lint/hashes.rb +162 -0
  75. data/test/lint/lists.rb +143 -0
  76. data/test/lint/sets.rb +125 -0
  77. data/test/lint/sorted_sets.rb +238 -0
  78. data/test/lint/strings.rb +260 -0
  79. data/test/lint/value_types.rb +122 -0
  80. data/test/persistence_control_commands_test.rb +26 -0
  81. data/test/pipelining_commands_test.rb +242 -0
  82. data/test/publish_subscribe_test.rb +210 -0
  83. data/test/remote_server_control_commands_test.rb +117 -0
  84. data/test/scanning_test.rb +413 -0
  85. data/test/scripting_test.rb +78 -0
  86. data/test/sorting_test.rb +59 -0
  87. data/test/support/connection/hiredis.rb +1 -0
  88. data/test/support/connection/ruby.rb +1 -0
  89. data/test/support/connection/synchrony.rb +17 -0
  90. data/test/support/redis_mock.rb +115 -0
  91. data/test/support/wire/synchrony.rb +24 -0
  92. data/test/support/wire/thread.rb +5 -0
  93. data/test/synchrony_driver.rb +88 -0
  94. data/test/test.conf +9 -0
  95. data/test/thread_safety_test.rb +32 -0
  96. data/test/transactions_test.rb +264 -0
  97. data/test/unknown_commands_test.rb +14 -0
  98. data/test/url_param_test.rb +132 -0
  99. metadata +226 -0
@@ -0,0 +1,242 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("helper", File.dirname(__FILE__))
4
+
5
+ class TestPipeliningCommands < Test::Unit::TestCase
6
+
7
+ include Helper::Client
8
+
9
+ def test_bulk_commands
10
+ r.pipelined do
11
+ r.lpush "foo", "s1"
12
+ r.lpush "foo", "s2"
13
+ end
14
+
15
+ assert_equal 2, r.llen("foo")
16
+ assert_equal "s2", r.lpop("foo")
17
+ assert_equal "s1", r.lpop("foo")
18
+ end
19
+
20
+ def test_multi_bulk_commands
21
+ r.pipelined do
22
+ r.mset("foo", "s1", "bar", "s2")
23
+ r.mset("baz", "s3", "qux", "s4")
24
+ end
25
+
26
+ assert_equal "s1", r.get("foo")
27
+ assert_equal "s2", r.get("bar")
28
+ assert_equal "s3", r.get("baz")
29
+ assert_equal "s4", r.get("qux")
30
+ end
31
+
32
+ def test_bulk_and_multi_bulk_commands_mixed
33
+ r.pipelined do
34
+ r.lpush "foo", "s1"
35
+ r.lpush "foo", "s2"
36
+ r.mset("baz", "s3", "qux", "s4")
37
+ end
38
+
39
+ assert_equal 2, r.llen("foo")
40
+ assert_equal "s2", r.lpop("foo")
41
+ assert_equal "s1", r.lpop("foo")
42
+ assert_equal "s3", r.get("baz")
43
+ assert_equal "s4", r.get("qux")
44
+ end
45
+
46
+ def test_multi_bulk_and_bulk_commands_mixed
47
+ r.pipelined do
48
+ r.mset("baz", "s3", "qux", "s4")
49
+ r.lpush "foo", "s1"
50
+ r.lpush "foo", "s2"
51
+ end
52
+
53
+ assert_equal 2, r.llen("foo")
54
+ assert_equal "s2", r.lpop("foo")
55
+ assert_equal "s1", r.lpop("foo")
56
+ assert_equal "s3", r.get("baz")
57
+ assert_equal "s4", r.get("qux")
58
+ end
59
+
60
+ def test_pipelined_with_an_empty_block
61
+ assert_nothing_raised do
62
+ r.pipelined do
63
+ end
64
+ end
65
+
66
+ assert_equal 0, r.dbsize
67
+ end
68
+
69
+ def test_returning_the_result_of_a_pipeline
70
+ result = r.pipelined do
71
+ r.set "foo", "bar"
72
+ r.get "foo"
73
+ r.get "bar"
74
+ end
75
+
76
+ assert_equal ["OK", "bar", nil], result
77
+ end
78
+
79
+ def test_assignment_of_results_inside_the_block
80
+ r.pipelined do
81
+ @first = r.sadd("foo", 1)
82
+ @second = r.sadd("foo", 1)
83
+ end
84
+
85
+ assert_equal true, @first.value
86
+ assert_equal false, @second.value
87
+ end
88
+
89
+ # Although we could support accessing the values in these futures,
90
+ # it doesn't make a lot of sense.
91
+ def test_assignment_of_results_inside_the_block_with_errors
92
+ assert_raise(Redis2::CommandError) do
93
+ r.pipelined do
94
+ r.doesnt_exist
95
+ @first = r.sadd("foo", 1)
96
+ @second = r.sadd("foo", 1)
97
+ end
98
+ end
99
+
100
+ assert_raise(Redis2::FutureNotReady) { @first.value }
101
+ assert_raise(Redis2::FutureNotReady) { @second.value }
102
+ end
103
+
104
+ def test_assignment_of_results_inside_a_nested_block
105
+ r.pipelined do
106
+ @first = r.sadd("foo", 1)
107
+
108
+ r.pipelined do
109
+ @second = r.sadd("foo", 1)
110
+ end
111
+ end
112
+
113
+ assert_equal true, @first.value
114
+ assert_equal false, @second.value
115
+ end
116
+
117
+ def test_futures_raise_when_confused_with_something_else
118
+ r.pipelined do
119
+ @result = r.sadd("foo", 1)
120
+ end
121
+
122
+ assert_raise(NoMethodError) { @result.to_s }
123
+ end
124
+
125
+ def test_futures_raise_when_trying_to_access_their_values_too_early
126
+ r.pipelined do
127
+ assert_raise(Redis2::FutureNotReady) do
128
+ r.sadd("foo", 1).value
129
+ end
130
+ end
131
+ end
132
+
133
+ def test_futures_can_be_identified
134
+ r.pipelined do
135
+ @result = r.sadd("foo", 1)
136
+ end
137
+
138
+ assert_equal true, @result.is_a?(Redis2::Future)
139
+ if defined?(::BasicObject)
140
+ assert_equal true, @result.is_a?(::BasicObject)
141
+ end
142
+ assert_equal Redis2::Future, @result.class
143
+ end
144
+
145
+ def test_returning_the_result_of_an_empty_pipeline
146
+ result = r.pipelined do
147
+ end
148
+
149
+ assert_equal [], result
150
+ end
151
+
152
+ def test_nesting_pipeline_blocks
153
+ r.pipelined do
154
+ r.set("foo", "s1")
155
+ r.pipelined do
156
+ r.set("bar", "s2")
157
+ end
158
+ end
159
+
160
+ assert_equal "s1", r.get("foo")
161
+ assert_equal "s2", r.get("bar")
162
+ end
163
+
164
+ def test_info_in_a_pipeline_returns_hash
165
+ result = r.pipelined do
166
+ r.info
167
+ end
168
+
169
+ assert result.first.kind_of?(Hash)
170
+ end
171
+
172
+ def test_config_get_in_a_pipeline_returns_hash
173
+ result = r.pipelined do
174
+ r.config(:get, "*")
175
+ end
176
+
177
+ assert result.first.kind_of?(Hash)
178
+ end
179
+
180
+ def test_hgetall_in_a_pipeline_returns_hash
181
+ r.hmset("hash", "field", "value")
182
+ result = r.pipelined do
183
+ r.hgetall("hash")
184
+ end
185
+
186
+ assert_equal result.first, { "field" => "value" }
187
+ end
188
+
189
+ def test_keys_in_a_pipeline
190
+ r.set("key", "value")
191
+ result = r.pipelined do
192
+ r.keys("*")
193
+ end
194
+
195
+ assert_equal ["key"], result.first
196
+ end
197
+
198
+ def test_pipeline_yields_a_connection
199
+ r.pipelined do |p|
200
+ p.set("foo", "bar")
201
+ end
202
+
203
+ assert_equal "bar", r.get("foo")
204
+ end
205
+
206
+ def test_pipeline_select
207
+ r.select 1
208
+ r.set("db", "1")
209
+
210
+ r.pipelined do |p|
211
+ p.select 2
212
+ p.set("db", "2")
213
+ end
214
+
215
+ r.select 1
216
+ assert_equal "1", r.get("db")
217
+
218
+ r.select 2
219
+ assert_equal "2", r.get("db")
220
+ end
221
+
222
+ def test_pipeline_select_client_db
223
+ r.select 1
224
+ r.pipelined do |p2|
225
+ p2.select 2
226
+ end
227
+
228
+ assert_equal 2, r.client.db
229
+ end
230
+
231
+ def test_nested_pipeline_select_client_db
232
+ r.select 1
233
+ r.pipelined do |p2|
234
+ p2.select 2
235
+ p2.pipelined do |p3|
236
+ p3.select 3
237
+ end
238
+ end
239
+
240
+ assert_equal 3, r.client.db
241
+ end
242
+ end
@@ -0,0 +1,210 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("helper", File.dirname(__FILE__))
4
+
5
+ class TestPublishSubscribe < Test::Unit::TestCase
6
+
7
+ include Helper::Client
8
+
9
+ class TestError < StandardError
10
+ end
11
+
12
+ def test_subscribe_and_unsubscribe
13
+ @subscribed = false
14
+ @unsubscribed = false
15
+
16
+ wire = Wire.new do
17
+ r.subscribe("foo") do |on|
18
+ on.subscribe do |channel, total|
19
+ @subscribed = true
20
+ @t1 = total
21
+ end
22
+
23
+ on.message do |channel, message|
24
+ if message == "s1"
25
+ r.unsubscribe
26
+ @message = message
27
+ end
28
+ end
29
+
30
+ on.unsubscribe do |channel, total|
31
+ @unsubscribed = true
32
+ @t2 = total
33
+ end
34
+ end
35
+ end
36
+
37
+ # Wait until the subscription is active before publishing
38
+ Wire.pass while !@subscribed
39
+
40
+ Redis2.new(OPTIONS).publish("foo", "s1")
41
+
42
+ wire.join
43
+
44
+ assert @subscribed
45
+ assert_equal 1, @t1
46
+ assert @unsubscribed
47
+ assert_equal 0, @t2
48
+ assert_equal "s1", @message
49
+ end
50
+
51
+ def test_psubscribe_and_punsubscribe
52
+ @subscribed = false
53
+ @unsubscribed = false
54
+
55
+ wire = Wire.new do
56
+ r.psubscribe("f*") do |on|
57
+ on.psubscribe do |pattern, total|
58
+ @subscribed = true
59
+ @t1 = total
60
+ end
61
+
62
+ on.pmessage do |pattern, channel, message|
63
+ if message == "s1"
64
+ r.punsubscribe
65
+ @message = message
66
+ end
67
+ end
68
+
69
+ on.punsubscribe do |pattern, total|
70
+ @unsubscribed = true
71
+ @t2 = total
72
+ end
73
+ end
74
+ end
75
+
76
+ # Wait until the subscription is active before publishing
77
+ Wire.pass while !@subscribed
78
+
79
+ Redis2.new(OPTIONS).publish("foo", "s1")
80
+
81
+ wire.join
82
+
83
+ assert @subscribed
84
+ assert_equal 1, @t1
85
+ assert @unsubscribed
86
+ assert_equal 0, @t2
87
+ assert_equal "s1", @message
88
+ end
89
+
90
+ def test_subscribe_connection_usable_after_raise
91
+ @subscribed = false
92
+
93
+ wire = Wire.new do
94
+ begin
95
+ r.subscribe("foo") do |on|
96
+ on.subscribe do |channel, total|
97
+ @subscribed = true
98
+ end
99
+
100
+ on.message do |channel, message|
101
+ raise TestError
102
+ end
103
+ end
104
+ rescue TestError
105
+ end
106
+ end
107
+
108
+ # Wait until the subscription is active before publishing
109
+ Wire.pass while !@subscribed
110
+
111
+ Redis2.new(OPTIONS).publish("foo", "s1")
112
+
113
+ wire.join
114
+
115
+ assert_equal "PONG", r.ping
116
+ end
117
+
118
+ def test_psubscribe_connection_usable_after_raise
119
+ @subscribed = false
120
+
121
+ wire = Wire.new do
122
+ begin
123
+ r.psubscribe("f*") do |on|
124
+ on.psubscribe do |pattern, total|
125
+ @subscribed = true
126
+ end
127
+
128
+ on.pmessage do |pattern, channel, message|
129
+ raise TestError
130
+ end
131
+ end
132
+ rescue TestError
133
+ end
134
+ end
135
+
136
+ # Wait until the subscription is active before publishing
137
+ Wire.pass while !@subscribed
138
+
139
+ Redis2.new(OPTIONS).publish("foo", "s1")
140
+
141
+ wire.join
142
+
143
+ assert_equal "PONG", r.ping
144
+ end
145
+
146
+ def test_subscribe_within_subscribe
147
+ @channels = []
148
+
149
+ wire = Wire.new do
150
+ r.subscribe("foo") do |on|
151
+ on.subscribe do |channel, total|
152
+ @channels << channel
153
+
154
+ r.subscribe("bar") if channel == "foo"
155
+ r.unsubscribe if channel == "bar"
156
+ end
157
+ end
158
+ end
159
+
160
+ wire.join
161
+
162
+ assert_equal ["foo", "bar"], @channels
163
+ end
164
+
165
+ def test_other_commands_within_a_subscribe
166
+ assert_raise Redis2::CommandError do
167
+ r.subscribe("foo") do |on|
168
+ on.subscribe do |channel, total|
169
+ r.set("bar", "s2")
170
+ end
171
+ end
172
+ end
173
+ end
174
+
175
+ def test_subscribe_without_a_block
176
+ assert_raise LocalJumpError do
177
+ r.subscribe("foo")
178
+ end
179
+ end
180
+
181
+ def test_unsubscribe_without_a_subscribe
182
+ assert_raise RuntimeError do
183
+ r.unsubscribe
184
+ end
185
+
186
+ assert_raise RuntimeError do
187
+ r.punsubscribe
188
+ end
189
+ end
190
+
191
+ def test_subscribe_past_a_timeout
192
+ # For some reason, a thread here doesn't reproduce the issue.
193
+ sleep = %{sleep #{OPTIONS[:timeout] * 2}}
194
+ publish = %{echo "publish foo bar\r\n" | nc 127.0.0.1 #{OPTIONS[:port]}}
195
+ cmd = [sleep, publish].join("; ")
196
+
197
+ IO.popen(cmd, "r+") do |pipe|
198
+ received = false
199
+
200
+ r.subscribe "foo" do |on|
201
+ on.message do |channel, message|
202
+ received = true
203
+ r.unsubscribe
204
+ end
205
+ end
206
+
207
+ assert received
208
+ end
209
+ end
210
+ end
@@ -0,0 +1,117 @@
1
+ # encoding: UTF-8
2
+
3
+ require File.expand_path("helper", File.dirname(__FILE__))
4
+
5
+ class TestRemoteServerControlCommands < Test::Unit::TestCase
6
+
7
+ include Helper::Client
8
+
9
+ def test_info
10
+ keys = [
11
+ "redis_version",
12
+ "uptime_in_seconds",
13
+ "uptime_in_days",
14
+ "connected_clients",
15
+ "used_memory",
16
+ "total_connections_received",
17
+ "total_commands_processed",
18
+ ]
19
+
20
+ info = r.info
21
+
22
+ keys.each do |k|
23
+ msg = "expected #info to include #{k}"
24
+ assert info.keys.include?(k), msg
25
+ end
26
+ end
27
+
28
+ def test_info_commandstats
29
+ target_version "2.5.7" do
30
+ r.config(:resetstat)
31
+ r.ping
32
+
33
+ result = r.info(:commandstats)
34
+ assert_equal "1", result["ping"]["calls"]
35
+ end
36
+ end
37
+
38
+ def test_monitor_redis_lt_2_5_0
39
+ return unless version < "2.5.0"
40
+
41
+ log = []
42
+
43
+ wire = Wire.new do
44
+ Redis2.new(OPTIONS).monitor do |line|
45
+ log << line
46
+ break if log.size == 3
47
+ end
48
+ end
49
+
50
+ Wire.pass while log.empty? # Faster than sleep
51
+
52
+ r.set "foo", "s1"
53
+
54
+ wire.join
55
+
56
+ assert log[-1][%q{(db 15) "set" "foo" "s1"}]
57
+ end
58
+
59
+ def test_monitor_redis_gte_2_5_0
60
+ return unless version >= "2.5.0"
61
+
62
+ log = []
63
+
64
+ wire = Wire.new do
65
+ Redis2.new(OPTIONS).monitor do |line|
66
+ log << line
67
+ break if line =~ /set/
68
+ end
69
+ end
70
+
71
+ Wire.pass while log.empty? # Faster than sleep
72
+
73
+ r.set "foo", "s1"
74
+
75
+ wire.join
76
+
77
+ assert log[-1] =~ /\b15\b.* "set" "foo" "s1"/
78
+ end
79
+
80
+ def test_monitor_returns_value_for_break
81
+ result = r.monitor do |line|
82
+ break line
83
+ end
84
+
85
+ assert_equal result, "OK"
86
+ end
87
+
88
+ def test_echo
89
+ assert_equal "foo bar baz\n", r.echo("foo bar baz\n")
90
+ end
91
+
92
+ def test_debug
93
+ r.set "foo", "s1"
94
+
95
+ assert r.debug(:object, "foo").kind_of?(String)
96
+ end
97
+
98
+ def test_object
99
+ r.lpush "list", "value"
100
+
101
+ assert_equal r.object(:refcount, "list"), 1
102
+ assert_equal r.object(:encoding, "list"), "ziplist"
103
+ assert r.object(:idletime, "list").kind_of?(Fixnum)
104
+ end
105
+
106
+ def test_sync
107
+ redis_mock(:sync => lambda { "+OK" }) do |redis|
108
+ assert_equal "OK", redis.sync
109
+ end
110
+ end
111
+
112
+ def test_slowlog
113
+ r.slowlog(:reset)
114
+ result = r.slowlog(:len)
115
+ assert_equal result, 0
116
+ end
117
+ end