redis 3.3.5 → 4.1.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 (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