redis 3.3.5 → 4.8.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 (147) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +232 -2
  3. data/README.md +169 -89
  4. data/lib/redis/client.rb +177 -100
  5. data/lib/redis/cluster/command.rb +79 -0
  6. data/lib/redis/cluster/command_loader.rb +33 -0
  7. data/lib/redis/cluster/key_slot_converter.rb +72 -0
  8. data/lib/redis/cluster/node.rb +120 -0
  9. data/lib/redis/cluster/node_key.rb +31 -0
  10. data/lib/redis/cluster/node_loader.rb +34 -0
  11. data/lib/redis/cluster/option.rb +100 -0
  12. data/lib/redis/cluster/slot.rb +86 -0
  13. data/lib/redis/cluster/slot_loader.rb +46 -0
  14. data/lib/redis/cluster.rb +315 -0
  15. data/lib/redis/commands/bitmaps.rb +63 -0
  16. data/lib/redis/commands/cluster.rb +45 -0
  17. data/lib/redis/commands/connection.rb +58 -0
  18. data/lib/redis/commands/geo.rb +84 -0
  19. data/lib/redis/commands/hashes.rb +251 -0
  20. data/lib/redis/commands/hyper_log_log.rb +37 -0
  21. data/lib/redis/commands/keys.rb +455 -0
  22. data/lib/redis/commands/lists.rb +290 -0
  23. data/lib/redis/commands/pubsub.rb +72 -0
  24. data/lib/redis/commands/scripting.rb +114 -0
  25. data/lib/redis/commands/server.rb +188 -0
  26. data/lib/redis/commands/sets.rb +223 -0
  27. data/lib/redis/commands/sorted_sets.rb +812 -0
  28. data/lib/redis/commands/streams.rb +382 -0
  29. data/lib/redis/commands/strings.rb +313 -0
  30. data/lib/redis/commands/transactions.rb +139 -0
  31. data/lib/redis/commands.rb +240 -0
  32. data/lib/redis/connection/command_helper.rb +7 -10
  33. data/lib/redis/connection/hiredis.rb +5 -3
  34. data/lib/redis/connection/registry.rb +2 -1
  35. data/lib/redis/connection/ruby.rb +136 -128
  36. data/lib/redis/connection/synchrony.rb +24 -9
  37. data/lib/redis/connection.rb +3 -1
  38. data/lib/redis/distributed.rb +255 -85
  39. data/lib/redis/errors.rb +57 -0
  40. data/lib/redis/hash_ring.rb +30 -73
  41. data/lib/redis/pipeline.rb +178 -13
  42. data/lib/redis/subscribe.rb +11 -12
  43. data/lib/redis/version.rb +3 -1
  44. data/lib/redis.rb +174 -2661
  45. metadata +66 -202
  46. data/.gitignore +0 -16
  47. data/.travis/Gemfile +0 -11
  48. data/.travis.yml +0 -89
  49. data/.yardopts +0 -3
  50. data/Gemfile +0 -4
  51. data/Rakefile +0 -87
  52. data/benchmarking/logging.rb +0 -71
  53. data/benchmarking/pipeline.rb +0 -51
  54. data/benchmarking/speed.rb +0 -21
  55. data/benchmarking/suite.rb +0 -24
  56. data/benchmarking/worker.rb +0 -71
  57. data/examples/basic.rb +0 -15
  58. data/examples/consistency.rb +0 -114
  59. data/examples/dist_redis.rb +0 -43
  60. data/examples/incr-decr.rb +0 -17
  61. data/examples/list.rb +0 -26
  62. data/examples/pubsub.rb +0 -37
  63. data/examples/sentinel/sentinel.conf +0 -9
  64. data/examples/sentinel/start +0 -49
  65. data/examples/sentinel.rb +0 -41
  66. data/examples/sets.rb +0 -36
  67. data/examples/unicorn/config.ru +0 -3
  68. data/examples/unicorn/unicorn.rb +0 -20
  69. data/redis.gemspec +0 -44
  70. data/test/bitpos_test.rb +0 -69
  71. data/test/blocking_commands_test.rb +0 -42
  72. data/test/client_test.rb +0 -59
  73. data/test/command_map_test.rb +0 -30
  74. data/test/commands_on_hashes_test.rb +0 -21
  75. data/test/commands_on_hyper_log_log_test.rb +0 -21
  76. data/test/commands_on_lists_test.rb +0 -20
  77. data/test/commands_on_sets_test.rb +0 -77
  78. data/test/commands_on_sorted_sets_test.rb +0 -137
  79. data/test/commands_on_strings_test.rb +0 -101
  80. data/test/commands_on_value_types_test.rb +0 -133
  81. data/test/connection_handling_test.rb +0 -277
  82. data/test/connection_test.rb +0 -57
  83. data/test/db/.gitkeep +0 -0
  84. data/test/distributed_blocking_commands_test.rb +0 -46
  85. data/test/distributed_commands_on_hashes_test.rb +0 -10
  86. data/test/distributed_commands_on_hyper_log_log_test.rb +0 -33
  87. data/test/distributed_commands_on_lists_test.rb +0 -22
  88. data/test/distributed_commands_on_sets_test.rb +0 -83
  89. data/test/distributed_commands_on_sorted_sets_test.rb +0 -18
  90. data/test/distributed_commands_on_strings_test.rb +0 -59
  91. data/test/distributed_commands_on_value_types_test.rb +0 -95
  92. data/test/distributed_commands_requiring_clustering_test.rb +0 -164
  93. data/test/distributed_connection_handling_test.rb +0 -23
  94. data/test/distributed_internals_test.rb +0 -79
  95. data/test/distributed_key_tags_test.rb +0 -52
  96. data/test/distributed_persistence_control_commands_test.rb +0 -26
  97. data/test/distributed_publish_subscribe_test.rb +0 -92
  98. data/test/distributed_remote_server_control_commands_test.rb +0 -66
  99. data/test/distributed_scripting_test.rb +0 -102
  100. data/test/distributed_sorting_test.rb +0 -20
  101. data/test/distributed_test.rb +0 -58
  102. data/test/distributed_transactions_test.rb +0 -32
  103. data/test/encoding_test.rb +0 -18
  104. data/test/error_replies_test.rb +0 -59
  105. data/test/fork_safety_test.rb +0 -65
  106. data/test/helper.rb +0 -232
  107. data/test/helper_test.rb +0 -24
  108. data/test/internals_test.rb +0 -417
  109. data/test/lint/blocking_commands.rb +0 -150
  110. data/test/lint/hashes.rb +0 -162
  111. data/test/lint/hyper_log_log.rb +0 -60
  112. data/test/lint/lists.rb +0 -143
  113. data/test/lint/sets.rb +0 -140
  114. data/test/lint/sorted_sets.rb +0 -316
  115. data/test/lint/strings.rb +0 -260
  116. data/test/lint/value_types.rb +0 -122
  117. data/test/persistence_control_commands_test.rb +0 -26
  118. data/test/pipelining_commands_test.rb +0 -242
  119. data/test/publish_subscribe_test.rb +0 -282
  120. data/test/remote_server_control_commands_test.rb +0 -118
  121. data/test/scanning_test.rb +0 -413
  122. data/test/scripting_test.rb +0 -78
  123. data/test/sentinel_command_test.rb +0 -80
  124. data/test/sentinel_test.rb +0 -255
  125. data/test/sorting_test.rb +0 -59
  126. data/test/ssl_test.rb +0 -73
  127. data/test/support/connection/hiredis.rb +0 -1
  128. data/test/support/connection/ruby.rb +0 -1
  129. data/test/support/connection/synchrony.rb +0 -17
  130. data/test/support/redis_mock.rb +0 -130
  131. data/test/support/ssl/gen_certs.sh +0 -31
  132. data/test/support/ssl/trusted-ca.crt +0 -25
  133. data/test/support/ssl/trusted-ca.key +0 -27
  134. data/test/support/ssl/trusted-cert.crt +0 -81
  135. data/test/support/ssl/trusted-cert.key +0 -28
  136. data/test/support/ssl/untrusted-ca.crt +0 -26
  137. data/test/support/ssl/untrusted-ca.key +0 -27
  138. data/test/support/ssl/untrusted-cert.crt +0 -82
  139. data/test/support/ssl/untrusted-cert.key +0 -28
  140. data/test/support/wire/synchrony.rb +0 -24
  141. data/test/support/wire/thread.rb +0 -5
  142. data/test/synchrony_driver.rb +0 -88
  143. data/test/test.conf.erb +0 -9
  144. data/test/thread_safety_test.rb +0 -62
  145. data/test/transactions_test.rb +0 -264
  146. data/test/unknown_commands_test.rb +0 -14
  147. data/test/url_param_test.rb +0 -138
@@ -1,6 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "redis/connection/registry"
2
4
  require "redis/connection/command_helper"
3
5
  require "redis/errors"
6
+
4
7
  require "socket"
5
8
  require "timeout"
6
9
 
@@ -10,130 +13,88 @@ rescue LoadError
10
13
  # Not all systems have OpenSSL support
11
14
  end
12
15
 
13
- if RUBY_VERSION < "1.9.3"
14
- class String
15
- # Ruby 1.8.7 does not have byteslice, but it handles encodings differently anyway.
16
- # We can simply slice the string, which is a byte array there.
17
- def byteslice(*args)
18
- slice(*args)
19
- end
20
- end
21
- end
22
-
23
16
  class Redis
24
17
  module Connection
25
18
  module SocketMixin
26
-
27
- CRLF = "\r\n".freeze
28
-
29
- # Exceptions raised during non-blocking I/O ops that require retrying the op
30
- if RUBY_VERSION >= "1.9.3"
31
- NBIO_READ_EXCEPTIONS = [IO::WaitReadable]
32
- NBIO_WRITE_EXCEPTIONS = [IO::WaitWritable]
33
- else
34
- NBIO_READ_EXCEPTIONS = [Errno::EWOULDBLOCK, Errno::EAGAIN]
35
- NBIO_WRITE_EXCEPTIONS = [Errno::EWOULDBLOCK, Errno::EAGAIN]
36
- end
19
+ CRLF = "\r\n"
37
20
 
38
21
  def initialize(*args)
39
22
  super(*args)
40
23
 
41
24
  @timeout = @write_timeout = nil
42
- @buffer = ""
25
+ @buffer = "".b
43
26
  end
44
27
 
45
28
  def timeout=(timeout)
46
- if timeout && timeout > 0
47
- @timeout = timeout
48
- else
49
- @timeout = nil
50
- end
29
+ @timeout = (timeout if timeout && timeout > 0)
51
30
  end
52
31
 
53
32
  def write_timeout=(timeout)
54
- if timeout && timeout > 0
55
- @write_timeout = timeout
56
- else
57
- @write_timeout = nil
58
- end
33
+ @write_timeout = (timeout if timeout && timeout > 0)
59
34
  end
60
35
 
61
36
  def read(nbytes)
62
37
  result = @buffer.slice!(0, nbytes)
63
38
 
64
- while result.bytesize < nbytes
65
- result << _read_from_socket(nbytes - result.bytesize)
66
- end
39
+ buffer = String.new(capacity: nbytes, encoding: Encoding::ASCII_8BIT)
40
+ result << _read_from_socket(nbytes - result.bytesize, buffer) while result.bytesize < nbytes
67
41
 
68
42
  result
69
43
  end
70
44
 
71
45
  def gets
72
- crlf = nil
73
-
74
- while (crlf = @buffer.index(CRLF)) == nil
75
- @buffer << _read_from_socket(1024)
46
+ while (crlf = @buffer.index(CRLF)).nil?
47
+ @buffer << _read_from_socket(16_384)
76
48
  end
77
49
 
78
50
  @buffer.slice!(0, crlf + CRLF.bytesize)
79
51
  end
80
52
 
81
- def _read_from_socket(nbytes)
82
-
83
- begin
84
- read_nonblock(nbytes)
85
-
86
- rescue *NBIO_READ_EXCEPTIONS
87
- if IO.select([self], nil, nil, @timeout)
88
- retry
89
- else
90
- raise Redis::TimeoutError
91
- end
92
- rescue *NBIO_WRITE_EXCEPTIONS
93
- if IO.select(nil, [self], nil, @timeout)
94
- retry
95
- else
96
- raise Redis::TimeoutError
97
- end
98
- end
99
-
100
- rescue EOFError
101
- raise Errno::ECONNRESET
102
- end
103
-
104
- def _write_to_socket(data)
105
- begin
106
- write_nonblock(data)
107
-
108
- rescue *NBIO_WRITE_EXCEPTIONS
109
- if IO.select(nil, [self], nil, @write_timeout)
110
- retry
111
- else
112
- raise Redis::TimeoutError
113
- end
114
- rescue *NBIO_READ_EXCEPTIONS
115
- if IO.select([self], nil, nil, @write_timeout)
116
- retry
117
- else
118
- raise Redis::TimeoutError
53
+ def _read_from_socket(nbytes, buffer = nil)
54
+ loop do
55
+ case chunk = read_nonblock(nbytes, buffer, exception: false)
56
+ when :wait_readable
57
+ unless wait_readable(@timeout)
58
+ raise Redis::TimeoutError
59
+ end
60
+ when :wait_writable
61
+ unless wait_writable(@timeout)
62
+ raise Redis::TimeoutError
63
+ end
64
+ when nil
65
+ raise Errno::ECONNRESET
66
+ when String
67
+ return chunk
119
68
  end
120
69
  end
121
-
122
- rescue EOFError
123
- raise Errno::ECONNRESET
124
70
  end
125
71
 
126
- def write(data)
127
- return super(data) unless @write_timeout
72
+ def write(buffer)
73
+ return super(buffer) unless @write_timeout
128
74
 
129
- length = data.bytesize
130
- total_count = 0
75
+ bytes_to_write = buffer.bytesize
76
+ total_bytes_written = 0
131
77
  loop do
132
- count = _write_to_socket(data)
78
+ case bytes_written = write_nonblock(buffer, exception: false)
79
+ when :wait_readable
80
+ unless wait_readable(@write_timeout)
81
+ raise Redis::TimeoutError
82
+ end
83
+ when :wait_writable
84
+ unless wait_writable(@write_timeout)
85
+ raise Redis::TimeoutError
86
+ end
87
+ when nil
88
+ raise Errno::ECONNRESET
89
+ when Integer
90
+ total_bytes_written += bytes_written
91
+
92
+ if total_bytes_written >= bytes_to_write
93
+ return total_bytes_written
94
+ end
133
95
 
134
- total_count += count
135
- return total_count if total_count >= length
136
- data = data.byteslice(count..-1)
96
+ buffer = buffer.byteslice(bytes_written..-1)
97
+ end
137
98
  end
138
99
  end
139
100
  end
@@ -143,7 +104,6 @@ class Redis
143
104
  require "timeout"
144
105
 
145
106
  class TCPSocket < ::TCPSocket
146
-
147
107
  include SocketMixin
148
108
 
149
109
  def self.connect(host, port, timeout)
@@ -159,7 +119,6 @@ class Redis
159
119
  if defined?(::UNIXSocket)
160
120
 
161
121
  class UNIXSocket < ::UNIXSocket
162
-
163
122
  include SocketMixin
164
123
 
165
124
  def self.connect(path, timeout)
@@ -171,13 +130,14 @@ class Redis
171
130
  raise TimeoutError
172
131
  end
173
132
 
174
- # JRuby raises Errno::EAGAIN on #read_nonblock even when IO.select
133
+ # JRuby raises Errno::EAGAIN on #read_nonblock even when it
175
134
  # says it is readable (1.6.6, in both 1.8 and 1.9 mode).
176
135
  # Use the blocking #readpartial method instead.
177
136
 
178
- def _read_from_socket(nbytes)
137
+ def _read_from_socket(nbytes, _buffer = nil)
138
+ # JRuby: Throw away the buffer as we won't need it
139
+ # but still need to support the max arity of 2
179
140
  readpartial(nbytes)
180
-
181
141
  rescue EOFError
182
142
  raise Errno::ECONNRESET
183
143
  end
@@ -188,19 +148,16 @@ class Redis
188
148
  else
189
149
 
190
150
  class TCPSocket < ::Socket
191
-
192
151
  include SocketMixin
193
152
 
194
- def self.connect_addrinfo(ai, port, timeout)
195
- sock = new(::Socket.const_get(ai[0]), Socket::SOCK_STREAM, 0)
196
- sockaddr = ::Socket.pack_sockaddr_in(port, ai[3])
153
+ def self.connect_addrinfo(addrinfo, port, timeout)
154
+ sock = new(::Socket.const_get(addrinfo[0]), Socket::SOCK_STREAM, 0)
155
+ sockaddr = ::Socket.pack_sockaddr_in(port, addrinfo[3])
197
156
 
198
157
  begin
199
158
  sock.connect_nonblock(sockaddr)
200
159
  rescue Errno::EINPROGRESS
201
- if IO.select(nil, [sock], nil, timeout) == nil
202
- raise TimeoutError
203
- end
160
+ raise TimeoutError unless sock.wait_writable(timeout)
204
161
 
205
162
  begin
206
163
  sock.connect_nonblock(sockaddr)
@@ -239,14 +196,13 @@ class Redis
239
196
  return connect_addrinfo(ai, port, timeout)
240
197
  rescue SystemCallError
241
198
  # Raise if this was our last attempt.
242
- raise if addrinfo.length == i+1
199
+ raise if addrinfo.length == i + 1
243
200
  end
244
201
  end
245
202
  end
246
203
  end
247
204
 
248
205
  class UNIXSocket < ::Socket
249
-
250
206
  include SocketMixin
251
207
 
252
208
  def self.connect(path, timeout)
@@ -256,9 +212,7 @@ class Redis
256
212
  begin
257
213
  sock.connect_nonblock(sockaddr)
258
214
  rescue Errno::EINPROGRESS
259
- if IO.select(nil, [sock], nil, timeout) == nil
260
- raise TimeoutError
261
- end
215
+ raise TimeoutError unless sock.wait_writable(timeout)
262
216
 
263
217
  begin
264
218
  sock.connect_nonblock(sockaddr)
@@ -276,17 +230,58 @@ class Redis
276
230
  class SSLSocket < ::OpenSSL::SSL::SSLSocket
277
231
  include SocketMixin
278
232
 
233
+ unless method_defined?(:wait_readable)
234
+ def wait_readable(timeout = nil)
235
+ to_io.wait_readable(timeout)
236
+ end
237
+ end
238
+
239
+ unless method_defined?(:wait_writable)
240
+ def wait_writable(timeout = nil)
241
+ to_io.wait_writable(timeout)
242
+ end
243
+ end
244
+
279
245
  def self.connect(host, port, timeout, ssl_params)
280
- # Note: this is using Redis::Connection::TCPSocket
246
+ # NOTE: this is using Redis::Connection::TCPSocket
281
247
  tcp_sock = TCPSocket.connect(host, port, timeout)
282
248
 
283
249
  ctx = OpenSSL::SSL::SSLContext.new
284
- ctx.set_params(ssl_params) if ssl_params && !ssl_params.empty?
250
+
251
+ # The provided parameters are merged into OpenSSL::SSL::SSLContext::DEFAULT_PARAMS
252
+ ctx.set_params(ssl_params || {})
285
253
 
286
254
  ssl_sock = new(tcp_sock, ctx)
287
255
  ssl_sock.hostname = host
288
- ssl_sock.connect
289
- ssl_sock.post_connection_check(host)
256
+
257
+ begin
258
+ # Initiate the socket connection in the background. If it doesn't fail
259
+ # immediately it will raise an IO::WaitWritable (Errno::EINPROGRESS)
260
+ # indicating the connection is in progress.
261
+ # Unlike waiting for a tcp socket to connect, you can't time out ssl socket
262
+ # connections during the connect phase properly, because IO.select only partially works.
263
+ # Instead, you have to retry.
264
+ ssl_sock.connect_nonblock
265
+ rescue Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable
266
+ if ssl_sock.wait_readable(timeout)
267
+ retry
268
+ else
269
+ raise TimeoutError
270
+ end
271
+ rescue IO::WaitWritable
272
+ if ssl_sock.wait_writable(timeout)
273
+ retry
274
+ else
275
+ raise TimeoutError
276
+ end
277
+ end
278
+
279
+ unless ctx.verify_mode == OpenSSL::SSL::VERIFY_NONE || (
280
+ ctx.respond_to?(:verify_hostname) &&
281
+ !ctx.verify_hostname
282
+ )
283
+ ssl_sock.post_connection_check(host)
284
+ end
290
285
 
291
286
  ssl_sock
292
287
  end
@@ -296,31 +291,32 @@ class Redis
296
291
  class Ruby
297
292
  include Redis::Connection::CommandHelper
298
293
 
299
- MINUS = "-".freeze
300
- PLUS = "+".freeze
301
- COLON = ":".freeze
302
- DOLLAR = "$".freeze
303
- ASTERISK = "*".freeze
294
+ MINUS = "-"
295
+ PLUS = "+"
296
+ COLON = ":"
297
+ DOLLAR = "$"
298
+ ASTERISK = "*"
304
299
 
305
300
  def self.connect(config)
306
301
  if config[:scheme] == "unix"
307
302
  raise ArgumentError, "SSL incompatible with unix sockets" if config[:ssl]
303
+
308
304
  sock = UNIXSocket.connect(config[:path], config[:connect_timeout])
309
305
  elsif config[:scheme] == "rediss" || config[:ssl]
310
- raise ArgumentError, "This library does not support SSL on Ruby < 1.9" if RUBY_VERSION < "1.9.3"
311
306
  sock = SSLSocket.connect(config[:host], config[:port], config[:connect_timeout], config[:ssl_params])
312
307
  else
313
308
  sock = TCPSocket.connect(config[:host], config[:port], config[:connect_timeout])
314
309
  end
315
310
 
316
311
  instance = new(sock)
317
- instance.timeout = config[:timeout]
312
+ instance.timeout = config[:read_timeout]
318
313
  instance.write_timeout = config[:write_timeout]
319
314
  instance.set_tcp_keepalive config[:tcp_keepalive]
315
+ instance.set_tcp_nodelay if sock.is_a? TCPSocket
320
316
  instance
321
317
  end
322
318
 
323
- if [:SOL_SOCKET, :SO_KEEPALIVE, :SOL_TCP, :TCP_KEEPIDLE, :TCP_KEEPINTVL, :TCP_KEEPCNT].all?{|c| Socket.const_defined? c}
319
+ if %i[SOL_SOCKET SO_KEEPALIVE SOL_TCP TCP_KEEPIDLE TCP_KEEPINTVL TCP_KEEPCNT].all? { |c| Socket.const_defined? c }
324
320
  def set_tcp_keepalive(keepalive)
325
321
  return unless keepalive.is_a?(Hash)
326
322
 
@@ -332,14 +328,13 @@ class Redis
332
328
 
333
329
  def get_tcp_keepalive
334
330
  {
335
- :time => @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPIDLE).int,
336
- :intvl => @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPINTVL).int,
337
- :probes => @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPCNT).int,
331
+ time: @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPIDLE).int,
332
+ intvl: @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPINTVL).int,
333
+ probes: @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPCNT).int
338
334
  }
339
335
  end
340
336
  else
341
- def set_tcp_keepalive(keepalive)
342
- end
337
+ def set_tcp_keepalive(keepalive); end
343
338
 
344
339
  def get_tcp_keepalive
345
340
  {
@@ -347,12 +342,21 @@ class Redis
347
342
  end
348
343
  end
349
344
 
345
+ # disables Nagle's Algorithm, prevents multiple round trips with MULTI
346
+ if %i[IPPROTO_TCP TCP_NODELAY].all? { |c| Socket.const_defined? c }
347
+ def set_tcp_nodelay
348
+ @sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
349
+ end
350
+ else
351
+ def set_tcp_nodelay; end
352
+ end
353
+
350
354
  def initialize(sock)
351
355
  @sock = sock
352
356
  end
353
357
 
354
358
  def connected?
355
- !! @sock
359
+ !!@sock
356
360
  end
357
361
 
358
362
  def disconnect
@@ -363,9 +367,7 @@ class Redis
363
367
  end
364
368
 
365
369
  def timeout=(timeout)
366
- if @sock.respond_to?(:timeout=)
367
- @sock.timeout = timeout
368
- end
370
+ @sock.timeout = timeout if @sock.respond_to?(:timeout=)
369
371
  end
370
372
 
371
373
  def write_timeout=(timeout)
@@ -380,9 +382,14 @@ class Redis
380
382
  line = @sock.gets
381
383
  reply_type = line.slice!(0, 1)
382
384
  format_reply(reply_type, line)
383
-
384
385
  rescue Errno::EAGAIN
385
386
  raise TimeoutError
387
+ rescue OpenSSL::SSL::SSLError => ssl_error
388
+ if ssl_error.message.match?(/SSL_read: unexpected eof while reading/i)
389
+ raise EOFError, ssl_error.message
390
+ else
391
+ raise
392
+ end
386
393
  end
387
394
 
388
395
  def format_reply(reply_type, line)
@@ -392,7 +399,7 @@ class Redis
392
399
  when COLON then format_integer_reply(line)
393
400
  when DOLLAR then format_bulk_reply(line)
394
401
  when ASTERISK then format_multi_bulk_reply(line)
395
- else raise ProtocolError.new(reply_type)
402
+ else raise ProtocolError, reply_type
396
403
  end
397
404
  end
398
405
 
@@ -411,6 +418,7 @@ class Redis
411
418
  def format_bulk_reply(line)
412
419
  bulklen = line.to_i
413
420
  return if bulklen == -1
421
+
414
422
  reply = encode(@sock.read(bulklen))
415
423
  @sock.read(2) # Discard CRLF.
416
424
  reply
@@ -1,9 +1,17 @@
1
- require "redis/connection/command_helper"
1
+ # frozen_string_literal: true
2
+
2
3
  require "redis/connection/registry"
4
+ require "redis/connection/command_helper"
3
5
  require "redis/errors"
6
+
4
7
  require "em-synchrony"
5
8
  require "hiredis/reader"
6
9
 
10
+ ::Redis.deprecate!(
11
+ "The redis synchrony driver is deprecated and will be removed in redis-rb 5.0.0. " \
12
+ "We're looking for people to maintain it as a separate gem, see https://github.com/redis/redis-rb/issues/915"
13
+ )
14
+
7
15
  class Redis
8
16
  module Connection
9
17
  class RedisClient < EventMachine::Connection
@@ -46,9 +54,7 @@ class Redis
46
54
 
47
55
  def read
48
56
  @req = EventMachine::DefaultDeferrable.new
49
- if @timeout > 0
50
- @req.timeout(@timeout, :timeout)
51
- end
57
+ @req.timeout(@timeout, :timeout) if @timeout > 0
52
58
  EventMachine::Synchrony.sync @req
53
59
  end
54
60
 
@@ -72,7 +78,15 @@ class Redis
72
78
 
73
79
  def self.connect(config)
74
80
  if config[:scheme] == "unix"
75
- conn = EventMachine.connect_unix_domain(config[:path], RedisClient)
81
+ begin
82
+ conn = EventMachine.connect_unix_domain(config[:path], RedisClient)
83
+ rescue RuntimeError => e
84
+ if e.message == "no connection"
85
+ raise Errno::ECONNREFUSED
86
+ else
87
+ raise e
88
+ end
89
+ end
76
90
  elsif config[:scheme] == "rediss" || config[:ssl]
77
91
  raise NotImplementedError, "SSL not supported by synchrony driver"
78
92
  else
@@ -97,7 +111,7 @@ class Redis
97
111
  end
98
112
 
99
113
  def connected?
100
- @connection && @connection.connected?
114
+ @connection&.connected?
101
115
  end
102
116
 
103
117
  def timeout=(timeout)
@@ -116,11 +130,12 @@ class Redis
116
130
  def read
117
131
  type, payload = @connection.read
118
132
 
119
- if type == :reply
133
+ case type
134
+ when :reply
120
135
  payload
121
- elsif type == :error
136
+ when :error
122
137
  raise payload
123
- elsif type == :timeout
138
+ when :timeout
124
139
  raise TimeoutError
125
140
  else
126
141
  raise "Unknown type #{type.inspect}"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "redis/connection/registry"
2
4
 
3
5
  # If a connection driver was required before this file, the array
@@ -6,4 +8,4 @@ require "redis/connection/registry"
6
8
  # the plain Ruby driver as our default. Another driver can be required at a
7
9
  # later point in time, causing it to be the last element of the #drivers array
8
10
  # and therefore be chosen by default.
9
- require "redis/connection/ruby" if Redis::Connection.drivers.empty?
11
+ require_relative "connection/ruby" if Redis::Connection.drivers.empty?