redis 4.1.0 → 4.6.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 (43) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +158 -0
  3. data/README.md +91 -27
  4. data/lib/redis/client.rb +148 -92
  5. data/lib/redis/cluster/command.rb +4 -6
  6. data/lib/redis/cluster/command_loader.rb +6 -7
  7. data/lib/redis/cluster/node.rb +17 -1
  8. data/lib/redis/cluster/node_key.rb +3 -7
  9. data/lib/redis/cluster/option.rb +30 -14
  10. data/lib/redis/cluster/slot.rb +30 -13
  11. data/lib/redis/cluster/slot_loader.rb +4 -4
  12. data/lib/redis/cluster.rb +46 -17
  13. data/lib/redis/commands/bitmaps.rb +63 -0
  14. data/lib/redis/commands/cluster.rb +45 -0
  15. data/lib/redis/commands/connection.rb +58 -0
  16. data/lib/redis/commands/geo.rb +84 -0
  17. data/lib/redis/commands/hashes.rb +251 -0
  18. data/lib/redis/commands/hyper_log_log.rb +37 -0
  19. data/lib/redis/commands/keys.rb +411 -0
  20. data/lib/redis/commands/lists.rb +289 -0
  21. data/lib/redis/commands/pubsub.rb +72 -0
  22. data/lib/redis/commands/scripting.rb +114 -0
  23. data/lib/redis/commands/server.rb +188 -0
  24. data/lib/redis/commands/sets.rb +207 -0
  25. data/lib/redis/commands/sorted_sets.rb +804 -0
  26. data/lib/redis/commands/streams.rb +382 -0
  27. data/lib/redis/commands/strings.rb +313 -0
  28. data/lib/redis/commands/transactions.rb +92 -0
  29. data/lib/redis/commands.rb +242 -0
  30. data/lib/redis/connection/command_helper.rb +5 -2
  31. data/lib/redis/connection/hiredis.rb +7 -5
  32. data/lib/redis/connection/registry.rb +2 -1
  33. data/lib/redis/connection/ruby.rb +129 -110
  34. data/lib/redis/connection/synchrony.rb +17 -10
  35. data/lib/redis/connection.rb +3 -1
  36. data/lib/redis/distributed.rb +209 -70
  37. data/lib/redis/errors.rb +2 -0
  38. data/lib/redis/hash_ring.rb +15 -14
  39. data/lib/redis/pipeline.rb +139 -8
  40. data/lib/redis/subscribe.rb +11 -12
  41. data/lib/redis/version.rb +3 -1
  42. data/lib/redis.rb +167 -3377
  43. metadata +32 -25
data/lib/redis/client.rb CHANGED
@@ -1,29 +1,38 @@
1
- require_relative "errors"
1
+ # frozen_string_literal: true
2
+
2
3
  require "socket"
3
4
  require "cgi"
5
+ require "redis/errors"
4
6
 
5
7
  class Redis
6
8
  class Client
7
-
9
+ # Defaults are also used for converting string keys to symbols.
8
10
  DEFAULTS = {
9
- :url => lambda { ENV["REDIS_URL"] },
10
- :scheme => "redis",
11
- :host => "127.0.0.1",
12
- :port => 6379,
13
- :path => nil,
14
- :timeout => 5.0,
15
- :password => nil,
16
- :db => 0,
17
- :driver => nil,
18
- :id => nil,
19
- :tcp_keepalive => 0,
20
- :reconnect_attempts => 1,
21
- :reconnect_delay => 0,
22
- :reconnect_delay_max => 0.5,
23
- :inherit_socket => false
24
- }
25
-
26
- attr_reader :options
11
+ url: -> { ENV["REDIS_URL"] },
12
+ scheme: "redis",
13
+ host: "127.0.0.1",
14
+ port: 6379,
15
+ path: nil,
16
+ read_timeout: nil,
17
+ write_timeout: nil,
18
+ connect_timeout: nil,
19
+ timeout: 5.0,
20
+ username: nil,
21
+ password: nil,
22
+ db: 0,
23
+ driver: nil,
24
+ id: nil,
25
+ tcp_keepalive: 0,
26
+ reconnect_attempts: 1,
27
+ reconnect_delay: 0,
28
+ reconnect_delay_max: 0.5,
29
+ inherit_socket: false,
30
+ logger: nil,
31
+ sentinels: nil,
32
+ role: nil
33
+ }.freeze
34
+
35
+ attr_reader :options, :connection, :command_map
27
36
 
28
37
  def scheme
29
38
  @options[:scheme]
@@ -53,6 +62,10 @@ class Redis
53
62
  @options[:read_timeout]
54
63
  end
55
64
 
65
+ def username
66
+ @options[:username]
67
+ end
68
+
56
69
  def password
57
70
  @options[:password]
58
71
  end
@@ -74,8 +87,6 @@ class Redis
74
87
  end
75
88
 
76
89
  attr_accessor :logger
77
- attr_reader :connection
78
- attr_reader :command_map
79
90
 
80
91
  def initialize(options = {})
81
92
  @options = _parse_options(options)
@@ -87,7 +98,7 @@ class Redis
87
98
  @pending_reads = 0
88
99
 
89
100
  @connector =
90
- if options.include?(:sentinels)
101
+ if !@options[:sentinels].nil?
91
102
  Connector::Sentinel.new(@options)
92
103
  elsif options.include?(:connector) && options[:connector].respond_to?(:new)
93
104
  options.delete(:connector).new(@options)
@@ -102,7 +113,34 @@ class Redis
102
113
  # Don't try to reconnect when the connection is fresh
103
114
  with_reconnect(false) do
104
115
  establish_connection
105
- call [:auth, password] if password
116
+ if password
117
+ if username
118
+ begin
119
+ call [:auth, username, password]
120
+ rescue CommandError => err # Likely on Redis < 6
121
+ case err.message
122
+ when /ERR wrong number of arguments for 'auth' command/
123
+ call [:auth, password]
124
+ when /WRONGPASS invalid username-password pair/
125
+ begin
126
+ call [:auth, password]
127
+ rescue CommandError
128
+ raise err
129
+ end
130
+ ::Redis.deprecate!(
131
+ "[redis-rb] The Redis connection was configured with username #{username.inspect}, but" \
132
+ " the provided password was for the default user. This will start failing in redis-rb 5.0.0."
133
+ )
134
+ else
135
+ raise
136
+ end
137
+ end
138
+ else
139
+ call [:auth, password]
140
+ end
141
+ end
142
+
143
+ call [:readonly] if @options[:readonly]
106
144
  call [:select, db] if db != 0
107
145
  call [:client, :setname, @options[:id]] if @options[:id]
108
146
  @connector.check(self)
@@ -123,7 +161,7 @@ class Redis
123
161
  reply = process([command]) { read }
124
162
  raise reply if reply.is_a?(CommandError)
125
163
 
126
- if block_given?
164
+ if block_given? && reply != 'QUEUED'
127
165
  yield reply
128
166
  else
129
167
  reply
@@ -155,16 +193,16 @@ class Redis
155
193
  end
156
194
 
157
195
  def call_pipeline(pipeline)
158
- commands = pipeline.commands
159
- return [] if commands.empty?
196
+ return [] if pipeline.futures.empty?
160
197
 
161
198
  with_reconnect pipeline.with_reconnect? do
162
199
  begin
163
- pipeline.finish(call_pipelined(commands)).tap do
200
+ pipeline.finish(call_pipelined(pipeline)).tap do
164
201
  self.db = pipeline.db if pipeline.db
165
202
  end
166
203
  rescue ConnectionError => e
167
204
  return nil if pipeline.shutdown?
205
+
168
206
  # Assume the pipeline was sent in one piece, but execution of
169
207
  # SHUTDOWN caused none of the replies for commands that were executed
170
208
  # prior to it from coming back around.
@@ -173,8 +211,8 @@ class Redis
173
211
  end
174
212
  end
175
213
 
176
- def call_pipelined(commands)
177
- return [] if commands.empty?
214
+ def call_pipelined(pipeline)
215
+ return [] if pipeline.futures.empty?
178
216
 
179
217
  # The method #ensure_connected (called from #process) reconnects once on
180
218
  # I/O errors. To make an effort in making sure that commands are not
@@ -184,6 +222,8 @@ class Redis
184
222
  # already successfully executed commands. To circumvent this, don't retry
185
223
  # after the first reply has been read successfully.
186
224
 
225
+ commands = pipeline.commands
226
+
187
227
  result = Array.new(commands.size)
188
228
  reconnect = @reconnect
189
229
 
@@ -191,8 +231,12 @@ class Redis
191
231
  exception = nil
192
232
 
193
233
  process(commands) do
194
- commands.size.times do |i|
195
- reply = read
234
+ pipeline.timeouts.each_with_index do |timeout, i|
235
+ reply = if timeout
236
+ with_socket_timeout(timeout) { read }
237
+ else
238
+ read
239
+ end
196
240
  result[i] = reply
197
241
  @reconnect = false
198
242
  exception = reply if exception.nil? && reply.is_a?(CommandError)
@@ -207,7 +251,8 @@ class Redis
207
251
  result
208
252
  end
209
253
 
210
- def call_with_timeout(command, timeout, &blk)
254
+ def call_with_timeout(command, extra_timeout, &blk)
255
+ timeout = extra_timeout == 0 ? 0 : self.timeout + extra_timeout
211
256
  with_socket_timeout(timeout) do
212
257
  call(command, &blk)
213
258
  end
@@ -237,12 +282,13 @@ class Redis
237
282
  end
238
283
 
239
284
  def connected?
240
- !! (connection && connection.connected?)
285
+ !!(connection && connection.connected?)
241
286
  end
242
287
 
243
288
  def disconnect
244
289
  connection.disconnect if connected?
245
290
  end
291
+ alias close disconnect
246
292
 
247
293
  def reconnect
248
294
  disconnect
@@ -293,30 +339,27 @@ class Redis
293
339
  with_socket_timeout(0, &blk)
294
340
  end
295
341
 
296
- def with_reconnect(val=true)
297
- begin
298
- original, @reconnect = @reconnect, val
299
- yield
300
- ensure
301
- @reconnect = original
302
- end
342
+ def with_reconnect(val = true)
343
+ original, @reconnect = @reconnect, val
344
+ yield
345
+ ensure
346
+ @reconnect = original
303
347
  end
304
348
 
305
349
  def without_reconnect(&blk)
306
350
  with_reconnect(false, &blk)
307
351
  end
308
352
 
309
- protected
353
+ protected
310
354
 
311
355
  def logging(commands)
312
- return yield unless @logger && @logger.debug?
356
+ return yield unless @logger&.debug?
313
357
 
314
358
  begin
315
359
  commands.each do |name, *args|
316
360
  logged_args = args.map do |a|
317
- case
318
- when a.respond_to?(:inspect) then a.inspect
319
- when a.respond_to?(:to_s) then a.to_s
361
+ if a.respond_to?(:inspect) then a.inspect
362
+ elsif a.respond_to?(:to_s) then a.to_s
320
363
  else
321
364
  # handle poorly-behaved descendants of BasicObject
322
365
  klass = a.instance_exec { (class << self; self end).superclass }
@@ -343,14 +386,16 @@ class Redis
343
386
  @pending_reads = 0
344
387
  rescue TimeoutError,
345
388
  SocketError,
389
+ Errno::EADDRNOTAVAIL,
346
390
  Errno::ECONNREFUSED,
347
391
  Errno::EHOSTDOWN,
348
392
  Errno::EHOSTUNREACH,
349
393
  Errno::ENETUNREACH,
350
394
  Errno::ENOENT,
351
- Errno::ETIMEDOUT
395
+ Errno::ETIMEDOUT,
396
+ Errno::EINVAL => error
352
397
 
353
- raise CannotConnectError, "Error connecting to Redis on #{location} (#{$!.class})"
398
+ raise CannotConnectError, "Error connecting to Redis on #{location} (#{error.class})"
354
399
  end
355
400
 
356
401
  def ensure_connected
@@ -364,9 +409,9 @@ class Redis
364
409
  if connected?
365
410
  unless inherit_socket? || Process.pid == @pid
366
411
  raise InheritedError,
367
- "Tried to use a connection from a child process without reconnecting. " +
368
- "You need to reconnect to Redis after forking " +
369
- "or set :inherit_socket to true."
412
+ "Tried to use a connection from a child process without reconnecting. " \
413
+ "You need to reconnect to Redis after forking " \
414
+ "or set :inherit_socket to true."
370
415
  end
371
416
  else
372
417
  connect
@@ -377,7 +422,7 @@ class Redis
377
422
  disconnect
378
423
 
379
424
  if attempts <= @options[:reconnect_attempts] && @reconnect
380
- sleep_t = [(@options[:reconnect_delay] * 2**(attempts-1)),
425
+ sleep_t = [(@options[:reconnect_delay] * 2**(attempts - 1)),
381
426
  @options[:reconnect_delay_max]].min
382
427
 
383
428
  Kernel.sleep(sleep_t)
@@ -397,17 +442,16 @@ class Redis
397
442
  defaults = DEFAULTS.dup
398
443
  options = options.dup
399
444
 
400
- defaults.keys.each do |key|
445
+ defaults.each_key do |key|
401
446
  # Fill in defaults if needed
402
- if defaults[key].respond_to?(:call)
403
- defaults[key] = defaults[key].call
404
- end
447
+ defaults[key] = defaults[key].call if defaults[key].respond_to?(:call)
405
448
 
406
449
  # Symbolize only keys that are needed
407
- options[key] = options[key.to_s] if options.has_key?(key.to_s)
450
+ options[key] = options[key.to_s] if options.key?(key.to_s)
408
451
  end
409
452
 
410
- url = options[:url] || defaults[:url]
453
+ url = options[:url]
454
+ url = defaults[:url] if url.nil?
411
455
 
412
456
  # Override defaults from URL if given
413
457
  if url
@@ -415,13 +459,15 @@ class Redis
415
459
 
416
460
  uri = URI(url)
417
461
 
418
- if uri.scheme == "unix"
419
- defaults[:path] = uri.path
420
- elsif uri.scheme == "redis" || uri.scheme == "rediss"
462
+ case uri.scheme
463
+ when "unix"
464
+ defaults[:path] = uri.path
465
+ when "redis", "rediss"
421
466
  defaults[:scheme] = uri.scheme
422
467
  defaults[:host] = uri.host if uri.host
423
468
  defaults[:port] = uri.port if uri.port
424
- defaults[:password] = CGI.unescape(uri.password) if uri.password
469
+ defaults[:username] = CGI.unescape(uri.user) if uri.user && !uri.user.empty?
470
+ defaults[:password] = CGI.unescape(uri.password) if uri.password && !uri.password.empty?
425
471
  defaults[:db] = uri.path[1..-1].to_i if uri.path
426
472
  defaults[:role] = :master
427
473
  else
@@ -432,7 +478,7 @@ class Redis
432
478
  end
433
479
 
434
480
  # Use default when option is not specified or nil
435
- defaults.keys.each do |key|
481
+ defaults.each_key do |key|
436
482
  options[key] = defaults[key] if options[key].nil?
437
483
  end
438
484
 
@@ -447,7 +493,7 @@ class Redis
447
493
  options[:port] = options[:port].to_i
448
494
  end
449
495
 
450
- if options.has_key?(:timeout)
496
+ if options.key?(:timeout)
451
497
  options[:connect_timeout] ||= options[:timeout]
452
498
  options[:read_timeout] ||= options[:timeout]
453
499
  options[:write_timeout] ||= options[:timeout]
@@ -466,7 +512,7 @@ class Redis
466
512
 
467
513
  case options[:tcp_keepalive]
468
514
  when Hash
469
- [:time, :intvl, :probes].each do |key|
515
+ %i[time intvl probes].each do |key|
470
516
  unless options[:tcp_keepalive][key].is_a?(Integer)
471
517
  raise "Expected the #{key.inspect} key in :tcp_keepalive to be an Integer"
472
518
  end
@@ -474,13 +520,13 @@ class Redis
474
520
 
475
521
  when Integer
476
522
  if options[:tcp_keepalive] >= 60
477
- options[:tcp_keepalive] = {:time => options[:tcp_keepalive] - 20, :intvl => 10, :probes => 2}
523
+ options[:tcp_keepalive] = { time: options[:tcp_keepalive] - 20, intvl: 10, probes: 2 }
478
524
 
479
525
  elsif options[:tcp_keepalive] >= 30
480
- options[:tcp_keepalive] = {:time => options[:tcp_keepalive] - 10, :intvl => 5, :probes => 2}
526
+ options[:tcp_keepalive] = { time: options[:tcp_keepalive] - 10, intvl: 5, probes: 2 }
481
527
 
482
528
  elsif options[:tcp_keepalive] >= 5
483
- options[:tcp_keepalive] = {:time => options[:tcp_keepalive] - 2, :intvl => 2, :probes => 1}
529
+ options[:tcp_keepalive] = { time: options[:tcp_keepalive] - 2, intvl: 2, probes: 1 }
484
530
  end
485
531
  end
486
532
 
@@ -492,14 +538,14 @@ class Redis
492
538
  def _parse_driver(driver)
493
539
  driver = driver.to_s if driver.is_a?(Symbol)
494
540
 
495
- if driver.kind_of?(String)
541
+ if driver.is_a?(String)
496
542
  begin
497
543
  require_relative "connection/#{driver}"
498
- rescue LoadError, NameError => e
544
+ rescue LoadError, NameError
499
545
  begin
500
- require "connection/#{driver}"
501
- rescue LoadError, NameError => e
502
- raise RuntimeError, "Cannot load driver #{driver.inspect}: #{e.message}"
546
+ require "redis/connection/#{driver}"
547
+ rescue LoadError, NameError => error
548
+ raise "Cannot load driver #{driver.inspect}: #{error.message}"
503
549
  end
504
550
  end
505
551
 
@@ -518,18 +564,16 @@ class Redis
518
564
  @options
519
565
  end
520
566
 
521
- def check(client)
522
- end
567
+ def check(client); end
523
568
 
524
569
  class Sentinel < Connector
525
570
  def initialize(options)
526
571
  super(options)
527
572
 
528
- @options[:password] = DEFAULTS.fetch(:password)
529
573
  @options[:db] = DEFAULTS.fetch(:db)
530
574
 
531
575
  @sentinels = @options.delete(:sentinels).dup
532
- @role = @options.fetch(:role, "master").to_s
576
+ @role = (@options[:role] || "master").to_s
533
577
  @master = @options[:host]
534
578
  end
535
579
 
@@ -552,13 +596,13 @@ class Redis
552
596
 
553
597
  def resolve
554
598
  result = case @role
555
- when "master"
556
- resolve_master
557
- when "slave"
558
- resolve_slave
559
- else
560
- raise ArgumentError, "Unknown instance role #{@role}"
561
- end
599
+ when "master"
600
+ resolve_master
601
+ when "slave"
602
+ resolve_slave
603
+ else
604
+ raise ArgumentError, "Unknown instance role #{@role}"
605
+ end
562
606
 
563
607
  result || (raise ConnectionError, "Unable to fetch #{@role} via Sentinel.")
564
608
  end
@@ -566,10 +610,12 @@ class Redis
566
610
  def sentinel_detect
567
611
  @sentinels.each do |sentinel|
568
612
  client = Client.new(@options.merge({
569
- :host => sentinel[:host],
570
- :port => sentinel[:port],
571
- :reconnect_attempts => 0,
572
- }))
613
+ host: sentinel[:host] || sentinel["host"],
614
+ port: sentinel[:port] || sentinel["port"],
615
+ username: sentinel[:username] || sentinel["username"],
616
+ password: sentinel[:password] || sentinel["password"],
617
+ reconnect_attempts: 0
618
+ }))
573
619
 
574
620
  begin
575
621
  if result = yield(client)
@@ -591,7 +637,7 @@ class Redis
591
637
  def resolve_master
592
638
  sentinel_detect do |client|
593
639
  if reply = client.call(["sentinel", "get-master-addr-by-name", @master])
594
- {:host => reply[0], :port => reply[1]}
640
+ { host: reply[0], port: reply[1] }
595
641
  end
596
642
  end
597
643
  end
@@ -599,9 +645,19 @@ class Redis
599
645
  def resolve_slave
600
646
  sentinel_detect do |client|
601
647
  if reply = client.call(["sentinel", "slaves", @master])
602
- slave = Hash[*reply.sample]
603
-
604
- {:host => slave.fetch("ip"), :port => slave.fetch("port")}
648
+ slaves = reply.map { |s| s.each_slice(2).to_h }
649
+ slaves.each { |s| s['flags'] = s.fetch('flags').split(',') }
650
+ slaves.reject! { |s| s.fetch('flags').include?('s_down') }
651
+
652
+ if slaves.empty?
653
+ raise CannotConnectError, 'No slaves available.'
654
+ else
655
+ slave = slaves.sample
656
+ {
657
+ host: slave.fetch('ip'),
658
+ port: slave.fetch('port')
659
+ }
660
+ end
605
661
  end
606
662
  end
607
663
  end
@@ -31,13 +31,13 @@ class Redis
31
31
  private
32
32
 
33
33
  def pick_details(details)
34
- details.map do |command, detail|
35
- [command, {
34
+ details.transform_values do |detail|
35
+ {
36
36
  first_key_position: detail[:first],
37
37
  write: detail[:flags].include?('write'),
38
38
  readonly: detail[:flags].include?('readonly')
39
- }]
40
- end.to_h
39
+ }
40
+ end
41
41
  end
42
42
 
43
43
  def dig_details(command, key)
@@ -53,8 +53,6 @@ class Redis
53
53
  when 'object' then 2
54
54
  when 'memory'
55
55
  command[1].to_s.casecmp('usage').zero? ? 2 : 0
56
- when 'scan', 'sscan', 'hscan', 'zscan'
57
- determine_optional_key_position(command, 'match')
58
56
  when 'xread', 'xreadgroup'
59
57
  determine_optional_key_position(command, 'streams')
60
58
  else
@@ -10,22 +10,21 @@ class Redis
10
10
  module_function
11
11
 
12
12
  def load(nodes)
13
- details = {}
14
-
15
13
  nodes.each do |node|
16
- details = fetch_command_details(node)
17
- details.empty? ? next : break
14
+ begin
15
+ return fetch_command_details(node)
16
+ rescue CannotConnectError, ConnectionError, CommandError
17
+ next # can retry on another node
18
+ end
18
19
  end
19
20
 
20
- details
21
+ raise CannotConnectError, 'Redis client could not connect to any cluster nodes'
21
22
  end
22
23
 
23
24
  def fetch_command_details(node)
24
25
  node.call(%i[command]).map do |reply|
25
26
  [reply[0], { arity: reply[1], flags: reply[2], first: reply[3], last: reply[4], step: reply[5] }]
26
27
  end.to_h
27
- rescue CannotConnectError, ConnectionError, CommandError
28
- {} # can retry on another node
29
28
  end
30
29
 
31
30
  private_class_method :fetch_command_details
@@ -39,6 +39,7 @@ class Redis
39
39
  def call_master(command, &block)
40
40
  try_map do |node_key, client|
41
41
  next if slave?(node_key)
42
+
42
43
  client.call(command, &block)
43
44
  end.values
44
45
  end
@@ -48,6 +49,7 @@ class Redis
48
49
 
49
50
  try_map do |node_key, client|
50
51
  next if master?(node_key)
52
+
51
53
  client.call(command, &block)
52
54
  end.values
53
55
  end
@@ -56,6 +58,18 @@ class Redis
56
58
  try_map { |_, client| client.process(commands, &block) }.values
57
59
  end
58
60
 
61
+ def scale_reading_clients
62
+ reading_clients = []
63
+
64
+ @clients.each do |node_key, client|
65
+ next unless replica_disabled? ? master?(node_key) : slave?(node_key)
66
+
67
+ reading_clients << client
68
+ end
69
+
70
+ reading_clients
71
+ end
72
+
59
73
  private
60
74
 
61
75
  def replica_disabled?
@@ -74,8 +88,9 @@ class Redis
74
88
  clients = options.map do |node_key, option|
75
89
  next if replica_disabled? && slave?(node_key)
76
90
 
91
+ option = option.merge(readonly: true) if slave?(node_key)
92
+
77
93
  client = Client.new(option)
78
- client.call(%i[readonly]) if slave?(node_key)
79
94
  [node_key, client]
80
95
  end
81
96
 
@@ -97,6 +112,7 @@ class Redis
97
112
  end
98
113
 
99
114
  return results if errors.empty?
115
+
100
116
  raise CommandErrorCollection, errors
101
117
  end
102
118
  end
@@ -6,17 +6,13 @@ class Redis
6
6
  # It is different from node id.
7
7
  # Node id is internal identifying code in Redis Cluster.
8
8
  module NodeKey
9
- DEFAULT_SCHEME = 'redis'
10
- SECURE_SCHEME = 'rediss'
11
9
  DELIMITER = ':'
12
10
 
13
11
  module_function
14
12
 
15
- def to_node_urls(node_keys, secure:)
16
- scheme = secure ? SECURE_SCHEME : DEFAULT_SCHEME
17
- node_keys
18
- .map { |k| k.split(DELIMITER) }
19
- .map { |k| URI::Generic.build(scheme: scheme, host: k[0], port: k[1].to_i).to_s }
13
+ def optionize(node_key)
14
+ host, port = split(node_key)
15
+ { host: host, port: port }
20
16
  end
21
17
 
22
18
  def split(node_key)