redis 4.1.1 → 4.2.5
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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +69 -0
- data/README.md +24 -5
- data/lib/redis/client.rb +71 -73
- data/lib/redis/cluster/node.rb +3 -0
- data/lib/redis/cluster/node_key.rb +3 -7
- data/lib/redis/cluster/option.rb +27 -14
- data/lib/redis/cluster/slot.rb +30 -13
- data/lib/redis/cluster/slot_loader.rb +4 -4
- data/lib/redis/cluster.rb +13 -4
- data/lib/redis/connection/command_helper.rb +3 -2
- data/lib/redis/connection/hiredis.rb +4 -3
- data/lib/redis/connection/registry.rb +2 -1
- data/lib/redis/connection/ruby.rb +118 -103
- data/lib/redis/connection/synchrony.rb +9 -4
- data/lib/redis/connection.rb +2 -0
- data/lib/redis/distributed.rb +117 -62
- data/lib/redis/errors.rb +2 -0
- data/lib/redis/hash_ring.rb +15 -14
- data/lib/redis/pipeline.rb +16 -3
- data/lib/redis/subscribe.rb +11 -12
- data/lib/redis/version.rb +3 -1
- data/lib/redis.rb +394 -354
- metadata +15 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 862e8133262fead707e4ca317b29d0e35425e3a7861ea1a52033bc91ba61bf6d
|
4
|
+
data.tar.gz: 5b2b32250d783fe58b0af54cc386f2568241644a0badc0b7247bb29b1ac94a93
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2aab289f4f22b2f3a804ca7b0da4cf95e352a8d246611490d8d803edebbbc7e7c299355cd0b90e6f3ac8bc0d11e9bf3792a328c95847d32e1d984729afe66ed2
|
7
|
+
data.tar.gz: d9ec8ba4d314d099e909cdddf6dfb4c3c14b853b46f45c4909ac3ba10fb1880bd9a7b0465257fa91f9a4ea6c9f82723c16c177810ac15c89fab3790d7af31ad0
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,74 @@
|
|
1
1
|
# Unreleased
|
2
2
|
|
3
|
+
# 4.2.5
|
4
|
+
|
5
|
+
* Optimize the ruby connector write buffering. See #964.
|
6
|
+
|
7
|
+
# 4.2.4
|
8
|
+
|
9
|
+
* Fix bytesize calculations in the ruby connector, and work on a copy of the buffer. Fix #961, #962.
|
10
|
+
|
11
|
+
# 4.2.3
|
12
|
+
|
13
|
+
* Use io/wait instead of IO.select in the ruby connector. See #960.
|
14
|
+
* Use exception free non blocking IOs in the ruby connector. See #926.
|
15
|
+
* Prevent corruption of the client when an interrupt happen during inside a pipeline block. See #945.
|
16
|
+
|
17
|
+
# 4.2.2
|
18
|
+
|
19
|
+
* Fix `WATCH` support for `Redis::Distributed`. See #941.
|
20
|
+
* Fix handling of empty stream responses. See #905, #929.
|
21
|
+
|
22
|
+
# 4.2.1
|
23
|
+
|
24
|
+
* Fix `exists?` returning an actual boolean when called with multiple keys. See #918.
|
25
|
+
* Setting `Redis.exists_returns_integer = false` disables warning message about new behaviour. See #920.
|
26
|
+
|
27
|
+
# 4.2.0
|
28
|
+
|
29
|
+
* Convert commands to accept keyword arguments rather than option hashes. This both help catching typos, and reduce needless allocations.
|
30
|
+
* Deprecate the synchrony driver. It will be removed in 5.0 and hopefully maintained as a separate gem. See #915.
|
31
|
+
* Make `Redis#exists` variadic, will return an Integer if called with multiple keys.
|
32
|
+
* Add `Redis#exists?` to get a Boolean if any of the keys exists.
|
33
|
+
* `Redis#exists` when called with a single key will warn that future versions will return an Integer.
|
34
|
+
Set `Redis.exists_returns_integer = true` to opt-in to the new behavior.
|
35
|
+
* Support `keepttl` ooption in `set`. See #913.
|
36
|
+
* Optimized initialization of Redis::Cluster. See #912.
|
37
|
+
* Accept sentinel options even with string key. See #599.
|
38
|
+
* Verify TLS connections by default. See #900.
|
39
|
+
* Make `Redis#hset` variadic. It now returns an integer, not a boolean. See #910.
|
40
|
+
|
41
|
+
# 4.1.4
|
42
|
+
|
43
|
+
* Alias `Redis#disconnect` as `#close`. See #901.
|
44
|
+
* Handle clusters with multiple slot ranges. See #894.
|
45
|
+
* Fix password authentication to a redis cluster. See #889.
|
46
|
+
* Handle recursive MOVED responses. See #882.
|
47
|
+
* Increase buffer size in the ruby connector. See #880.
|
48
|
+
* Fix thread safety of `Redis.queue`. See #878.
|
49
|
+
* Deprecate `Redis::Future#==` as it's likely to be a mistake. See #876.
|
50
|
+
* Support `KEEPTTL` option for SET command. See #913.
|
51
|
+
|
52
|
+
# 4.1.3
|
53
|
+
|
54
|
+
* Fix the client hanging forever when connecting with SSL to a non-SSL server. See #835.
|
55
|
+
|
56
|
+
# 4.1.2
|
57
|
+
|
58
|
+
* Fix several authentication problems with sentinel. See #850 and #856.
|
59
|
+
* Explicitly drop Ruby 2.2 support.
|
60
|
+
|
61
|
+
|
62
|
+
# 4.1.1
|
63
|
+
|
64
|
+
* Fix error handling in multi blocks. See #754.
|
65
|
+
* Fix geoadd to accept arrays like georadius and georadiusbymember. See #841.
|
66
|
+
* Fix georadius command failing when long == lat. See #841.
|
67
|
+
* Fix timeout error in xread block: 0. See #837.
|
68
|
+
* Fix incompatibility issue with redis-objects. See #834.
|
69
|
+
* Properly handle Errno::EADDRNOTAVAIL on connect.
|
70
|
+
* Fix password authentication to sentinel instances. See #813.
|
71
|
+
|
3
72
|
# 4.1.0
|
4
73
|
|
5
74
|
* Add Redis Cluster support. See #716.
|
data/README.md
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
-
# redis-rb [![Build Status][travis-image]][travis-link] [![Inline docs][inchpages-image]][inchpages-link]
|
1
|
+
# redis-rb [![Build Status][travis-image]][travis-link] [![Inline docs][inchpages-image]][inchpages-link] 
|
2
2
|
|
3
3
|
A Ruby client that tries to match [Redis][redis-home]' API one-to-one, while still
|
4
4
|
providing an idiomatic interface.
|
5
5
|
|
6
|
+
See [RubyDoc.info][rubydoc] for the API docs of the latest published gem.
|
6
7
|
|
7
8
|
## Getting started
|
8
9
|
|
@@ -34,6 +35,9 @@ You can also specify connection options as a [`redis://` URL][redis-url]:
|
|
34
35
|
redis = Redis.new(url: "redis://:p4ssw0rd@10.0.1.1:6380/15")
|
35
36
|
```
|
36
37
|
|
38
|
+
The client expects passwords with special chracters to be URL-encoded (i.e.
|
39
|
+
`CGI.escape(password)`).
|
40
|
+
|
37
41
|
By default, the client will try to read the `REDIS_URL` environment variable
|
38
42
|
and use that as URL to connect to. The above statement is therefore equivalent
|
39
43
|
to setting this environment variable and calling `Redis.new` without arguments.
|
@@ -95,6 +99,15 @@ but a few so that if one is down the client will try the next one. The client
|
|
95
99
|
is able to remember the last Sentinel that was able to reply correctly and will
|
96
100
|
use it for the next requests.
|
97
101
|
|
102
|
+
If you want to [authenticate](https://redis.io/topics/sentinel#configuring-sentinel-instances-with-authentication) Sentinel itself, you must specify the `password` option per instance.
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
SENTINELS = [{ host: '127.0.0.1', port: 26380, password: 'mysecret' },
|
106
|
+
{ host: '127.0.0.1', port: 26381, password: 'mysecret' }]
|
107
|
+
|
108
|
+
redis = Redis.new(host: 'mymaster', sentinels: SENTINELS, role: :master)
|
109
|
+
```
|
110
|
+
|
98
111
|
## Cluster support
|
99
112
|
|
100
113
|
`redis-rb` supports [clustering](https://redis.io/topics/cluster-spec).
|
@@ -133,12 +146,13 @@ redis.mget('{key}1', '{key}2')
|
|
133
146
|
```
|
134
147
|
|
135
148
|
* The client automatically reconnects after a failover occurred, but the caller is responsible for handling errors while it is happening.
|
149
|
+
* The client support permanent node failures, and will reroute requests to promoted slaves.
|
136
150
|
* The client supports `MOVED` and `ASK` redirections transparently.
|
137
151
|
|
138
152
|
## Storing objects
|
139
153
|
|
140
|
-
Redis
|
141
|
-
|
154
|
+
Redis "string" types can be used to store serialized Ruby objects, for
|
155
|
+
example with JSON:
|
142
156
|
|
143
157
|
```ruby
|
144
158
|
require "json"
|
@@ -251,6 +265,7 @@ All timeout values are specified in seconds.
|
|
251
265
|
When using pub/sub, you can subscribe to a channel using a timeout as well:
|
252
266
|
|
253
267
|
```ruby
|
268
|
+
redis = Redis.new(reconnect_attempts: 0)
|
254
269
|
redis.subscribe_with_timeout(5, "news") do |on|
|
255
270
|
on.message do |channel, message|
|
256
271
|
# ...
|
@@ -312,7 +327,7 @@ This library supports natively terminating client side SSL/TLS connections
|
|
312
327
|
when talking to Redis via a server-side proxy such as [stunnel], [hitch],
|
313
328
|
or [ghostunnel].
|
314
329
|
|
315
|
-
To enable SSL support, pass the `:ssl =>
|
330
|
+
To enable SSL support, pass the `:ssl => true` option when configuring the
|
316
331
|
Redis client, or pass in `:url => "rediss://..."` (like HTTPS for Redis).
|
317
332
|
You will also need to pass in an `:ssl_params => { ... }` hash used to
|
318
333
|
configure the `OpenSSL::SSL::SSLContext` object used for the connection:
|
@@ -427,6 +442,10 @@ redis = Redis.new(:driver => :synchrony)
|
|
427
442
|
This library is tested against recent Ruby and Redis versions.
|
428
443
|
Check [Travis][travis-link] for the exact versions supported.
|
429
444
|
|
445
|
+
## See Also
|
446
|
+
|
447
|
+
- [async-redis](https://github.com/socketry/async-redis) — An [async](https://github.com/socketry/async) compatible Redis client.
|
448
|
+
|
430
449
|
## Contributors
|
431
450
|
|
432
451
|
Several people contributed to redis-rb, but we would like to especially
|
@@ -437,7 +456,7 @@ client and evangelized Redis in Rubyland. Thank you, Ezra.
|
|
437
456
|
## Contributing
|
438
457
|
|
439
458
|
[Fork the project](https://github.com/redis/redis-rb) and send pull
|
440
|
-
requests.
|
459
|
+
requests.
|
441
460
|
|
442
461
|
|
443
462
|
[inchpages-image]: https://inch-ci.org/github/redis/redis-rb.svg
|
data/lib/redis/client.rb
CHANGED
@@ -6,24 +6,30 @@ require "cgi"
|
|
6
6
|
|
7
7
|
class Redis
|
8
8
|
class Client
|
9
|
-
|
9
|
+
# Defaults are also used for converting string keys to symbols.
|
10
10
|
DEFAULTS = {
|
11
|
-
:
|
12
|
-
:
|
13
|
-
:
|
14
|
-
:
|
15
|
-
:
|
16
|
-
:
|
17
|
-
:
|
18
|
-
:
|
19
|
-
:
|
20
|
-
:
|
21
|
-
:
|
22
|
-
:
|
23
|
-
:
|
24
|
-
:
|
25
|
-
:
|
26
|
-
|
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
|
+
password: nil,
|
21
|
+
db: 0,
|
22
|
+
driver: nil,
|
23
|
+
id: nil,
|
24
|
+
tcp_keepalive: 0,
|
25
|
+
reconnect_attempts: 1,
|
26
|
+
reconnect_delay: 0,
|
27
|
+
reconnect_delay_max: 0.5,
|
28
|
+
inherit_socket: false,
|
29
|
+
logger: nil,
|
30
|
+
sentinels: nil,
|
31
|
+
role: nil
|
32
|
+
}.freeze
|
27
33
|
|
28
34
|
attr_reader :options
|
29
35
|
|
@@ -89,7 +95,7 @@ class Redis
|
|
89
95
|
@pending_reads = 0
|
90
96
|
|
91
97
|
@connector =
|
92
|
-
if options.
|
98
|
+
if !@options[:sentinels].nil?
|
93
99
|
Connector::Sentinel.new(@options)
|
94
100
|
elsif options.include?(:connector) && options[:connector].respond_to?(:new)
|
95
101
|
options.delete(:connector).new(@options)
|
@@ -166,6 +172,7 @@ class Redis
|
|
166
172
|
end
|
167
173
|
rescue ConnectionError => e
|
168
174
|
return nil if pipeline.shutdown?
|
175
|
+
|
169
176
|
# Assume the pipeline was sent in one piece, but execution of
|
170
177
|
# SHUTDOWN caused none of the replies for commands that were executed
|
171
178
|
# prior to it from coming back around.
|
@@ -244,12 +251,13 @@ class Redis
|
|
244
251
|
end
|
245
252
|
|
246
253
|
def connected?
|
247
|
-
!!
|
254
|
+
!!(connection && connection.connected?)
|
248
255
|
end
|
249
256
|
|
250
257
|
def disconnect
|
251
258
|
connection.disconnect if connected?
|
252
259
|
end
|
260
|
+
alias close disconnect
|
253
261
|
|
254
262
|
def reconnect
|
255
263
|
disconnect
|
@@ -300,30 +308,27 @@ class Redis
|
|
300
308
|
with_socket_timeout(0, &blk)
|
301
309
|
end
|
302
310
|
|
303
|
-
def with_reconnect(val=true)
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
@reconnect = original
|
309
|
-
end
|
311
|
+
def with_reconnect(val = true)
|
312
|
+
original, @reconnect = @reconnect, val
|
313
|
+
yield
|
314
|
+
ensure
|
315
|
+
@reconnect = original
|
310
316
|
end
|
311
317
|
|
312
318
|
def without_reconnect(&blk)
|
313
319
|
with_reconnect(false, &blk)
|
314
320
|
end
|
315
321
|
|
316
|
-
|
322
|
+
protected
|
317
323
|
|
318
324
|
def logging(commands)
|
319
|
-
return yield unless @logger
|
325
|
+
return yield unless @logger&.debug?
|
320
326
|
|
321
327
|
begin
|
322
328
|
commands.each do |name, *args|
|
323
329
|
logged_args = args.map do |a|
|
324
|
-
|
325
|
-
|
326
|
-
when a.respond_to?(:to_s) then a.to_s
|
330
|
+
if a.respond_to?(:inspect) then a.inspect
|
331
|
+
elsif a.respond_to?(:to_s) then a.to_s
|
327
332
|
else
|
328
333
|
# handle poorly-behaved descendants of BasicObject
|
329
334
|
klass = a.instance_exec { (class << self; self end).superclass }
|
@@ -357,9 +362,9 @@ class Redis
|
|
357
362
|
Errno::ENETUNREACH,
|
358
363
|
Errno::ENOENT,
|
359
364
|
Errno::ETIMEDOUT,
|
360
|
-
Errno::EINVAL
|
365
|
+
Errno::EINVAL => error
|
361
366
|
|
362
|
-
raise CannotConnectError, "Error connecting to Redis on #{location} (#{
|
367
|
+
raise CannotConnectError, "Error connecting to Redis on #{location} (#{error.class})"
|
363
368
|
end
|
364
369
|
|
365
370
|
def ensure_connected
|
@@ -373,9 +378,9 @@ class Redis
|
|
373
378
|
if connected?
|
374
379
|
unless inherit_socket? || Process.pid == @pid
|
375
380
|
raise InheritedError,
|
376
|
-
|
377
|
-
|
378
|
-
|
381
|
+
"Tried to use a connection from a child process without reconnecting. " \
|
382
|
+
"You need to reconnect to Redis after forking " \
|
383
|
+
"or set :inherit_socket to true."
|
379
384
|
end
|
380
385
|
else
|
381
386
|
connect
|
@@ -386,7 +391,7 @@ class Redis
|
|
386
391
|
disconnect
|
387
392
|
|
388
393
|
if attempts <= @options[:reconnect_attempts] && @reconnect
|
389
|
-
sleep_t = [(@options[:reconnect_delay] * 2**(attempts-1)),
|
394
|
+
sleep_t = [(@options[:reconnect_delay] * 2**(attempts - 1)),
|
390
395
|
@options[:reconnect_delay_max]].min
|
391
396
|
|
392
397
|
Kernel.sleep(sleep_t)
|
@@ -408,16 +413,14 @@ class Redis
|
|
408
413
|
|
409
414
|
defaults.keys.each do |key|
|
410
415
|
# Fill in defaults if needed
|
411
|
-
if defaults[key].respond_to?(:call)
|
412
|
-
defaults[key] = defaults[key].call
|
413
|
-
end
|
416
|
+
defaults[key] = defaults[key].call if defaults[key].respond_to?(:call)
|
414
417
|
|
415
418
|
# Symbolize only keys that are needed
|
416
|
-
options[key] = options[key.to_s] if options.
|
419
|
+
options[key] = options[key.to_s] if options.key?(key.to_s)
|
417
420
|
end
|
418
421
|
|
419
422
|
url = options[:url]
|
420
|
-
url = defaults[:url] if url
|
423
|
+
url = defaults[:url] if url.nil?
|
421
424
|
|
422
425
|
# Override defaults from URL if given
|
423
426
|
if url
|
@@ -426,7 +429,7 @@ class Redis
|
|
426
429
|
uri = URI(url)
|
427
430
|
|
428
431
|
if uri.scheme == "unix"
|
429
|
-
defaults[:path]
|
432
|
+
defaults[:path] = uri.path
|
430
433
|
elsif uri.scheme == "redis" || uri.scheme == "rediss"
|
431
434
|
defaults[:scheme] = uri.scheme
|
432
435
|
defaults[:host] = uri.host if uri.host
|
@@ -457,7 +460,7 @@ class Redis
|
|
457
460
|
options[:port] = options[:port].to_i
|
458
461
|
end
|
459
462
|
|
460
|
-
if options.
|
463
|
+
if options.key?(:timeout)
|
461
464
|
options[:connect_timeout] ||= options[:timeout]
|
462
465
|
options[:read_timeout] ||= options[:timeout]
|
463
466
|
options[:write_timeout] ||= options[:timeout]
|
@@ -476,7 +479,7 @@ class Redis
|
|
476
479
|
|
477
480
|
case options[:tcp_keepalive]
|
478
481
|
when Hash
|
479
|
-
[
|
482
|
+
%i[time intvl probes].each do |key|
|
480
483
|
unless options[:tcp_keepalive][key].is_a?(Integer)
|
481
484
|
raise "Expected the #{key.inspect} key in :tcp_keepalive to be an Integer"
|
482
485
|
end
|
@@ -484,13 +487,13 @@ class Redis
|
|
484
487
|
|
485
488
|
when Integer
|
486
489
|
if options[:tcp_keepalive] >= 60
|
487
|
-
options[:tcp_keepalive] = {:
|
490
|
+
options[:tcp_keepalive] = { time: options[:tcp_keepalive] - 20, intvl: 10, probes: 2 }
|
488
491
|
|
489
492
|
elsif options[:tcp_keepalive] >= 30
|
490
|
-
options[:tcp_keepalive] = {:
|
493
|
+
options[:tcp_keepalive] = { time: options[:tcp_keepalive] - 10, intvl: 5, probes: 2 }
|
491
494
|
|
492
495
|
elsif options[:tcp_keepalive] >= 5
|
493
|
-
options[:tcp_keepalive] = {:
|
496
|
+
options[:tcp_keepalive] = { time: options[:tcp_keepalive] - 2, intvl: 2, probes: 1 }
|
494
497
|
end
|
495
498
|
end
|
496
499
|
|
@@ -502,14 +505,14 @@ class Redis
|
|
502
505
|
def _parse_driver(driver)
|
503
506
|
driver = driver.to_s if driver.is_a?(Symbol)
|
504
507
|
|
505
|
-
if driver.
|
508
|
+
if driver.is_a?(String)
|
506
509
|
begin
|
507
510
|
require_relative "connection/#{driver}"
|
508
|
-
rescue LoadError, NameError
|
511
|
+
rescue LoadError, NameError
|
509
512
|
begin
|
510
513
|
require "connection/#{driver}"
|
511
|
-
rescue LoadError, NameError =>
|
512
|
-
raise
|
514
|
+
rescue LoadError, NameError => error
|
515
|
+
raise "Cannot load driver #{driver.inspect}: #{error.message}"
|
513
516
|
end
|
514
517
|
end
|
515
518
|
|
@@ -528,8 +531,7 @@ class Redis
|
|
528
531
|
@options
|
529
532
|
end
|
530
533
|
|
531
|
-
def check(client)
|
532
|
-
end
|
534
|
+
def check(client); end
|
533
535
|
|
534
536
|
class Sentinel < Connector
|
535
537
|
def initialize(options)
|
@@ -538,7 +540,7 @@ class Redis
|
|
538
540
|
@options[:db] = DEFAULTS.fetch(:db)
|
539
541
|
|
540
542
|
@sentinels = @options.delete(:sentinels).dup
|
541
|
-
@role = @options
|
543
|
+
@role = (@options[:role] || "master").to_s
|
542
544
|
@master = @options[:host]
|
543
545
|
end
|
544
546
|
|
@@ -561,13 +563,13 @@ class Redis
|
|
561
563
|
|
562
564
|
def resolve
|
563
565
|
result = case @role
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
566
|
+
when "master"
|
567
|
+
resolve_master
|
568
|
+
when "slave"
|
569
|
+
resolve_slave
|
570
|
+
else
|
571
|
+
raise ArgumentError, "Unknown instance role #{@role}"
|
572
|
+
end
|
571
573
|
|
572
574
|
result || (raise ConnectionError, "Unable to fetch #{@role} via Sentinel.")
|
573
575
|
end
|
@@ -575,10 +577,11 @@ class Redis
|
|
575
577
|
def sentinel_detect
|
576
578
|
@sentinels.each do |sentinel|
|
577
579
|
client = Client.new(@options.merge({
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
580
|
+
host: sentinel[:host] || sentinel["host"],
|
581
|
+
port: sentinel[:port] || sentinel["port"],
|
582
|
+
password: sentinel[:password] || sentinel["password"],
|
583
|
+
reconnect_attempts: 0
|
584
|
+
}))
|
582
585
|
|
583
586
|
begin
|
584
587
|
if result = yield(client)
|
@@ -595,17 +598,12 @@ class Redis
|
|
595
598
|
end
|
596
599
|
|
597
600
|
raise CannotConnectError, "No sentinels available."
|
598
|
-
rescue Redis::CommandError => err
|
599
|
-
# this feature is only available starting with Redis 5.0.1
|
600
|
-
raise unless err.message.start_with?('ERR unknown command `auth`')
|
601
|
-
@options[:password] = DEFAULTS.fetch(:password)
|
602
|
-
retry
|
603
601
|
end
|
604
602
|
|
605
603
|
def resolve_master
|
606
604
|
sentinel_detect do |client|
|
607
605
|
if reply = client.call(["sentinel", "get-master-addr-by-name", @master])
|
608
|
-
{:
|
606
|
+
{ host: reply[0], port: reply[1] }
|
609
607
|
end
|
610
608
|
end
|
611
609
|
end
|
@@ -623,7 +621,7 @@ class Redis
|
|
623
621
|
slave = slaves.sample
|
624
622
|
{
|
625
623
|
host: slave.fetch('ip'),
|
626
|
-
port: slave.fetch('port')
|
624
|
+
port: slave.fetch('port')
|
627
625
|
}
|
628
626
|
end
|
629
627
|
end
|
data/lib/redis/cluster/node.rb
CHANGED
@@ -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
|
@@ -97,6 +99,7 @@ class Redis
|
|
97
99
|
end
|
98
100
|
|
99
101
|
return results if errors.empty?
|
102
|
+
|
100
103
|
raise CommandErrorCollection, errors
|
101
104
|
end
|
102
105
|
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
|
16
|
-
|
17
|
-
|
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)
|
data/lib/redis/cluster/option.rb
CHANGED
@@ -15,36 +15,35 @@ class Redis
|
|
15
15
|
def initialize(options)
|
16
16
|
options = options.dup
|
17
17
|
node_addrs = options.delete(:cluster)
|
18
|
-
@
|
18
|
+
@node_opts = build_node_options(node_addrs)
|
19
19
|
@replica = options.delete(:replica) == true
|
20
|
+
add_common_node_option_if_needed(options, @node_opts, :scheme)
|
21
|
+
add_common_node_option_if_needed(options, @node_opts, :password)
|
20
22
|
@options = options
|
21
23
|
end
|
22
24
|
|
23
25
|
def per_node_key
|
24
|
-
@
|
26
|
+
@node_opts.map { |opt| [NodeKey.build_from_host_port(opt[:host], opt[:port]), @options.merge(opt)] }
|
25
27
|
.to_h
|
26
28
|
end
|
27
29
|
|
28
|
-
def secure?
|
29
|
-
@node_uris.any? { |uri| uri.scheme == SECURE_SCHEME } || @options[:ssl_params] || false
|
30
|
-
end
|
31
|
-
|
32
30
|
def use_replica?
|
33
31
|
@replica
|
34
32
|
end
|
35
33
|
|
36
34
|
def update_node(addrs)
|
37
|
-
@
|
35
|
+
@node_opts = build_node_options(addrs)
|
38
36
|
end
|
39
37
|
|
40
38
|
def add_node(host, port)
|
41
|
-
@
|
39
|
+
@node_opts << { host: host, port: port }
|
42
40
|
end
|
43
41
|
|
44
42
|
private
|
45
43
|
|
46
|
-
def
|
44
|
+
def build_node_options(addrs)
|
47
45
|
raise InvalidClientOptionError, 'Redis option of `cluster` must be an Array' unless addrs.is_a?(Array)
|
46
|
+
|
48
47
|
addrs.map { |addr| parse_node_addr(addr) }
|
49
48
|
end
|
50
49
|
|
@@ -53,7 +52,7 @@ class Redis
|
|
53
52
|
when String
|
54
53
|
parse_node_url(addr)
|
55
54
|
when Hash
|
56
|
-
|
55
|
+
parse_node_option(addr)
|
57
56
|
else
|
58
57
|
raise InvalidClientOptionError, 'Redis option of `cluster` must includes String or Hash'
|
59
58
|
end
|
@@ -62,15 +61,29 @@ class Redis
|
|
62
61
|
def parse_node_url(addr)
|
63
62
|
uri = URI(addr)
|
64
63
|
raise InvalidClientOptionError, "Invalid uri scheme #{addr}" unless VALID_SCHEMES.include?(uri.scheme)
|
65
|
-
|
64
|
+
|
65
|
+
db = uri.path.split('/')[1]&.to_i
|
66
|
+
{ scheme: uri.scheme, password: uri.password, host: uri.host, port: uri.port, db: db }.reject { |_, v| v.nil? }
|
66
67
|
rescue URI::InvalidURIError => err
|
67
68
|
raise InvalidClientOptionError, err.message
|
68
69
|
end
|
69
70
|
|
70
|
-
def
|
71
|
+
def parse_node_option(addr)
|
71
72
|
addr = addr.map { |k, v| [k.to_sym, v] }.to_h
|
72
|
-
|
73
|
-
|
73
|
+
if addr.values_at(:host, :port).any?(&:nil?)
|
74
|
+
raise InvalidClientOptionError, 'Redis option of `cluster` must includes `:host` and `:port` keys'
|
75
|
+
end
|
76
|
+
|
77
|
+
addr
|
78
|
+
end
|
79
|
+
|
80
|
+
# Redis cluster node returns only host and port information.
|
81
|
+
# So we should complement additional information such as:
|
82
|
+
# scheme, password and so on.
|
83
|
+
def add_common_node_option_if_needed(options, node_opts, key)
|
84
|
+
return options if options[key].nil? && node_opts.first[key].nil?
|
85
|
+
|
86
|
+
options[key] ||= node_opts.first[key]
|
74
87
|
end
|
75
88
|
end
|
76
89
|
end
|