redis 3.3.5 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +54 -2
  3. data/README.md +77 -76
  4. data/lib/redis.rb +779 -63
  5. data/lib/redis/client.rb +41 -20
  6. data/lib/redis/cluster.rb +286 -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 +104 -0
  11. data/lib/redis/cluster/node_key.rb +35 -0
  12. data/lib/redis/cluster/node_loader.rb +37 -0
  13. data/lib/redis/cluster/option.rb +77 -0
  14. data/lib/redis/cluster/slot.rb +69 -0
  15. data/lib/redis/cluster/slot_loader.rb +49 -0
  16. data/lib/redis/connection.rb +2 -2
  17. data/lib/redis/connection/command_helper.rb +2 -8
  18. data/lib/redis/connection/hiredis.rb +2 -2
  19. data/lib/redis/connection/ruby.rb +13 -30
  20. data/lib/redis/connection/synchrony.rb +12 -4
  21. data/lib/redis/distributed.rb +32 -12
  22. data/lib/redis/errors.rb +46 -0
  23. data/lib/redis/hash_ring.rb +20 -64
  24. data/lib/redis/pipeline.rb +9 -7
  25. data/lib/redis/version.rb +1 -1
  26. metadata +53 -196
  27. data/.gitignore +0 -16
  28. data/.travis.yml +0 -89
  29. data/.travis/Gemfile +0 -11
  30. data/.yardopts +0 -3
  31. data/Gemfile +0 -4
  32. data/Rakefile +0 -87
  33. data/benchmarking/logging.rb +0 -71
  34. data/benchmarking/pipeline.rb +0 -51
  35. data/benchmarking/speed.rb +0 -21
  36. data/benchmarking/suite.rb +0 -24
  37. data/benchmarking/worker.rb +0 -71
  38. data/examples/basic.rb +0 -15
  39. data/examples/consistency.rb +0 -114
  40. data/examples/dist_redis.rb +0 -43
  41. data/examples/incr-decr.rb +0 -17
  42. data/examples/list.rb +0 -26
  43. data/examples/pubsub.rb +0 -37
  44. data/examples/sentinel.rb +0 -41
  45. data/examples/sentinel/start +0 -49
  46. data/examples/sets.rb +0 -36
  47. data/examples/unicorn/config.ru +0 -3
  48. data/examples/unicorn/unicorn.rb +0 -20
  49. data/redis.gemspec +0 -44
  50. data/test/bitpos_test.rb +0 -69
  51. data/test/blocking_commands_test.rb +0 -42
  52. data/test/client_test.rb +0 -59
  53. data/test/command_map_test.rb +0 -30
  54. data/test/commands_on_hashes_test.rb +0 -21
  55. data/test/commands_on_hyper_log_log_test.rb +0 -21
  56. data/test/commands_on_lists_test.rb +0 -20
  57. data/test/commands_on_sets_test.rb +0 -77
  58. data/test/commands_on_sorted_sets_test.rb +0 -137
  59. data/test/commands_on_strings_test.rb +0 -101
  60. data/test/commands_on_value_types_test.rb +0 -133
  61. data/test/connection_handling_test.rb +0 -277
  62. data/test/connection_test.rb +0 -57
  63. data/test/distributed_blocking_commands_test.rb +0 -46
  64. data/test/distributed_commands_on_hashes_test.rb +0 -10
  65. data/test/distributed_commands_on_hyper_log_log_test.rb +0 -33
  66. data/test/distributed_commands_on_lists_test.rb +0 -22
  67. data/test/distributed_commands_on_sets_test.rb +0 -83
  68. data/test/distributed_commands_on_sorted_sets_test.rb +0 -18
  69. data/test/distributed_commands_on_strings_test.rb +0 -59
  70. data/test/distributed_commands_on_value_types_test.rb +0 -95
  71. data/test/distributed_commands_requiring_clustering_test.rb +0 -164
  72. data/test/distributed_connection_handling_test.rb +0 -23
  73. data/test/distributed_internals_test.rb +0 -79
  74. data/test/distributed_key_tags_test.rb +0 -52
  75. data/test/distributed_persistence_control_commands_test.rb +0 -26
  76. data/test/distributed_publish_subscribe_test.rb +0 -92
  77. data/test/distributed_remote_server_control_commands_test.rb +0 -66
  78. data/test/distributed_scripting_test.rb +0 -102
  79. data/test/distributed_sorting_test.rb +0 -20
  80. data/test/distributed_test.rb +0 -58
  81. data/test/distributed_transactions_test.rb +0 -32
  82. data/test/encoding_test.rb +0 -18
  83. data/test/error_replies_test.rb +0 -59
  84. data/test/fork_safety_test.rb +0 -65
  85. data/test/helper.rb +0 -232
  86. data/test/helper_test.rb +0 -24
  87. data/test/internals_test.rb +0 -417
  88. data/test/lint/blocking_commands.rb +0 -150
  89. data/test/lint/hashes.rb +0 -162
  90. data/test/lint/hyper_log_log.rb +0 -60
  91. data/test/lint/lists.rb +0 -143
  92. data/test/lint/sets.rb +0 -140
  93. data/test/lint/sorted_sets.rb +0 -316
  94. data/test/lint/strings.rb +0 -260
  95. data/test/lint/value_types.rb +0 -122
  96. data/test/persistence_control_commands_test.rb +0 -26
  97. data/test/pipelining_commands_test.rb +0 -242
  98. data/test/publish_subscribe_test.rb +0 -282
  99. data/test/remote_server_control_commands_test.rb +0 -118
  100. data/test/scanning_test.rb +0 -413
  101. data/test/scripting_test.rb +0 -78
  102. data/test/sentinel_command_test.rb +0 -80
  103. data/test/sentinel_test.rb +0 -255
  104. data/test/sorting_test.rb +0 -59
  105. data/test/ssl_test.rb +0 -73
  106. data/test/support/connection/hiredis.rb +0 -1
  107. data/test/support/connection/ruby.rb +0 -1
  108. data/test/support/connection/synchrony.rb +0 -17
  109. data/test/support/redis_mock.rb +0 -130
  110. data/test/support/ssl/gen_certs.sh +0 -31
  111. data/test/support/ssl/trusted-ca.crt +0 -25
  112. data/test/support/ssl/trusted-ca.key +0 -27
  113. data/test/support/ssl/trusted-cert.crt +0 -81
  114. data/test/support/ssl/trusted-cert.key +0 -28
  115. data/test/support/ssl/untrusted-ca.crt +0 -26
  116. data/test/support/ssl/untrusted-ca.key +0 -27
  117. data/test/support/ssl/untrusted-cert.crt +0 -82
  118. data/test/support/ssl/untrusted-cert.key +0 -28
  119. data/test/support/wire/synchrony.rb +0 -24
  120. data/test/support/wire/thread.rb +0 -5
  121. data/test/synchrony_driver.rb +0 -88
  122. data/test/test.conf.erb +0 -9
  123. data/test/thread_safety_test.rb +0 -62
  124. data/test/transactions_test.rb +0 -264
  125. data/test/unknown_commands_test.rb +0 -14
  126. data/test/url_param_test.rb +0 -138
@@ -1,32 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- require File.expand_path("helper", File.dirname(__FILE__))
4
-
5
- class TestDistributedTransactions < Test::Unit::TestCase
6
-
7
- include Helper::Distributed
8
-
9
- def test_multi_discard
10
- @foo = nil
11
-
12
- assert_raise Redis::Distributed::CannotDistribute do
13
- r.multi { @foo = 1 }
14
- end
15
-
16
- assert_equal nil, @foo
17
-
18
- assert_raise Redis::Distributed::CannotDistribute do
19
- r.discard
20
- end
21
- end
22
-
23
- def test_watch_unwatch
24
- assert_raise Redis::Distributed::CannotDistribute do
25
- r.watch("foo")
26
- end
27
-
28
- assert_raise Redis::Distributed::CannotDistribute do
29
- r.unwatch
30
- end
31
- end
32
- end
@@ -1,18 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- require File.expand_path("helper", File.dirname(__FILE__))
4
-
5
- class TestEncoding < Test::Unit::TestCase
6
-
7
- include Helper::Client
8
-
9
- def test_returns_properly_encoded_strings
10
- if defined?(Encoding)
11
- with_external_encoding("UTF-8") do
12
- r.set "foo", "שלום"
13
-
14
- assert_equal "Shalom שלום", "Shalom " + r.get("foo")
15
- end
16
- end
17
- end
18
- end
@@ -1,59 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- require File.expand_path("helper", File.dirname(__FILE__))
4
-
5
- class TestErrorReplies < Test::Unit::TestCase
6
-
7
- include Helper::Client
8
-
9
- # Every test shouldn't disconnect from the server. Also, when error replies are
10
- # in play, the protocol should never get into an invalid state where there are
11
- # pending replies in the connection. Calling INFO after every test ensures that
12
- # the protocol is still in a valid state.
13
- def with_reconnection_check
14
- before = r.info["total_connections_received"]
15
- yield(r)
16
- after = r.info["total_connections_received"]
17
- ensure
18
- assert_equal before, after
19
- end
20
-
21
- def test_error_reply_for_single_command
22
- with_reconnection_check do
23
- begin
24
- r.unknown_command
25
- rescue => ex
26
- ensure
27
- assert ex.message =~ /unknown command/i
28
- end
29
- end
30
- end
31
-
32
- def test_raise_first_error_reply_in_pipeline
33
- with_reconnection_check do
34
- begin
35
- r.pipelined do
36
- r.set("foo", "s1")
37
- r.incr("foo") # not an integer
38
- r.lpush("foo", "value") # wrong kind of value
39
- end
40
- rescue => ex
41
- ensure
42
- assert ex.message =~ /not an integer/i
43
- end
44
- end
45
- end
46
-
47
- def test_recover_from_raise_in__call_loop
48
- with_reconnection_check do
49
- begin
50
- r.client.call_loop([:invalid_monitor]) do
51
- assert false # Should never be executed
52
- end
53
- rescue => ex
54
- ensure
55
- assert ex.message =~ /unknown command/i
56
- end
57
- end
58
- end
59
- end
@@ -1,65 +0,0 @@
1
- # encoding: UTF-8
2
-
3
- require File.expand_path("helper", File.dirname(__FILE__))
4
-
5
- class TestForkSafety < Test::Unit::TestCase
6
-
7
- include Helper::Client
8
- include Helper::Skipable
9
-
10
- driver(:ruby, :hiredis) do
11
- def test_fork_safety
12
- redis = Redis.new(OPTIONS)
13
- redis.set "foo", 1
14
-
15
- child_pid = fork do
16
- begin
17
- # InheritedError triggers a reconnect,
18
- # so we need to disable reconnects to force
19
- # the exception bubble up
20
- redis.without_reconnect do
21
- redis.set "foo", 2
22
- end
23
- rescue Redis::InheritedError
24
- exit 127
25
- end
26
- end
27
-
28
- _, status = Process.wait2(child_pid)
29
-
30
- assert_equal 127, status.exitstatus
31
- assert_equal "1", redis.get("foo")
32
-
33
- rescue NotImplementedError => error
34
- raise unless error.message =~ /fork is not available/
35
- return skip(error.message)
36
- end
37
-
38
- def test_fork_safety_with_enabled_inherited_socket
39
- redis = Redis.new(OPTIONS.merge(:inherit_socket => true))
40
- redis.set "foo", 1
41
-
42
- child_pid = fork do
43
- begin
44
- # InheritedError triggers a reconnect,
45
- # so we need to disable reconnects to force
46
- # the exception bubble up
47
- redis.without_reconnect do
48
- redis.set "foo", 2
49
- end
50
- rescue Redis::InheritedError
51
- exit 127
52
- end
53
- end
54
-
55
- _, status = Process.wait2(child_pid)
56
-
57
- assert_equal 0, status.exitstatus
58
- assert_equal "2", redis.get("foo")
59
-
60
- rescue NotImplementedError => error
61
- raise unless error.message =~ /fork is not available/
62
- return skip(error.message)
63
- end
64
- end
65
- end
@@ -1,232 +0,0 @@
1
- $:.unshift File.expand_path("../lib", File.dirname(__FILE__))
2
- $:.unshift File.expand_path(File.dirname(__FILE__))
3
-
4
- require "test/unit"
5
- require "logger"
6
- require "stringio"
7
-
8
- (class Random; def self.rand(*args) super end; end) unless defined?(Random)
9
-
10
- begin
11
- require "ruby-debug"
12
- rescue LoadError
13
- end
14
-
15
- $VERBOSE = true
16
-
17
- ENV["conn"] ||= "ruby"
18
-
19
- require "redis"
20
- require "redis/distributed"
21
- require "redis/connection/#{ENV["conn"]}"
22
-
23
- require "support/redis_mock"
24
- require "support/connection/#{ENV["conn"]}"
25
-
26
- PORT = 6381
27
- OPTIONS = {:port => PORT, :db => 15, :timeout => Float(ENV["TIMEOUT"] || 0.1)}
28
- NODES = ["redis://127.0.0.1:#{PORT}/15"]
29
-
30
- def init(redis)
31
- begin
32
- redis.select 14
33
- redis.flushdb
34
- redis.select 15
35
- redis.flushdb
36
- redis
37
- rescue Redis::CannotConnectError
38
- puts <<-EOS
39
-
40
- Cannot connect to Redis.
41
-
42
- Make sure Redis is running on localhost, port #{PORT}.
43
- This testing suite connects to the database 15.
44
-
45
- Try this once:
46
-
47
- $ rake clean
48
-
49
- Then run the build again:
50
-
51
- $ rake
52
-
53
- EOS
54
- exit 1
55
- end
56
- end
57
-
58
- def driver(*drivers, &blk)
59
- if drivers.map(&:to_s).include?(ENV["conn"])
60
- class_eval(&blk)
61
- end
62
- end
63
-
64
- module Helper
65
-
66
- def run(runner)
67
- if respond_to?(:around)
68
- around { super(runner) }
69
- else
70
- super
71
- end
72
- end
73
-
74
- def silent
75
- verbose, $VERBOSE = $VERBOSE, false
76
-
77
- begin
78
- yield
79
- ensure
80
- $VERBOSE = verbose
81
- end
82
- end
83
-
84
- def with_external_encoding(encoding)
85
- original_encoding = Encoding.default_external
86
-
87
- begin
88
- silent { Encoding.default_external = Encoding.find(encoding) }
89
- yield
90
- ensure
91
- silent { Encoding.default_external = original_encoding }
92
- end
93
- end
94
-
95
- def try_encoding(encoding, &block)
96
- if defined?(Encoding)
97
- with_external_encoding(encoding, &block)
98
- else
99
- yield
100
- end
101
- end
102
-
103
- class Version
104
-
105
- include Comparable
106
-
107
- attr :parts
108
-
109
- def initialize(v)
110
- case v
111
- when Version
112
- @parts = v.parts
113
- else
114
- @parts = v.to_s.split(".")
115
- end
116
- end
117
-
118
- def <=>(other)
119
- other = Version.new(other)
120
- length = [self.parts.length, other.parts.length].max
121
- length.times do |i|
122
- a, b = self.parts[i], other.parts[i]
123
-
124
- return -1 if a.nil?
125
- return +1 if b.nil?
126
- return a.to_i <=> b.to_i if a != b
127
- end
128
-
129
- 0
130
- end
131
- end
132
-
133
- module Generic
134
-
135
- include Helper
136
-
137
- attr_reader :log
138
- attr_reader :redis
139
-
140
- alias :r :redis
141
-
142
- def setup
143
- @log = StringIO.new
144
- @redis = init _new_client
145
-
146
- # Run GC to make sure orphaned connections are closed.
147
- GC.start
148
- end
149
-
150
- def teardown
151
- @redis.quit if @redis
152
- end
153
-
154
- def redis_mock(commands, options = {}, &blk)
155
- RedisMock.start(commands, options) do |port|
156
- yield _new_client(options.merge(:port => port))
157
- end
158
- end
159
-
160
- def redis_mock_with_handler(handler, options = {}, &blk)
161
- RedisMock.start_with_handler(handler, options) do |port|
162
- yield _new_client(options.merge(:port => port))
163
- end
164
- end
165
-
166
- def assert_in_range(range, value)
167
- assert range.include?(value), "expected #{value} to be in #{range.inspect}"
168
- end
169
-
170
- def target_version(target)
171
- if version < target
172
- skip("Requires Redis > #{target}") if respond_to?(:skip)
173
- else
174
- yield
175
- end
176
- end
177
- end
178
-
179
- module Client
180
-
181
- include Generic
182
-
183
- def version
184
- Version.new(redis.info["redis_version"])
185
- end
186
-
187
- private
188
-
189
- def _format_options(options)
190
- OPTIONS.merge(:logger => ::Logger.new(@log)).merge(options)
191
- end
192
-
193
- def _new_client(options = {})
194
- Redis.new(_format_options(options).merge(:driver => ENV["conn"]))
195
- end
196
- end
197
-
198
- module Distributed
199
-
200
- include Generic
201
-
202
- def version
203
- Version.new(redis.info.first["redis_version"])
204
- end
205
-
206
- private
207
-
208
- def _format_options(options)
209
- {
210
- :timeout => OPTIONS[:timeout],
211
- :logger => ::Logger.new(@log),
212
- }.merge(options)
213
- end
214
-
215
- def _new_client(options = {})
216
- Redis::Distributed.new(NODES, _format_options(options).merge(:driver => ENV["conn"]))
217
- end
218
- end
219
-
220
- # Basic support for `skip` in 1.8.x
221
- # Note: YOU MUST use `return skip(message)` in order to appropriately bail
222
- # from a running test.
223
- module Skipable
224
- Skipped = Class.new(RuntimeError)
225
-
226
- def skip(message = nil, bt = caller)
227
- return super if defined?(super)
228
-
229
- $stderr.puts("SKIPPED: #{self} #{message || 'no reason given'}")
230
- end
231
- end
232
- end
@@ -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,417 +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
- include Helper::Skipable
9
-
10
- def test_logger
11
- r.ping
12
-
13
- assert log.string["[Redis] command=PING"]
14
- assert log.string =~ /\[Redis\] call_time=\d+\.\d+ ms/
15
- end
16
-
17
- def test_logger_with_pipelining
18
- r.pipelined do
19
- r.set "foo", "bar"
20
- r.get "foo"
21
- end
22
-
23
- assert log.string[" command=SET args=\"foo\" \"bar\""]
24
- assert log.string[" command=GET args=\"foo\""]
25
- end
26
-
27
- def test_recovers_from_failed_commands
28
- # See https://github.com/redis/redis-rb/issues#issue/28
29
-
30
- assert_raise(Redis::CommandError) do
31
- r.command_that_doesnt_exist
32
- end
33
-
34
- assert_nothing_raised do
35
- r.info
36
- end
37
- end
38
-
39
- def test_raises_on_protocol_errors
40
- redis_mock(:ping => lambda { |*_| "foo" }) do |redis|
41
- assert_raise(Redis::ProtocolError) do
42
- redis.ping
43
- end
44
- end
45
- end
46
-
47
- def test_redis_current
48
- assert_equal "127.0.0.1", Redis.current.client.host
49
- assert_equal 6379, Redis.current.client.port
50
- assert_equal 0, Redis.current.client.db
51
-
52
- Redis.current = Redis.new(OPTIONS.merge(:port => 6380, :db => 1))
53
-
54
- t = Thread.new do
55
- assert_equal "127.0.0.1", Redis.current.client.host
56
- assert_equal 6380, Redis.current.client.port
57
- assert_equal 1, Redis.current.client.db
58
- end
59
-
60
- t.join
61
-
62
- assert_equal "127.0.0.1", Redis.current.client.host
63
- assert_equal 6380, Redis.current.client.port
64
- assert_equal 1, Redis.current.client.db
65
- end
66
-
67
- def test_redis_connected?
68
- fresh_client = _new_client
69
- assert !fresh_client.connected?
70
-
71
- fresh_client.ping
72
- assert fresh_client.connected?
73
-
74
- fresh_client.quit
75
- assert !fresh_client.connected?
76
- end
77
-
78
- def test_timeout
79
- assert_nothing_raised do
80
- Redis.new(OPTIONS.merge(:timeout => 0))
81
- end
82
- end
83
-
84
- driver(:ruby) do
85
- def test_tcp_keepalive
86
- keepalive = {:time => 20, :intvl => 10, :probes => 5}
87
-
88
- redis = Redis.new(OPTIONS.merge(:tcp_keepalive => keepalive))
89
- redis.ping
90
-
91
- connection = redis.client.connection
92
- actual_keepalive = connection.get_tcp_keepalive
93
-
94
- [:time, :intvl, :probes].each do |key|
95
- if actual_keepalive.has_key?(key)
96
- assert_equal actual_keepalive[key], keepalive[key]
97
- end
98
- end
99
- end
100
- end
101
-
102
- def test_time
103
- target_version "2.5.4" do
104
- # Test that the difference between the time that Ruby reports and the time
105
- # that Redis reports is minimal (prevents the test from being racy).
106
- rv = r.time
107
-
108
- redis_usec = rv[0] * 1_000_000 + rv[1]
109
- ruby_usec = Integer(Time.now.to_f * 1_000_000)
110
-
111
- assert 500_000 > (ruby_usec - redis_usec).abs
112
- end
113
- end
114
-
115
- def test_connection_timeout
116
- opts = OPTIONS.merge(:host => "10.255.255.254", :connect_timeout => 0.1, :timeout => 5.0)
117
- start_time = Time.now
118
- assert_raise Redis::CannotConnectError do
119
- Redis.new(opts).ping
120
- end
121
- assert (Time.now - start_time) <= opts[:timeout]
122
- end
123
-
124
- driver(:ruby) do
125
- def test_write_timeout
126
- return skip("Relies on buffer sizes, might be unreliable")
127
-
128
- server = TCPServer.new("127.0.0.1", 0)
129
- port = server.addr[1]
130
-
131
- # Hacky, but we need the buffer size
132
- val = TCPSocket.new("127.0.0.1", port).getsockopt(Socket::SOL_SOCKET, Socket::SO_SNDBUF).unpack("i")[0]
133
-
134
- assert_raise(Redis::TimeoutError) do
135
- Timeout.timeout(1) do
136
- redis = Redis.new(:port => port, :timeout => 5, :write_timeout => 0.1)
137
- redis.set("foo", "1" * val*2)
138
- end
139
- end
140
- end
141
- end
142
-
143
- def close_on_ping(seq, options = {})
144
- $request = 0
145
-
146
- command = lambda do
147
- idx = $request
148
- $request += 1
149
-
150
- rv = "+%d" % idx
151
- rv = nil if seq.include?(idx)
152
- rv
153
- end
154
-
155
- redis_mock({:ping => command}, {:timeout => 0.1}.merge(options)) do |redis|
156
- yield(redis)
157
- end
158
- end
159
-
160
- def test_retry_by_default
161
- close_on_ping([0]) do |redis|
162
- assert_equal "1", redis.ping
163
- end
164
- end
165
-
166
- def test_retry_when_wrapped_in_with_reconnect_true
167
- close_on_ping([0]) do |redis|
168
- redis.with_reconnect(true) do
169
- assert_equal "1", redis.ping
170
- end
171
- end
172
- end
173
-
174
- def test_dont_retry_when_wrapped_in_with_reconnect_false
175
- close_on_ping([0]) do |redis|
176
- assert_raise Redis::ConnectionError do
177
- redis.with_reconnect(false) do
178
- redis.ping
179
- end
180
- end
181
- end
182
- end
183
-
184
- def test_dont_retry_when_wrapped_in_without_reconnect
185
- close_on_ping([0]) do |redis|
186
- assert_raise Redis::ConnectionError do
187
- redis.without_reconnect do
188
- redis.ping
189
- end
190
- end
191
- end
192
- end
193
-
194
- def test_retry_only_once_when_read_raises_econnreset
195
- close_on_ping([0, 1]) do |redis|
196
- assert_raise Redis::ConnectionError do
197
- redis.ping
198
- end
199
-
200
- assert !redis.client.connected?
201
- end
202
- end
203
-
204
- def test_retry_with_custom_reconnect_attempts
205
- close_on_ping([0, 1], :reconnect_attempts => 2) do |redis|
206
- assert_equal "2", redis.ping
207
- end
208
- end
209
-
210
- def test_retry_with_custom_reconnect_attempts_can_still_fail
211
- close_on_ping([0, 1, 2], :reconnect_attempts => 2) do |redis|
212
- assert_raise Redis::ConnectionError do
213
- redis.ping
214
- end
215
-
216
- assert !redis.client.connected?
217
- end
218
- end
219
-
220
- def test_don_t_retry_when_second_read_in_pipeline_raises_econnreset
221
- close_on_ping([1]) do |redis|
222
- assert_raise Redis::ConnectionError do
223
- redis.pipelined do
224
- redis.ping
225
- redis.ping # Second #read times out
226
- end
227
- end
228
-
229
- assert !redis.client.connected?
230
- end
231
- end
232
-
233
- def close_on_connection(seq)
234
- $n = 0
235
-
236
- read_command = lambda do |session|
237
- Array.new(session.gets[1..-3].to_i) do
238
- bytes = session.gets[1..-3].to_i
239
- arg = session.read(bytes)
240
- session.read(2) # Discard \r\n
241
- arg
242
- end
243
- end
244
-
245
- handler = lambda do |session|
246
- n = $n
247
- $n += 1
248
-
249
- select = read_command.call(session)
250
- if select[0].downcase == "select"
251
- session.write("+OK\r\n")
252
- else
253
- raise "Expected SELECT"
254
- end
255
-
256
- if !seq.include?(n)
257
- while read_command.call(session)
258
- session.write("+#{n}\r\n")
259
- end
260
- end
261
- end
262
-
263
- redis_mock_with_handler(handler) do |redis|
264
- yield(redis)
265
- end
266
- end
267
-
268
- def test_retry_on_write_error_by_default
269
- close_on_connection([0]) do |redis|
270
- assert_equal "1", redis.client.call(["x" * 128 * 1024])
271
- end
272
- end
273
-
274
- def test_retry_on_write_error_when_wrapped_in_with_reconnect_true
275
- close_on_connection([0]) do |redis|
276
- redis.with_reconnect(true) do
277
- assert_equal "1", redis.client.call(["x" * 128 * 1024])
278
- end
279
- end
280
- end
281
-
282
- def test_dont_retry_on_write_error_when_wrapped_in_with_reconnect_false
283
- close_on_connection([0]) do |redis|
284
- assert_raise Redis::ConnectionError do
285
- redis.with_reconnect(false) do
286
- redis.client.call(["x" * 128 * 1024])
287
- end
288
- end
289
- end
290
- end
291
-
292
- def test_dont_retry_on_write_error_when_wrapped_in_without_reconnect
293
- close_on_connection([0]) do |redis|
294
- assert_raise Redis::ConnectionError do
295
- redis.without_reconnect do
296
- redis.client.call(["x" * 128 * 1024])
297
- end
298
- end
299
- end
300
- end
301
-
302
- def test_connecting_to_unix_domain_socket
303
- assert_nothing_raised do
304
- Redis.new(OPTIONS.merge(:path => "./test/db/redis.sock")).ping
305
- end
306
- end
307
-
308
- driver(:ruby, :hiredis) do
309
- def test_bubble_timeout_without_retrying
310
- serv = TCPServer.new(6380)
311
-
312
- redis = Redis.new(:port => 6380, :timeout => 0.1)
313
-
314
- assert_raise(Redis::TimeoutError) do
315
- redis.ping
316
- end
317
-
318
- ensure
319
- serv.close if serv
320
- end
321
- end
322
-
323
- def test_client_options
324
- redis = Redis.new(OPTIONS.merge(:host => "host", :port => 1234, :db => 1, :scheme => "foo"))
325
-
326
- assert_equal "host", redis.client.options[:host]
327
- assert_equal 1234, redis.client.options[:port]
328
- assert_equal 1, redis.client.options[:db]
329
- assert_equal "foo", redis.client.options[:scheme]
330
- end
331
-
332
- def test_does_not_change_self_client_options
333
- redis = Redis.new(OPTIONS.merge(:host => "host", :port => 1234, :db => 1, :scheme => "foo"))
334
- options = redis.client.options
335
-
336
- options[:host] << "new_host"
337
- options[:scheme] << "bar"
338
- options.merge!(:db => 0)
339
-
340
- assert_equal "host", redis.client.options[:host]
341
- assert_equal 1, redis.client.options[:db]
342
- assert_equal "foo", redis.client.options[:scheme]
343
- end
344
-
345
- def test_resolves_localhost
346
- assert_nothing_raised do
347
- Redis.new(OPTIONS.merge(:host => 'localhost')).ping
348
- end
349
- end
350
-
351
- class << self
352
- def af_family_supported(af)
353
- hosts = {
354
- Socket::AF_INET => "127.0.0.1",
355
- Socket::AF_INET6 => "::1",
356
- }
357
-
358
- begin
359
- s = Socket.new(af, Socket::SOCK_STREAM, 0)
360
- begin
361
- tries = 5
362
- begin
363
- sa = Socket.pack_sockaddr_in(1024 + Random.rand(63076), hosts[af])
364
- s.bind(sa)
365
- rescue Errno::EADDRINUSE
366
- tries -= 1
367
- retry if tries > 0
368
-
369
- raise
370
- end
371
- yield
372
- rescue Errno::EADDRNOTAVAIL
373
- ensure
374
- s.close
375
- end
376
- rescue Errno::ESOCKTNOSUPPORT
377
- end
378
- end
379
- end
380
-
381
- def af_test(host)
382
- commands = {
383
- :ping => lambda { |*_| "+pong" },
384
- }
385
-
386
- redis_mock(commands, :host => host) do |redis|
387
- assert_nothing_raised do
388
- redis.ping
389
- end
390
- end
391
- end
392
-
393
- driver(:ruby) do
394
- af_family_supported(Socket::AF_INET) do
395
- def test_connect_ipv4
396
- af_test("127.0.0.1")
397
- end
398
- end
399
- end
400
-
401
- driver(:ruby) do
402
- af_family_supported(Socket::AF_INET6) do
403
- def test_connect_ipv6
404
- af_test("::1")
405
- end
406
- end
407
- end
408
-
409
- def test_can_be_duped_to_create_a_new_connection
410
- clients = r.info["connected_clients"].to_i
411
-
412
- r2 = r.dup
413
- r2.ping
414
-
415
- assert_equal clients + 1, r.info["connected_clients"].to_i
416
- end
417
- end