redis 4.1.2 → 4.2.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.
data/lib/redis/cluster.rb CHANGED
@@ -80,6 +80,7 @@ class Redis
80
80
  def call_pipeline(pipeline)
81
81
  node_keys, command_keys = extract_keys_in_pipeline(pipeline)
82
82
  raise CrossSlotPipeliningError, command_keys if node_keys.size > 1
83
+
83
84
  node = find_node(node_keys.first)
84
85
  try_send(node, :call_pipeline, pipeline)
85
86
  end
@@ -112,12 +113,11 @@ class Redis
112
113
  node = Node.new(option.per_node_key)
113
114
  available_slots = SlotLoader.load(node)
114
115
  node_flags = NodeLoader.load_flags(node)
115
- available_node_urls = NodeKey.to_node_urls(available_slots.keys, secure: option.secure?)
116
- option.update_node(available_node_urls)
116
+ option.update_node(available_slots.keys.map { |k| NodeKey.optionize(k) })
117
117
  [Node.new(option.per_node_key, node_flags, option.use_replica?),
118
118
  Slot.new(available_slots, node_flags, option.use_replica?)]
119
119
  ensure
120
- node.map(&:disconnect)
120
+ node&.each(&:disconnect)
121
121
  end
122
122
 
123
123
  def fetch_command_details(nodes)
@@ -216,9 +216,14 @@ class Redis
216
216
  node.public_send(method_name, *args, &block)
217
217
  rescue CommandError => err
218
218
  if err.message.start_with?('MOVED')
219
- assign_redirection_node(err.message).public_send(method_name, *args, &block)
219
+ raise if retry_count <= 0
220
+
221
+ node = assign_redirection_node(err.message)
222
+ retry_count -= 1
223
+ retry
220
224
  elsif err.message.start_with?('ASK')
221
225
  raise if retry_count <= 0
226
+
222
227
  node = assign_asking_node(err.message)
223
228
  node.call(%i[asking])
224
229
  retry_count -= 1
@@ -226,6 +231,9 @@ class Redis
226
231
  else
227
232
  raise
228
233
  end
234
+ rescue CannotConnectError
235
+ update_cluster_info!
236
+ raise
229
237
  end
230
238
 
231
239
  def assign_redirection_node(err_msg)
@@ -261,6 +269,7 @@ class Redis
261
269
 
262
270
  def find_node(node_key)
263
271
  return @node.sample if node_key.nil?
272
+
264
273
  @node.find_by(node_key)
265
274
  rescue Node::ReloadNeeded
266
275
  update_cluster_info!(node_key)
@@ -1,7 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Redis
2
4
  module Connection
3
5
  module CommandHelper
4
-
5
6
  COMMAND_DELIMITER = "\r\n"
6
7
 
7
8
  def build_command(args)
@@ -28,7 +29,7 @@ class Redis
28
29
  command.join(COMMAND_DELIMITER)
29
30
  end
30
31
 
31
- protected
32
+ protected
32
33
 
33
34
  def encode(string)
34
35
  string.force_encoding(Encoding.default_external)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative "registry"
2
4
  require_relative "../errors"
3
5
  require "hiredis/connection"
@@ -6,7 +8,6 @@ require "timeout"
6
8
  class Redis
7
9
  module Connection
8
10
  class Hiredis
9
-
10
11
  def self.connect(config)
11
12
  connection = ::Hiredis::Connection.new
12
13
  connect_timeout = (config.fetch(:connect_timeout, 0) * 1_000_000).to_i
@@ -31,7 +32,7 @@ class Redis
31
32
  end
32
33
 
33
34
  def connected?
34
- @connection && @connection.connected?
35
+ @connection&.connected?
35
36
  end
36
37
 
37
38
  def timeout=(timeout)
@@ -57,7 +58,7 @@ class Redis
57
58
  rescue Errno::EAGAIN
58
59
  raise TimeoutError
59
60
  rescue RuntimeError => err
60
- raise ProtocolError.new(err.message)
61
+ raise ProtocolError, err.message
61
62
  end
62
63
  end
63
64
  end
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Redis
2
4
  module Connection
3
-
4
5
  # Store a list of loaded connection drivers in the Connection module.
5
6
  # Redis::Client uses the last required driver by default, and will be aware
6
7
  # of the loaded connection drivers if the user chooses to override the
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative "registry"
2
4
  require_relative "command_helper"
3
5
  require_relative "../errors"
@@ -13,8 +15,7 @@ end
13
15
  class Redis
14
16
  module Connection
15
17
  module SocketMixin
16
-
17
- CRLF = "\r\n".freeze
18
+ CRLF = "\r\n"
18
19
 
19
20
  def initialize(*args)
20
21
  super(*args)
@@ -24,46 +25,32 @@ class Redis
24
25
  end
25
26
 
26
27
  def timeout=(timeout)
27
- if timeout && timeout > 0
28
- @timeout = timeout
29
- else
30
- @timeout = nil
31
- end
28
+ @timeout = (timeout if timeout && timeout > 0)
32
29
  end
33
30
 
34
31
  def write_timeout=(timeout)
35
- if timeout && timeout > 0
36
- @write_timeout = timeout
37
- else
38
- @write_timeout = nil
39
- end
32
+ @write_timeout = (timeout if timeout && timeout > 0)
40
33
  end
41
34
 
42
35
  def read(nbytes)
43
36
  result = @buffer.slice!(0, nbytes)
44
37
 
45
- while result.bytesize < nbytes
46
- result << _read_from_socket(nbytes - result.bytesize)
47
- end
38
+ result << _read_from_socket(nbytes - result.bytesize) while result.bytesize < nbytes
48
39
 
49
40
  result
50
41
  end
51
42
 
52
43
  def gets
53
- crlf = nil
54
-
55
- while (crlf = @buffer.index(CRLF)) == nil
56
- @buffer << _read_from_socket(1024)
44
+ while (crlf = @buffer.index(CRLF)).nil?
45
+ @buffer << _read_from_socket(16_384)
57
46
  end
58
47
 
59
48
  @buffer.slice!(0, crlf + CRLF.bytesize)
60
49
  end
61
50
 
62
51
  def _read_from_socket(nbytes)
63
-
64
52
  begin
65
53
  read_nonblock(nbytes)
66
-
67
54
  rescue IO::WaitReadable
68
55
  if IO.select([self], nil, nil, @timeout)
69
56
  retry
@@ -77,7 +64,6 @@ class Redis
77
64
  raise Redis::TimeoutError
78
65
  end
79
66
  end
80
-
81
67
  rescue EOFError
82
68
  raise Errno::ECONNRESET
83
69
  end
@@ -85,7 +71,6 @@ class Redis
85
71
  def _write_to_socket(data)
86
72
  begin
87
73
  write_nonblock(data)
88
-
89
74
  rescue IO::WaitWritable
90
75
  if IO.select(nil, [self], nil, @write_timeout)
91
76
  retry
@@ -99,7 +84,6 @@ class Redis
99
84
  raise Redis::TimeoutError
100
85
  end
101
86
  end
102
-
103
87
  rescue EOFError
104
88
  raise Errno::ECONNRESET
105
89
  end
@@ -114,6 +98,7 @@ class Redis
114
98
 
115
99
  total_count += count
116
100
  return total_count if total_count >= length
101
+
117
102
  data = data.byteslice(count..-1)
118
103
  end
119
104
  end
@@ -124,7 +109,6 @@ class Redis
124
109
  require "timeout"
125
110
 
126
111
  class TCPSocket < ::TCPSocket
127
-
128
112
  include SocketMixin
129
113
 
130
114
  def self.connect(host, port, timeout)
@@ -140,7 +124,6 @@ class Redis
140
124
  if defined?(::UNIXSocket)
141
125
 
142
126
  class UNIXSocket < ::UNIXSocket
143
-
144
127
  include SocketMixin
145
128
 
146
129
  def self.connect(path, timeout)
@@ -158,7 +141,6 @@ class Redis
158
141
 
159
142
  def _read_from_socket(nbytes)
160
143
  readpartial(nbytes)
161
-
162
144
  rescue EOFError
163
145
  raise Errno::ECONNRESET
164
146
  end
@@ -169,19 +151,16 @@ class Redis
169
151
  else
170
152
 
171
153
  class TCPSocket < ::Socket
172
-
173
154
  include SocketMixin
174
155
 
175
- def self.connect_addrinfo(ai, port, timeout)
176
- sock = new(::Socket.const_get(ai[0]), Socket::SOCK_STREAM, 0)
177
- sockaddr = ::Socket.pack_sockaddr_in(port, ai[3])
156
+ def self.connect_addrinfo(addrinfo, port, timeout)
157
+ sock = new(::Socket.const_get(addrinfo[0]), Socket::SOCK_STREAM, 0)
158
+ sockaddr = ::Socket.pack_sockaddr_in(port, addrinfo[3])
178
159
 
179
160
  begin
180
161
  sock.connect_nonblock(sockaddr)
181
162
  rescue Errno::EINPROGRESS
182
- if IO.select(nil, [sock], nil, timeout) == nil
183
- raise TimeoutError
184
- end
163
+ raise TimeoutError if IO.select(nil, [sock], nil, timeout).nil?
185
164
 
186
165
  begin
187
166
  sock.connect_nonblock(sockaddr)
@@ -220,14 +199,13 @@ class Redis
220
199
  return connect_addrinfo(ai, port, timeout)
221
200
  rescue SystemCallError
222
201
  # Raise if this was our last attempt.
223
- raise if addrinfo.length == i+1
202
+ raise if addrinfo.length == i + 1
224
203
  end
225
204
  end
226
205
  end
227
206
  end
228
207
 
229
208
  class UNIXSocket < ::Socket
230
-
231
209
  include SocketMixin
232
210
 
233
211
  def self.connect(path, timeout)
@@ -237,9 +215,7 @@ class Redis
237
215
  begin
238
216
  sock.connect_nonblock(sockaddr)
239
217
  rescue Errno::EINPROGRESS
240
- if IO.select(nil, [sock], nil, timeout) == nil
241
- raise TimeoutError
242
- end
218
+ raise TimeoutError if IO.select(nil, [sock], nil, timeout).nil?
243
219
 
244
220
  begin
245
221
  sock.connect_nonblock(sockaddr)
@@ -262,7 +238,9 @@ class Redis
262
238
  tcp_sock = TCPSocket.connect(host, port, timeout)
263
239
 
264
240
  ctx = OpenSSL::SSL::SSLContext.new
265
- ctx.set_params(ssl_params) if ssl_params && !ssl_params.empty?
241
+
242
+ # The provided parameters are merged into OpenSSL::SSL::SSLContext::DEFAULT_PARAMS
243
+ ctx.set_params(ssl_params || {})
266
244
 
267
245
  ssl_sock = new(tcp_sock, ctx)
268
246
  ssl_sock.hostname = host
@@ -289,7 +267,10 @@ class Redis
289
267
  end
290
268
  end
291
269
 
292
- unless ctx.verify_mode == OpenSSL::SSL::VERIFY_NONE
270
+ unless ctx.verify_mode == OpenSSL::SSL::VERIFY_NONE || (
271
+ ctx.respond_to?(:verify_hostname) &&
272
+ !ctx.verify_hostname
273
+ )
293
274
  ssl_sock.post_connection_check(host)
294
275
  end
295
276
 
@@ -301,15 +282,16 @@ class Redis
301
282
  class Ruby
302
283
  include Redis::Connection::CommandHelper
303
284
 
304
- MINUS = "-".freeze
305
- PLUS = "+".freeze
306
- COLON = ":".freeze
307
- DOLLAR = "$".freeze
308
- ASTERISK = "*".freeze
285
+ MINUS = "-"
286
+ PLUS = "+"
287
+ COLON = ":"
288
+ DOLLAR = "$"
289
+ ASTERISK = "*"
309
290
 
310
291
  def self.connect(config)
311
292
  if config[:scheme] == "unix"
312
293
  raise ArgumentError, "SSL incompatible with unix sockets" if config[:ssl]
294
+
313
295
  sock = UNIXSocket.connect(config[:path], config[:connect_timeout])
314
296
  elsif config[:scheme] == "rediss" || config[:ssl]
315
297
  sock = SSLSocket.connect(config[:host], config[:port], config[:connect_timeout], config[:ssl_params])
@@ -321,10 +303,11 @@ class Redis
321
303
  instance.timeout = config[:read_timeout]
322
304
  instance.write_timeout = config[:write_timeout]
323
305
  instance.set_tcp_keepalive config[:tcp_keepalive]
306
+ instance.set_tcp_nodelay if sock.is_a? TCPSocket
324
307
  instance
325
308
  end
326
309
 
327
- if [:SOL_SOCKET, :SO_KEEPALIVE, :SOL_TCP, :TCP_KEEPIDLE, :TCP_KEEPINTVL, :TCP_KEEPCNT].all?{|c| Socket.const_defined? c}
310
+ if %i[SOL_SOCKET SO_KEEPALIVE SOL_TCP TCP_KEEPIDLE TCP_KEEPINTVL TCP_KEEPCNT].all? { |c| Socket.const_defined? c }
328
311
  def set_tcp_keepalive(keepalive)
329
312
  return unless keepalive.is_a?(Hash)
330
313
 
@@ -336,14 +319,13 @@ class Redis
336
319
 
337
320
  def get_tcp_keepalive
338
321
  {
339
- :time => @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPIDLE).int,
340
- :intvl => @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPINTVL).int,
341
- :probes => @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPCNT).int,
322
+ time: @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPIDLE).int,
323
+ intvl: @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPINTVL).int,
324
+ probes: @sock.getsockopt(Socket::SOL_TCP, Socket::TCP_KEEPCNT).int
342
325
  }
343
326
  end
344
327
  else
345
- def set_tcp_keepalive(keepalive)
346
- end
328
+ def set_tcp_keepalive(keepalive); end
347
329
 
348
330
  def get_tcp_keepalive
349
331
  {
@@ -351,12 +333,21 @@ class Redis
351
333
  end
352
334
  end
353
335
 
336
+ # disables Nagle's Algorithm, prevents multiple round trips with MULTI
337
+ if %i[IPPROTO_TCP TCP_NODELAY].all? { |c| Socket.const_defined? c }
338
+ def set_tcp_nodelay
339
+ @sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
340
+ end
341
+ else
342
+ def set_tcp_nodelay; end
343
+ end
344
+
354
345
  def initialize(sock)
355
346
  @sock = sock
356
347
  end
357
348
 
358
349
  def connected?
359
- !! @sock
350
+ !!@sock
360
351
  end
361
352
 
362
353
  def disconnect
@@ -367,9 +358,7 @@ class Redis
367
358
  end
368
359
 
369
360
  def timeout=(timeout)
370
- if @sock.respond_to?(:timeout=)
371
- @sock.timeout = timeout
372
- end
361
+ @sock.timeout = timeout if @sock.respond_to?(:timeout=)
373
362
  end
374
363
 
375
364
  def write_timeout=(timeout)
@@ -384,7 +373,6 @@ class Redis
384
373
  line = @sock.gets
385
374
  reply_type = line.slice!(0, 1)
386
375
  format_reply(reply_type, line)
387
-
388
376
  rescue Errno::EAGAIN
389
377
  raise TimeoutError
390
378
  end
@@ -396,7 +384,7 @@ class Redis
396
384
  when COLON then format_integer_reply(line)
397
385
  when DOLLAR then format_bulk_reply(line)
398
386
  when ASTERISK then format_multi_bulk_reply(line)
399
- else raise ProtocolError.new(reply_type)
387
+ else raise ProtocolError, reply_type
400
388
  end
401
389
  end
402
390
 
@@ -415,6 +403,7 @@ class Redis
415
403
  def format_bulk_reply(line)
416
404
  bulklen = line.to_i
417
405
  return if bulklen == -1
406
+
418
407
  reply = encode(@sock.read(bulklen))
419
408
  @sock.read(2) # Discard CRLF.
420
409
  reply
@@ -1,9 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative "command_helper"
2
4
  require_relative "registry"
3
5
  require_relative "../errors"
4
6
  require "em-synchrony"
5
7
  require "hiredis/reader"
6
8
 
9
+ Kernel.warn(
10
+ "The redis synchrony driver is deprecated and will be removed in redis-rb 5.0. " \
11
+ "We're looking for people to maintain it as a separate gem, see https://github.com/redis/redis-rb/issues/915"
12
+ )
13
+
7
14
  class Redis
8
15
  module Connection
9
16
  class RedisClient < EventMachine::Connection
@@ -46,9 +53,7 @@ class Redis
46
53
 
47
54
  def read
48
55
  @req = EventMachine::DefaultDeferrable.new
49
- if @timeout > 0
50
- @req.timeout(@timeout, :timeout)
51
- end
56
+ @req.timeout(@timeout, :timeout) if @timeout > 0
52
57
  EventMachine::Synchrony.sync @req
53
58
  end
54
59
 
@@ -105,7 +110,7 @@ class Redis
105
110
  end
106
111
 
107
112
  def connected?
108
- @connection && @connection.connected?
113
+ @connection&.connected?
109
114
  end
110
115
 
111
116
  def timeout=(timeout)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative "connection/registry"
2
4
 
3
5
  # If a connection driver was required before this file, the array