redis 3.3.5 → 4.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +225 -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 +411 -0
  22. data/lib/redis/commands/lists.rb +289 -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 +207 -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 +242 -0
  32. data/lib/redis/connection/command_helper.rb +7 -10
  33. data/lib/redis/connection/hiredis.rb +5 -5
  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 +231 -72
  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 +173 -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?