redis 3.2.2 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +175 -13
  3. data/README.md +223 -76
  4. data/lib/redis.rb +1360 -445
  5. data/lib/redis/client.rb +183 -103
  6. data/lib/redis/cluster.rb +291 -0
  7. data/lib/redis/cluster/command.rb +81 -0
  8. data/lib/redis/cluster/command_loader.rb +34 -0
  9. data/lib/redis/cluster/key_slot_converter.rb +72 -0
  10. data/lib/redis/cluster/node.rb +108 -0
  11. data/lib/redis/cluster/node_key.rb +31 -0
  12. data/lib/redis/cluster/node_loader.rb +37 -0
  13. data/lib/redis/cluster/option.rb +93 -0
  14. data/lib/redis/cluster/slot.rb +86 -0
  15. data/lib/redis/cluster/slot_loader.rb +49 -0
  16. data/lib/redis/connection.rb +4 -2
  17. data/lib/redis/connection/command_helper.rb +5 -10
  18. data/lib/redis/connection/hiredis.rb +9 -6
  19. data/lib/redis/connection/registry.rb +2 -1
  20. data/lib/redis/connection/ruby.rb +168 -63
  21. data/lib/redis/connection/synchrony.rb +29 -7
  22. data/lib/redis/distributed.rb +156 -74
  23. data/lib/redis/errors.rb +48 -0
  24. data/lib/redis/hash_ring.rb +30 -73
  25. data/lib/redis/pipeline.rb +55 -15
  26. data/lib/redis/subscribe.rb +20 -13
  27. data/lib/redis/version.rb +3 -1
  28. metadata +41 -170
  29. data/.gitignore +0 -16
  30. data/.travis.yml +0 -59
  31. data/.travis/Gemfile +0 -11
  32. data/.yardopts +0 -3
  33. data/Gemfile +0 -4
  34. data/Rakefile +0 -87
  35. data/benchmarking/logging.rb +0 -71
  36. data/benchmarking/pipeline.rb +0 -51
  37. data/benchmarking/speed.rb +0 -21
  38. data/benchmarking/suite.rb +0 -24
  39. data/benchmarking/worker.rb +0 -71
  40. data/examples/basic.rb +0 -15
  41. data/examples/consistency.rb +0 -114
  42. data/examples/dist_redis.rb +0 -43
  43. data/examples/incr-decr.rb +0 -17
  44. data/examples/list.rb +0 -26
  45. data/examples/pubsub.rb +0 -37
  46. data/examples/sentinel.rb +0 -41
  47. data/examples/sentinel/sentinel.conf +0 -9
  48. data/examples/sentinel/start +0 -49
  49. data/examples/sets.rb +0 -36
  50. data/examples/unicorn/config.ru +0 -3
  51. data/examples/unicorn/unicorn.rb +0 -20
  52. data/redis.gemspec +0 -44
  53. data/test/bitpos_test.rb +0 -69
  54. data/test/blocking_commands_test.rb +0 -42
  55. data/test/command_map_test.rb +0 -30
  56. data/test/commands_on_hashes_test.rb +0 -21
  57. data/test/commands_on_hyper_log_log_test.rb +0 -21
  58. data/test/commands_on_lists_test.rb +0 -20
  59. data/test/commands_on_sets_test.rb +0 -77
  60. data/test/commands_on_sorted_sets_test.rb +0 -137
  61. data/test/commands_on_strings_test.rb +0 -101
  62. data/test/commands_on_value_types_test.rb +0 -133
  63. data/test/connection_handling_test.rb +0 -250
  64. data/test/db/.gitkeep +0 -0
  65. data/test/distributed_blocking_commands_test.rb +0 -46
  66. data/test/distributed_commands_on_hashes_test.rb +0 -10
  67. data/test/distributed_commands_on_hyper_log_log_test.rb +0 -33
  68. data/test/distributed_commands_on_lists_test.rb +0 -22
  69. data/test/distributed_commands_on_sets_test.rb +0 -83
  70. data/test/distributed_commands_on_sorted_sets_test.rb +0 -18
  71. data/test/distributed_commands_on_strings_test.rb +0 -59
  72. data/test/distributed_commands_on_value_types_test.rb +0 -95
  73. data/test/distributed_commands_requiring_clustering_test.rb +0 -164
  74. data/test/distributed_connection_handling_test.rb +0 -23
  75. data/test/distributed_internals_test.rb +0 -79
  76. data/test/distributed_key_tags_test.rb +0 -52
  77. data/test/distributed_persistence_control_commands_test.rb +0 -26
  78. data/test/distributed_publish_subscribe_test.rb +0 -92
  79. data/test/distributed_remote_server_control_commands_test.rb +0 -66
  80. data/test/distributed_scripting_test.rb +0 -102
  81. data/test/distributed_sorting_test.rb +0 -20
  82. data/test/distributed_test.rb +0 -58
  83. data/test/distributed_transactions_test.rb +0 -32
  84. data/test/encoding_test.rb +0 -18
  85. data/test/error_replies_test.rb +0 -59
  86. data/test/fork_safety_test.rb +0 -65
  87. data/test/helper.rb +0 -232
  88. data/test/helper_test.rb +0 -24
  89. data/test/internals_test.rb +0 -437
  90. data/test/lint/blocking_commands.rb +0 -150
  91. data/test/lint/hashes.rb +0 -162
  92. data/test/lint/hyper_log_log.rb +0 -60
  93. data/test/lint/lists.rb +0 -143
  94. data/test/lint/sets.rb +0 -125
  95. data/test/lint/sorted_sets.rb +0 -316
  96. data/test/lint/strings.rb +0 -260
  97. data/test/lint/value_types.rb +0 -122
  98. data/test/persistence_control_commands_test.rb +0 -26
  99. data/test/pipelining_commands_test.rb +0 -242
  100. data/test/publish_subscribe_test.rb +0 -254
  101. data/test/remote_server_control_commands_test.rb +0 -118
  102. data/test/scanning_test.rb +0 -413
  103. data/test/scripting_test.rb +0 -78
  104. data/test/sentinel_command_test.rb +0 -80
  105. data/test/sentinel_test.rb +0 -255
  106. data/test/sorting_test.rb +0 -59
  107. data/test/support/connection/hiredis.rb +0 -1
  108. data/test/support/connection/ruby.rb +0 -1
  109. data/test/support/connection/synchrony.rb +0 -17
  110. data/test/support/redis_mock.rb +0 -119
  111. data/test/support/wire/synchrony.rb +0 -24
  112. data/test/support/wire/thread.rb +0 -5
  113. data/test/synchrony_driver.rb +0 -88
  114. data/test/test.conf.erb +0 -9
  115. data/test/thread_safety_test.rb +0 -32
  116. data/test/transactions_test.rb +0 -264
  117. data/test/unknown_commands_test.rb +0 -14
  118. data/test/url_param_test.rb +0 -138
data/test/helper_test.rb DELETED
@@ -1,24 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- require File.expand_path("helper", File.dirname(__FILE__))
4
-
5
- class TestHelper < Test::Unit::TestCase
6
-
7
- include Helper
8
-
9
- def test_version_comparison
10
- v = Version.new("2.0.1")
11
-
12
- assert v > "1"
13
- assert v > "2"
14
- assert v < "3"
15
- assert v < "10"
16
-
17
- assert v < "2.1"
18
- assert v < "2.0.2"
19
- assert v < "2.0.1.1"
20
- assert v < "2.0.10"
21
-
22
- assert v == "2.0.1"
23
- end
24
- end
@@ -1,437 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- require File.expand_path("helper", File.dirname(__FILE__))
4
-
5
- class TestInternals < Test::Unit::TestCase
6
-
7
- include Helper::Client
8
-
9
- def test_logger
10
- r.ping
11
-
12
- assert log.string["[Redis] command=PING"]
13
- assert log.string =~ /\[Redis\] call_time=\d+\.\d+ ms/
14
- end
15
-
16
- def test_logger_with_pipelining
17
- r.pipelined do
18
- r.set "foo", "bar"
19
- r.get "foo"
20
- end
21
-
22
- assert log.string[" command=SET args=\"foo\" \"bar\""]
23
- assert log.string[" command=GET args=\"foo\""]
24
- end
25
-
26
- def test_recovers_from_failed_commands
27
- # See https://github.com/redis/redis-rb/issues#issue/28
28
-
29
- assert_raise(Redis::CommandError) do
30
- r.command_that_doesnt_exist
31
- end
32
-
33
- assert_nothing_raised do
34
- r.info
35
- end
36
- end
37
-
38
- def test_raises_on_protocol_errors
39
- redis_mock(:ping => lambda { |*_| "foo" }) do |redis|
40
- assert_raise(Redis::ProtocolError) do
41
- redis.ping
42
- end
43
- end
44
- end
45
-
46
- def test_provides_a_meaningful_inspect
47
- assert_equal "#<Redis client v#{Redis::VERSION} for redis://127.0.0.1:#{PORT}/15>", r.inspect
48
- end
49
-
50
- def test_redis_current
51
- assert_equal "127.0.0.1", Redis.current.client.host
52
- assert_equal 6379, Redis.current.client.port
53
- assert_equal 0, Redis.current.client.db
54
-
55
- Redis.current = Redis.new(OPTIONS.merge(:port => 6380, :db => 1))
56
-
57
- t = Thread.new do
58
- assert_equal "127.0.0.1", Redis.current.client.host
59
- assert_equal 6380, Redis.current.client.port
60
- assert_equal 1, Redis.current.client.db
61
- end
62
-
63
- t.join
64
-
65
- assert_equal "127.0.0.1", Redis.current.client.host
66
- assert_equal 6380, Redis.current.client.port
67
- assert_equal 1, Redis.current.client.db
68
- end
69
-
70
- def test_redis_connected?
71
- fresh_client = _new_client
72
- assert !fresh_client.connected?
73
-
74
- fresh_client.ping
75
- assert fresh_client.connected?
76
-
77
- fresh_client.quit
78
- assert !fresh_client.connected?
79
- end
80
-
81
- def test_default_id_with_host_and_port
82
- redis = Redis.new(OPTIONS.merge(:host => "host", :port => "1234", :db => 0))
83
- assert_equal "redis://host:1234/0", redis.client.id
84
- end
85
-
86
- def test_default_id_with_host_and_port_and_explicit_scheme
87
- redis = Redis.new(OPTIONS.merge(:host => "host", :port => "1234", :db => 0, :scheme => "foo"))
88
- assert_equal "redis://host:1234/0", redis.client.id
89
- end
90
-
91
- def test_default_id_with_path
92
- redis = Redis.new(OPTIONS.merge(:path => "/tmp/redis.sock", :db => 0))
93
- assert_equal "redis:///tmp/redis.sock/0", redis.client.id
94
- end
95
-
96
- def test_default_id_with_path_and_explicit_scheme
97
- redis = Redis.new(OPTIONS.merge(:path => "/tmp/redis.sock", :db => 0, :scheme => "foo"))
98
- assert_equal "redis:///tmp/redis.sock/0", redis.client.id
99
- end
100
-
101
- def test_override_id
102
- redis = Redis.new(OPTIONS.merge(:id => "test"))
103
- assert_equal redis.client.id, "test"
104
- end
105
-
106
- def test_timeout
107
- assert_nothing_raised do
108
- Redis.new(OPTIONS.merge(:timeout => 0))
109
- end
110
- end
111
-
112
- def test_id_inside_multi
113
- redis = Redis.new(OPTIONS)
114
- id = nil
115
-
116
- redis.multi do
117
- id = redis.id
118
- end
119
-
120
- assert_equal id, "redis://127.0.0.1:6381/15"
121
- end
122
-
123
- driver(:ruby) do
124
- def test_tcp_keepalive
125
- keepalive = {:time => 20, :intvl => 10, :probes => 5}
126
-
127
- redis = Redis.new(OPTIONS.merge(:tcp_keepalive => keepalive))
128
- redis.ping
129
-
130
- connection = redis.client.connection
131
- actual_keepalive = connection.get_tcp_keepalive
132
-
133
- [:time, :intvl, :probes].each do |key|
134
- if actual_keepalive.has_key?(key)
135
- assert_equal actual_keepalive[key], keepalive[key]
136
- end
137
- end
138
- end
139
- end
140
-
141
- def test_time
142
- target_version "2.5.4" do
143
- # Test that the difference between the time that Ruby reports and the time
144
- # that Redis reports is minimal (prevents the test from being racy).
145
- rv = r.time
146
-
147
- redis_usec = rv[0] * 1_000_000 + rv[1]
148
- ruby_usec = Integer(Time.now.to_f * 1_000_000)
149
-
150
- assert 500_000 > (ruby_usec - redis_usec).abs
151
- end
152
- end
153
-
154
- def test_connection_timeout
155
- opts = OPTIONS.merge(:host => "10.255.255.254", :connect_timeout => 0.1, :timeout => 5.0)
156
- start_time = Time.now
157
- assert_raise Redis::CannotConnectError do
158
- Redis.new(opts).ping
159
- end
160
- assert (Time.now - start_time) <= opts[:timeout]
161
- end
162
-
163
- def close_on_ping(seq, options = {})
164
- $request = 0
165
-
166
- command = lambda do
167
- idx = $request
168
- $request += 1
169
-
170
- rv = "+%d" % idx
171
- rv = nil if seq.include?(idx)
172
- rv
173
- end
174
-
175
- redis_mock({:ping => command}, {:timeout => 0.1}.merge(options)) do |redis|
176
- yield(redis)
177
- end
178
- end
179
-
180
- def test_retry_by_default
181
- close_on_ping([0]) do |redis|
182
- assert_equal "1", redis.ping
183
- end
184
- end
185
-
186
- def test_retry_when_wrapped_in_with_reconnect_true
187
- close_on_ping([0]) do |redis|
188
- redis.with_reconnect(true) do
189
- assert_equal "1", redis.ping
190
- end
191
- end
192
- end
193
-
194
- def test_dont_retry_when_wrapped_in_with_reconnect_false
195
- close_on_ping([0]) do |redis|
196
- assert_raise Redis::ConnectionError do
197
- redis.with_reconnect(false) do
198
- redis.ping
199
- end
200
- end
201
- end
202
- end
203
-
204
- def test_dont_retry_when_wrapped_in_without_reconnect
205
- close_on_ping([0]) do |redis|
206
- assert_raise Redis::ConnectionError do
207
- redis.without_reconnect do
208
- redis.ping
209
- end
210
- end
211
- end
212
- end
213
-
214
- def test_retry_only_once_when_read_raises_econnreset
215
- close_on_ping([0, 1]) do |redis|
216
- assert_raise Redis::ConnectionError do
217
- redis.ping
218
- end
219
-
220
- assert !redis.client.connected?
221
- end
222
- end
223
-
224
- def test_retry_with_custom_reconnect_attempts
225
- close_on_ping([0, 1], :reconnect_attempts => 2) do |redis|
226
- assert_equal "2", redis.ping
227
- end
228
- end
229
-
230
- def test_retry_with_custom_reconnect_attempts_can_still_fail
231
- close_on_ping([0, 1, 2], :reconnect_attempts => 2) do |redis|
232
- assert_raise Redis::ConnectionError do
233
- redis.ping
234
- end
235
-
236
- assert !redis.client.connected?
237
- end
238
- end
239
-
240
- def test_don_t_retry_when_second_read_in_pipeline_raises_econnreset
241
- close_on_ping([1]) do |redis|
242
- assert_raise Redis::ConnectionError do
243
- redis.pipelined do
244
- redis.ping
245
- redis.ping # Second #read times out
246
- end
247
- end
248
-
249
- assert !redis.client.connected?
250
- end
251
- end
252
-
253
- def close_on_connection(seq)
254
- $n = 0
255
-
256
- read_command = lambda do |session|
257
- Array.new(session.gets[1..-3].to_i) do
258
- bytes = session.gets[1..-3].to_i
259
- arg = session.read(bytes)
260
- session.read(2) # Discard \r\n
261
- arg
262
- end
263
- end
264
-
265
- handler = lambda do |session|
266
- n = $n
267
- $n += 1
268
-
269
- select = read_command.call(session)
270
- if select[0].downcase == "select"
271
- session.write("+OK\r\n")
272
- else
273
- raise "Expected SELECT"
274
- end
275
-
276
- if !seq.include?(n)
277
- while read_command.call(session)
278
- session.write("+#{n}\r\n")
279
- end
280
- end
281
- end
282
-
283
- redis_mock_with_handler(handler) do |redis|
284
- yield(redis)
285
- end
286
- end
287
-
288
- def test_retry_on_write_error_by_default
289
- close_on_connection([0]) do |redis|
290
- assert_equal "1", redis.client.call(["x" * 128 * 1024])
291
- end
292
- end
293
-
294
- def test_retry_on_write_error_when_wrapped_in_with_reconnect_true
295
- close_on_connection([0]) do |redis|
296
- redis.with_reconnect(true) do
297
- assert_equal "1", redis.client.call(["x" * 128 * 1024])
298
- end
299
- end
300
- end
301
-
302
- def test_dont_retry_on_write_error_when_wrapped_in_with_reconnect_false
303
- close_on_connection([0]) do |redis|
304
- assert_raise Redis::ConnectionError do
305
- redis.with_reconnect(false) do
306
- redis.client.call(["x" * 128 * 1024])
307
- end
308
- end
309
- end
310
- end
311
-
312
- def test_dont_retry_on_write_error_when_wrapped_in_without_reconnect
313
- close_on_connection([0]) do |redis|
314
- assert_raise Redis::ConnectionError do
315
- redis.without_reconnect do
316
- redis.client.call(["x" * 128 * 1024])
317
- end
318
- end
319
- end
320
- end
321
-
322
- def test_connecting_to_unix_domain_socket
323
- assert_nothing_raised do
324
- Redis.new(OPTIONS.merge(:path => "./test/db/redis.sock")).ping
325
- end
326
- end
327
-
328
- driver(:ruby, :hiredis) do
329
- def test_bubble_timeout_without_retrying
330
- serv = TCPServer.new(6380)
331
-
332
- redis = Redis.new(:port => 6380, :timeout => 0.1)
333
-
334
- assert_raise(Redis::TimeoutError) do
335
- redis.ping
336
- end
337
-
338
- ensure
339
- serv.close if serv
340
- end
341
- end
342
-
343
- def test_client_options
344
- redis = Redis.new(OPTIONS.merge(:host => "host", :port => 1234, :db => 1, :scheme => "foo"))
345
-
346
- assert_equal "host", redis.client.options[:host]
347
- assert_equal 1234, redis.client.options[:port]
348
- assert_equal 1, redis.client.options[:db]
349
- assert_equal "foo", redis.client.options[:scheme]
350
- end
351
-
352
- def test_does_not_change_self_client_options
353
- redis = Redis.new(OPTIONS.merge(:host => "host", :port => 1234, :db => 1, :scheme => "foo"))
354
- options = redis.client.options
355
-
356
- options[:host] << "new_host"
357
- options[:scheme] << "bar"
358
- options.merge!(:db => 0)
359
-
360
- assert_equal "host", redis.client.options[:host]
361
- assert_equal 1, redis.client.options[:db]
362
- assert_equal "foo", redis.client.options[:scheme]
363
- end
364
-
365
- def test_resolves_localhost
366
- assert_nothing_raised do
367
- Redis.new(OPTIONS.merge(:host => 'localhost')).ping
368
- end
369
- end
370
-
371
- class << self
372
- def af_family_supported(af)
373
- hosts = {
374
- Socket::AF_INET => "127.0.0.1",
375
- Socket::AF_INET6 => "::1",
376
- }
377
-
378
- begin
379
- s = Socket.new(af, Socket::SOCK_STREAM, 0)
380
- begin
381
- tries = 5
382
- begin
383
- sa = Socket.pack_sockaddr_in(1024 + Random.rand(63076), hosts[af])
384
- s.bind(sa)
385
- rescue Errno::EADDRINUSE
386
- tries -= 1
387
- retry if tries > 0
388
-
389
- raise
390
- end
391
- yield
392
- rescue Errno::EADDRNOTAVAIL
393
- ensure
394
- s.close
395
- end
396
- rescue Errno::ESOCKTNOSUPPORT
397
- end
398
- end
399
- end
400
-
401
- def af_test(host)
402
- commands = {
403
- :ping => lambda { |*_| "+pong" },
404
- }
405
-
406
- redis_mock(commands, :host => host) do |redis|
407
- assert_nothing_raised do
408
- redis.ping
409
- end
410
- end
411
- end
412
-
413
- driver(:ruby) do
414
- af_family_supported(Socket::AF_INET) do
415
- def test_connect_ipv4
416
- af_test("127.0.0.1")
417
- end
418
- end
419
- end
420
-
421
- driver(:ruby) do
422
- af_family_supported(Socket::AF_INET6) do
423
- def test_connect_ipv6
424
- af_test("::1")
425
- end
426
- end
427
- end
428
-
429
- def test_can_be_duped_to_create_a_new_connection
430
- clients = r.info["connected_clients"].to_i
431
-
432
- r2 = r.dup
433
- r2.ping
434
-
435
- assert_equal clients + 1, r.info["connected_clients"].to_i
436
- end
437
- end