redis 4.2.1 → 4.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -0
- data/README.md +17 -11
- data/lib/redis.rb +30 -17
- data/lib/redis/client.rb +16 -4
- data/lib/redis/cluster.rb +1 -1
- data/lib/redis/cluster/option.rb +5 -2
- data/lib/redis/connection/ruby.rb +53 -48
- data/lib/redis/distributed.rb +37 -8
- data/lib/redis/version.rb +1 -1
- metadata +8 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d173abb7a6c08e1feb87c9fd5ba33a2a0d907c6d045f6959d55f3234e56ceeb
|
4
|
+
data.tar.gz: 7463d58522a3db5262eeea4b95834d82ede95cf102d2375e051cf4ada1b235c3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0b34ab14e41e1bd63a99319f3ee5e1e7ea0c1e89581e525dfd82b77968a834525807b025f02d8ff5df4e7a994f19cc7ee4098103c70abd78ab6c22ec435a055c
|
7
|
+
data.tar.gz: 84da776467bebb7fd59333084787e484203e55b81be5031c78b44741f2b79342cc1f6f48c1f9663b15f360a3af590783ee2f403da07a3e04486e35ba70e49e01
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,29 @@
|
|
1
1
|
# Unreleased
|
2
2
|
|
3
|
+
# 4.3.0
|
4
|
+
|
5
|
+
* Add the TYPE argument to scan and scan_each. See #985.
|
6
|
+
* Support AUTH command for ACL. See #967.
|
7
|
+
|
8
|
+
# 4.2.5
|
9
|
+
|
10
|
+
* Optimize the ruby connector write buffering. See #964.
|
11
|
+
|
12
|
+
# 4.2.4
|
13
|
+
|
14
|
+
* Fix bytesize calculations in the ruby connector, and work on a copy of the buffer. Fix #961, #962.
|
15
|
+
|
16
|
+
# 4.2.3
|
17
|
+
|
18
|
+
* Use io/wait instead of IO.select in the ruby connector. See #960.
|
19
|
+
* Use exception free non blocking IOs in the ruby connector. See #926.
|
20
|
+
* Prevent corruption of the client when an interrupt happen during inside a pipeline block. See #945.
|
21
|
+
|
22
|
+
# 4.2.2
|
23
|
+
|
24
|
+
* Fix `WATCH` support for `Redis::Distributed`. See #941.
|
25
|
+
* Fix handling of empty stream responses. See #905, #929.
|
26
|
+
|
3
27
|
# 4.2.1
|
4
28
|
|
5
29
|
* Fix `exists?` returning an actual boolean when called with multiple keys. See #918.
|
@@ -17,6 +41,7 @@
|
|
17
41
|
* Optimized initialization of Redis::Cluster. See #912.
|
18
42
|
* Accept sentinel options even with string key. See #599.
|
19
43
|
* Verify TLS connections by default. See #900.
|
44
|
+
* Make `Redis#hset` variadic. It now returns an integer, not a boolean. See #910.
|
20
45
|
|
21
46
|
# 4.1.4
|
22
47
|
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# redis-rb [![Build Status][
|
1
|
+
# redis-rb [![Build Status][gh-actions-image]][gh-actions-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.
|
@@ -54,6 +54,12 @@ To connect to a password protected Redis instance, use:
|
|
54
54
|
redis = Redis.new(password: "mysecret")
|
55
55
|
```
|
56
56
|
|
57
|
+
To connect a Redis instance using [ACL](https://redis.io/topics/acl), use:
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
redis = Redis.new(username: 'myname', password: 'mysecret')
|
61
|
+
```
|
62
|
+
|
57
63
|
The Redis class exports methods that are named identical to the commands
|
58
64
|
they execute. The arguments these methods accept are often identical to
|
59
65
|
the arguments specified on the [Redis website][redis-commands]. For
|
@@ -265,6 +271,7 @@ All timeout values are specified in seconds.
|
|
265
271
|
When using pub/sub, you can subscribe to a channel using a timeout as well:
|
266
272
|
|
267
273
|
```ruby
|
274
|
+
redis = Redis.new(reconnect_attempts: 0)
|
268
275
|
redis.subscribe_with_timeout(5, "news") do |on|
|
269
276
|
on.message do |channel, message|
|
270
277
|
# ...
|
@@ -439,7 +446,7 @@ redis = Redis.new(:driver => :synchrony)
|
|
439
446
|
## Testing
|
440
447
|
|
441
448
|
This library is tested against recent Ruby and Redis versions.
|
442
|
-
Check [
|
449
|
+
Check [Github Actions][gh-actions-link] for the exact versions supported.
|
443
450
|
|
444
451
|
## See Also
|
445
452
|
|
@@ -458,12 +465,11 @@ client and evangelized Redis in Rubyland. Thank you, Ezra.
|
|
458
465
|
requests.
|
459
466
|
|
460
467
|
|
461
|
-
[inchpages-image]:
|
462
|
-
[inchpages-link]:
|
463
|
-
[redis-commands]:
|
464
|
-
[redis-home]:
|
465
|
-
[redis-url]:
|
466
|
-
[
|
467
|
-
[
|
468
|
-
[
|
469
|
-
[rubydoc]: http://www.rubydoc.info/gems/redis
|
468
|
+
[inchpages-image]: https://inch-ci.org/github/redis/redis-rb.svg
|
469
|
+
[inchpages-link]: https://inch-ci.org/github/redis/redis-rb
|
470
|
+
[redis-commands]: https://redis.io/commands
|
471
|
+
[redis-home]: https://redis.io
|
472
|
+
[redis-url]: http://www.iana.org/assignments/uri-schemes/prov/redis
|
473
|
+
[gh-actions-image]: https://github.com/redis/redis-rb/workflows/Test/badge.svg
|
474
|
+
[gh-actions-link]: https://github.com/redis/redis-rb/actions
|
475
|
+
[rubydoc]: http://www.rubydoc.info/gems/redis
|
data/lib/redis.rb
CHANGED
@@ -39,6 +39,7 @@ class Redis
|
|
39
39
|
# @option options [String] :path path to server socket (overrides host and port)
|
40
40
|
# @option options [Float] :timeout (5.0) timeout in seconds
|
41
41
|
# @option options [Float] :connect_timeout (same as timeout) timeout for initial connect in seconds
|
42
|
+
# @option options [String] :username Username to authenticate against server
|
42
43
|
# @option options [String] :password Password to authenticate against server
|
43
44
|
# @option options [Integer] :db (0) Database to select after initial connect
|
44
45
|
# @option options [Symbol] :driver Driver to use, currently supported: `:ruby`, `:hiredis`, `:synchrony`
|
@@ -143,12 +144,13 @@ class Redis
|
|
143
144
|
|
144
145
|
# Authenticate to the server.
|
145
146
|
#
|
146
|
-
# @param [String]
|
147
|
-
#
|
147
|
+
# @param [Array<String>] args includes both username and password
|
148
|
+
# or only password
|
148
149
|
# @return [String] `OK`
|
149
|
-
|
150
|
+
# @see https://redis.io/commands/auth AUTH command
|
151
|
+
def auth(*args)
|
150
152
|
synchronize do |client|
|
151
|
-
client.call([:auth,
|
153
|
+
client.call([:auth, *args])
|
152
154
|
end
|
153
155
|
end
|
154
156
|
|
@@ -2438,14 +2440,13 @@ class Redis
|
|
2438
2440
|
end
|
2439
2441
|
|
2440
2442
|
def pipelined
|
2441
|
-
synchronize do |
|
2443
|
+
synchronize do |prior_client|
|
2442
2444
|
begin
|
2443
|
-
|
2444
|
-
original, @client = @client, pipeline
|
2445
|
+
@client = Pipeline.new(prior_client)
|
2445
2446
|
yield(self)
|
2446
|
-
|
2447
|
+
prior_client.call_pipeline(@client)
|
2447
2448
|
ensure
|
2448
|
-
@client =
|
2449
|
+
@client = prior_client
|
2449
2450
|
end
|
2450
2451
|
end
|
2451
2452
|
end
|
@@ -2481,17 +2482,16 @@ class Redis
|
|
2481
2482
|
# @see #watch
|
2482
2483
|
# @see #unwatch
|
2483
2484
|
def multi
|
2484
|
-
synchronize do |
|
2485
|
+
synchronize do |prior_client|
|
2485
2486
|
if !block_given?
|
2486
|
-
|
2487
|
+
prior_client.call([:multi])
|
2487
2488
|
else
|
2488
2489
|
begin
|
2489
|
-
|
2490
|
-
original, @client = @client, pipeline
|
2490
|
+
@client = Pipeline::Multi.new(prior_client)
|
2491
2491
|
yield(self)
|
2492
|
-
|
2492
|
+
prior_client.call_pipeline(@client)
|
2493
2493
|
ensure
|
2494
|
-
@client =
|
2494
|
+
@client = prior_client
|
2495
2495
|
end
|
2496
2496
|
end
|
2497
2497
|
end
|
@@ -2638,12 +2638,13 @@ class Redis
|
|
2638
2638
|
_eval(:evalsha, args)
|
2639
2639
|
end
|
2640
2640
|
|
2641
|
-
def _scan(command, cursor, args, match: nil, count: nil, &block)
|
2641
|
+
def _scan(command, cursor, args, match: nil, count: nil, type: nil, &block)
|
2642
2642
|
# SSCAN/ZSCAN/HSCAN already prepend the key to +args+.
|
2643
2643
|
|
2644
2644
|
args << cursor
|
2645
2645
|
args << "MATCH" << match if match
|
2646
2646
|
args << "COUNT" << count if count
|
2647
|
+
args << "TYPE" << type if type
|
2647
2648
|
|
2648
2649
|
synchronize do |client|
|
2649
2650
|
client.call([command] + args, &block)
|
@@ -2658,11 +2659,15 @@ class Redis
|
|
2658
2659
|
# @example Retrieve a batch of keys matching a pattern
|
2659
2660
|
# redis.scan(4, :match => "key:1?")
|
2660
2661
|
# # => ["92", ["key:13", "key:18"]]
|
2662
|
+
# @example Retrieve a batch of keys of a certain type
|
2663
|
+
# redis.scan(92, :type => "zset")
|
2664
|
+
# # => ["173", ["sortedset:14", "sortedset:78"]]
|
2661
2665
|
#
|
2662
2666
|
# @param [String, Integer] cursor the cursor of the iteration
|
2663
2667
|
# @param [Hash] options
|
2664
2668
|
# - `:match => String`: only return keys matching the pattern
|
2665
2669
|
# - `:count => Integer`: return count keys at most per iteration
|
2670
|
+
# - `:type => String`: return keys only of the given type
|
2666
2671
|
#
|
2667
2672
|
# @return [String, Array<String>] the next cursor and all found keys
|
2668
2673
|
def scan(cursor, **options)
|
@@ -2678,10 +2683,15 @@ class Redis
|
|
2678
2683
|
# redis.scan_each(:match => "key:1?") {|key| puts key}
|
2679
2684
|
# # => key:13
|
2680
2685
|
# # => key:18
|
2686
|
+
# @example Execute block for each key of a type
|
2687
|
+
# redis.scan_each(:type => "hash") {|key| puts redis.type(key)}
|
2688
|
+
# # => "hash"
|
2689
|
+
# # => "hash"
|
2681
2690
|
#
|
2682
2691
|
# @param [Hash] options
|
2683
2692
|
# - `:match => String`: only return keys matching the pattern
|
2684
2693
|
# - `:count => Integer`: return count keys at most per iteration
|
2694
|
+
# - `:type => String`: return keys only of the given type
|
2685
2695
|
#
|
2686
2696
|
# @return [Enumerator] an enumerator for all found keys
|
2687
2697
|
def scan_each(**options, &block)
|
@@ -3423,8 +3433,11 @@ class Redis
|
|
3423
3433
|
end
|
3424
3434
|
}
|
3425
3435
|
|
3436
|
+
EMPTY_STREAM_RESPONSE = [nil].freeze
|
3437
|
+
private_constant :EMPTY_STREAM_RESPONSE
|
3438
|
+
|
3426
3439
|
HashifyStreamEntries = lambda { |reply|
|
3427
|
-
reply.map do |entry_id, values|
|
3440
|
+
reply.compact.map do |entry_id, values|
|
3428
3441
|
[entry_id, values.each_slice(2).to_h]
|
3429
3442
|
end
|
3430
3443
|
}
|
data/lib/redis/client.rb
CHANGED
@@ -6,13 +6,18 @@ require "cgi"
|
|
6
6
|
|
7
7
|
class Redis
|
8
8
|
class Client
|
9
|
+
# Defaults are also used for converting string keys to symbols.
|
9
10
|
DEFAULTS = {
|
10
11
|
url: -> { ENV["REDIS_URL"] },
|
11
12
|
scheme: "redis",
|
12
13
|
host: "127.0.0.1",
|
13
14
|
port: 6379,
|
14
15
|
path: nil,
|
16
|
+
read_timeout: nil,
|
17
|
+
write_timeout: nil,
|
18
|
+
connect_timeout: nil,
|
15
19
|
timeout: 5.0,
|
20
|
+
username: nil,
|
16
21
|
password: nil,
|
17
22
|
db: 0,
|
18
23
|
driver: nil,
|
@@ -22,6 +27,7 @@ class Redis
|
|
22
27
|
reconnect_delay: 0,
|
23
28
|
reconnect_delay_max: 0.5,
|
24
29
|
inherit_socket: false,
|
30
|
+
logger: nil,
|
25
31
|
sentinels: nil,
|
26
32
|
role: nil
|
27
33
|
}.freeze
|
@@ -56,6 +62,10 @@ class Redis
|
|
56
62
|
@options[:read_timeout]
|
57
63
|
end
|
58
64
|
|
65
|
+
def username
|
66
|
+
@options[:username]
|
67
|
+
end
|
68
|
+
|
59
69
|
def password
|
60
70
|
@options[:password]
|
61
71
|
end
|
@@ -105,7 +115,7 @@ class Redis
|
|
105
115
|
# Don't try to reconnect when the connection is fresh
|
106
116
|
with_reconnect(false) do
|
107
117
|
establish_connection
|
108
|
-
call [:auth, password] if password
|
118
|
+
call [:auth, username, password].compact if username || password
|
109
119
|
call [:select, db] if db != 0
|
110
120
|
call [:client, :setname, @options[:id]] if @options[:id]
|
111
121
|
@connector.check(self)
|
@@ -126,7 +136,7 @@ class Redis
|
|
126
136
|
reply = process([command]) { read }
|
127
137
|
raise reply if reply.is_a?(CommandError)
|
128
138
|
|
129
|
-
if block_given?
|
139
|
+
if block_given? && reply != 'QUEUED'
|
130
140
|
yield reply
|
131
141
|
else
|
132
142
|
reply
|
@@ -429,7 +439,8 @@ class Redis
|
|
429
439
|
defaults[:scheme] = uri.scheme
|
430
440
|
defaults[:host] = uri.host if uri.host
|
431
441
|
defaults[:port] = uri.port if uri.port
|
432
|
-
defaults[:
|
442
|
+
defaults[:username] = CGI.unescape(uri.user) if uri.user && !uri.user.empty?
|
443
|
+
defaults[:password] = CGI.unescape(uri.password) if uri.password && !uri.password.empty?
|
433
444
|
defaults[:db] = uri.path[1..-1].to_i if uri.path
|
434
445
|
defaults[:role] = :master
|
435
446
|
else
|
@@ -505,7 +516,7 @@ class Redis
|
|
505
516
|
require_relative "connection/#{driver}"
|
506
517
|
rescue LoadError, NameError
|
507
518
|
begin
|
508
|
-
require "connection/#{driver}"
|
519
|
+
require "redis/connection/#{driver}"
|
509
520
|
rescue LoadError, NameError => error
|
510
521
|
raise "Cannot load driver #{driver.inspect}: #{error.message}"
|
511
522
|
end
|
@@ -574,6 +585,7 @@ class Redis
|
|
574
585
|
client = Client.new(@options.merge({
|
575
586
|
host: sentinel[:host] || sentinel["host"],
|
576
587
|
port: sentinel[:port] || sentinel["port"],
|
588
|
+
username: sentinel[:username] || sentinel["username"],
|
577
589
|
password: sentinel[:password] || sentinel["password"],
|
578
590
|
reconnect_attempts: 0
|
579
591
|
}))
|
data/lib/redis/cluster.rb
CHANGED
@@ -128,7 +128,7 @@ class Redis
|
|
128
128
|
def send_command(command, &block)
|
129
129
|
cmd = command.first.to_s.downcase
|
130
130
|
case cmd
|
131
|
-
when 'auth', 'bgrewriteaof', 'bgsave', 'quit', 'save'
|
131
|
+
when 'acl', 'auth', 'bgrewriteaof', 'bgsave', 'quit', 'save'
|
132
132
|
@node.call_all(command, &block).first
|
133
133
|
when 'flushall', 'flushdb'
|
134
134
|
@node.call_master(command, &block).first
|
data/lib/redis/cluster/option.rb
CHANGED
@@ -18,6 +18,7 @@ class Redis
|
|
18
18
|
@node_opts = build_node_options(node_addrs)
|
19
19
|
@replica = options.delete(:replica) == true
|
20
20
|
add_common_node_option_if_needed(options, @node_opts, :scheme)
|
21
|
+
add_common_node_option_if_needed(options, @node_opts, :username)
|
21
22
|
add_common_node_option_if_needed(options, @node_opts, :password)
|
22
23
|
@options = options
|
23
24
|
end
|
@@ -63,7 +64,9 @@ class Redis
|
|
63
64
|
raise InvalidClientOptionError, "Invalid uri scheme #{addr}" unless VALID_SCHEMES.include?(uri.scheme)
|
64
65
|
|
65
66
|
db = uri.path.split('/')[1]&.to_i
|
66
|
-
|
67
|
+
|
68
|
+
{ scheme: uri.scheme, username: uri.user, password: uri.password, host: uri.host, port: uri.port, db: db }
|
69
|
+
.reject { |_, v| v.nil? || v == '' }
|
67
70
|
rescue URI::InvalidURIError => err
|
68
71
|
raise InvalidClientOptionError, err.message
|
69
72
|
end
|
@@ -79,7 +82,7 @@ class Redis
|
|
79
82
|
|
80
83
|
# Redis cluster node returns only host and port information.
|
81
84
|
# So we should complement additional information such as:
|
82
|
-
# scheme, password and so on.
|
85
|
+
# scheme, username, password and so on.
|
83
86
|
def add_common_node_option_if_needed(options, node_opts, key)
|
84
87
|
return options if options[key].nil? && node_opts.first[key].nil?
|
85
88
|
|
@@ -49,57 +49,50 @@ class Redis
|
|
49
49
|
end
|
50
50
|
|
51
51
|
def _read_from_socket(nbytes)
|
52
|
-
|
53
|
-
read_nonblock(nbytes)
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
end
|
67
|
-
rescue EOFError
|
68
|
-
raise Errno::ECONNRESET
|
69
|
-
end
|
70
|
-
|
71
|
-
def _write_to_socket(data)
|
72
|
-
begin
|
73
|
-
write_nonblock(data)
|
74
|
-
rescue IO::WaitWritable
|
75
|
-
if IO.select(nil, [self], nil, @write_timeout)
|
76
|
-
retry
|
77
|
-
else
|
78
|
-
raise Redis::TimeoutError
|
79
|
-
end
|
80
|
-
rescue IO::WaitReadable
|
81
|
-
if IO.select([self], nil, nil, @write_timeout)
|
82
|
-
retry
|
83
|
-
else
|
84
|
-
raise Redis::TimeoutError
|
52
|
+
loop do
|
53
|
+
case chunk = read_nonblock(nbytes, exception: false)
|
54
|
+
when :wait_readable
|
55
|
+
unless wait_readable(@timeout)
|
56
|
+
raise Redis::TimeoutError
|
57
|
+
end
|
58
|
+
when :wait_writable
|
59
|
+
unless wait_writable(@timeout)
|
60
|
+
raise Redis::TimeoutError
|
61
|
+
end
|
62
|
+
when nil
|
63
|
+
raise Errno::ECONNRESET
|
64
|
+
when String
|
65
|
+
return chunk
|
85
66
|
end
|
86
67
|
end
|
87
|
-
rescue EOFError
|
88
|
-
raise Errno::ECONNRESET
|
89
68
|
end
|
90
69
|
|
91
|
-
def write(
|
92
|
-
return super(
|
70
|
+
def write(buffer)
|
71
|
+
return super(buffer) unless @write_timeout
|
93
72
|
|
94
|
-
|
95
|
-
|
73
|
+
bytes_to_write = buffer.bytesize
|
74
|
+
total_bytes_written = 0
|
96
75
|
loop do
|
97
|
-
|
76
|
+
case bytes_written = write_nonblock(buffer, exception: false)
|
77
|
+
when :wait_readable
|
78
|
+
unless wait_readable(@write_timeout)
|
79
|
+
raise Redis::TimeoutError
|
80
|
+
end
|
81
|
+
when :wait_writable
|
82
|
+
unless wait_writable(@write_timeout)
|
83
|
+
raise Redis::TimeoutError
|
84
|
+
end
|
85
|
+
when nil
|
86
|
+
raise Errno::ECONNRESET
|
87
|
+
when Integer
|
88
|
+
total_bytes_written += bytes_written
|
98
89
|
|
99
|
-
|
100
|
-
|
90
|
+
if total_bytes_written >= bytes_to_write
|
91
|
+
return total_bytes_written
|
92
|
+
end
|
101
93
|
|
102
|
-
|
94
|
+
buffer = buffer.byteslice(bytes_written..-1)
|
95
|
+
end
|
103
96
|
end
|
104
97
|
end
|
105
98
|
end
|
@@ -135,7 +128,7 @@ class Redis
|
|
135
128
|
raise TimeoutError
|
136
129
|
end
|
137
130
|
|
138
|
-
# JRuby raises Errno::EAGAIN on #read_nonblock even when
|
131
|
+
# JRuby raises Errno::EAGAIN on #read_nonblock even when it
|
139
132
|
# says it is readable (1.6.6, in both 1.8 and 1.9 mode).
|
140
133
|
# Use the blocking #readpartial method instead.
|
141
134
|
|
@@ -160,7 +153,7 @@ class Redis
|
|
160
153
|
begin
|
161
154
|
sock.connect_nonblock(sockaddr)
|
162
155
|
rescue Errno::EINPROGRESS
|
163
|
-
raise TimeoutError
|
156
|
+
raise TimeoutError unless sock.wait_writable(timeout)
|
164
157
|
|
165
158
|
begin
|
166
159
|
sock.connect_nonblock(sockaddr)
|
@@ -215,7 +208,7 @@ class Redis
|
|
215
208
|
begin
|
216
209
|
sock.connect_nonblock(sockaddr)
|
217
210
|
rescue Errno::EINPROGRESS
|
218
|
-
raise TimeoutError
|
211
|
+
raise TimeoutError unless sock.wait_writable(timeout)
|
219
212
|
|
220
213
|
begin
|
221
214
|
sock.connect_nonblock(sockaddr)
|
@@ -233,6 +226,18 @@ class Redis
|
|
233
226
|
class SSLSocket < ::OpenSSL::SSL::SSLSocket
|
234
227
|
include SocketMixin
|
235
228
|
|
229
|
+
unless method_defined?(:wait_readable)
|
230
|
+
def wait_readable(timeout = nil)
|
231
|
+
to_io.wait_readable(timeout)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
unless method_defined?(:wait_writable)
|
236
|
+
def wait_writable(timeout = nil)
|
237
|
+
to_io.wait_writable(timeout)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
236
241
|
def self.connect(host, port, timeout, ssl_params)
|
237
242
|
# Note: this is using Redis::Connection::TCPSocket
|
238
243
|
tcp_sock = TCPSocket.connect(host, port, timeout)
|
@@ -254,13 +259,13 @@ class Redis
|
|
254
259
|
# Instead, you have to retry.
|
255
260
|
ssl_sock.connect_nonblock
|
256
261
|
rescue Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable
|
257
|
-
if
|
262
|
+
if ssl_sock.wait_readable(timeout)
|
258
263
|
retry
|
259
264
|
else
|
260
265
|
raise TimeoutError
|
261
266
|
end
|
262
267
|
rescue IO::WaitWritable
|
263
|
-
if
|
268
|
+
if ssl_sock.wait_writable(timeout)
|
264
269
|
retry
|
265
270
|
else
|
266
271
|
raise TimeoutError
|
data/lib/redis/distributed.rb
CHANGED
@@ -24,10 +24,14 @@ class Redis
|
|
24
24
|
@default_options = options.dup
|
25
25
|
node_configs.each { |node_config| add_node(node_config) }
|
26
26
|
@subscribed_node = nil
|
27
|
+
@watch_key = nil
|
27
28
|
end
|
28
29
|
|
29
30
|
def node_for(key)
|
30
|
-
|
31
|
+
key = key_tag(key.to_s) || key.to_s
|
32
|
+
raise CannotDistribute, :watch if @watch_key && @watch_key != key
|
33
|
+
|
34
|
+
@ring.get_node(key)
|
31
35
|
end
|
32
36
|
|
33
37
|
def nodes
|
@@ -799,13 +803,26 @@ class Redis
|
|
799
803
|
end
|
800
804
|
|
801
805
|
# Watch the given keys to determine execution of the MULTI/EXEC block.
|
802
|
-
def watch(*
|
803
|
-
|
806
|
+
def watch(*keys, &block)
|
807
|
+
ensure_same_node(:watch, keys) do |node|
|
808
|
+
@watch_key = key_tag(keys.first) || keys.first.to_s
|
809
|
+
|
810
|
+
begin
|
811
|
+
node.watch(*keys, &block)
|
812
|
+
rescue StandardError
|
813
|
+
@watch_key = nil
|
814
|
+
raise
|
815
|
+
end
|
816
|
+
end
|
804
817
|
end
|
805
818
|
|
806
819
|
# Forget about all watched keys.
|
807
820
|
def unwatch
|
808
|
-
raise CannotDistribute, :unwatch
|
821
|
+
raise CannotDistribute, :unwatch unless @watch_key
|
822
|
+
|
823
|
+
result = node_for(@watch_key).unwatch
|
824
|
+
@watch_key = nil
|
825
|
+
result
|
809
826
|
end
|
810
827
|
|
811
828
|
def pipelined
|
@@ -813,18 +830,30 @@ class Redis
|
|
813
830
|
end
|
814
831
|
|
815
832
|
# Mark the start of a transaction block.
|
816
|
-
def multi
|
817
|
-
raise CannotDistribute, :multi
|
833
|
+
def multi(&block)
|
834
|
+
raise CannotDistribute, :multi unless @watch_key
|
835
|
+
|
836
|
+
result = node_for(@watch_key).multi(&block)
|
837
|
+
@watch_key = nil if block_given?
|
838
|
+
result
|
818
839
|
end
|
819
840
|
|
820
841
|
# Execute all commands issued after MULTI.
|
821
842
|
def exec
|
822
|
-
raise CannotDistribute, :exec
|
843
|
+
raise CannotDistribute, :exec unless @watch_key
|
844
|
+
|
845
|
+
result = node_for(@watch_key).exec
|
846
|
+
@watch_key = nil
|
847
|
+
result
|
823
848
|
end
|
824
849
|
|
825
850
|
# Discard all commands issued after MULTI.
|
826
851
|
def discard
|
827
|
-
raise CannotDistribute, :discard
|
852
|
+
raise CannotDistribute, :discard unless @watch_key
|
853
|
+
|
854
|
+
result = node_for(@watch_key).discard
|
855
|
+
@watch_key = nil
|
856
|
+
result
|
828
857
|
end
|
829
858
|
|
830
859
|
# Control remote script registry.
|
data/lib/redis/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ezra Zygmuntowicz
|
@@ -13,10 +13,10 @@ authors:
|
|
13
13
|
- Michel Martens
|
14
14
|
- Damian Janowski
|
15
15
|
- Pieter Noordhuis
|
16
|
-
autorequire:
|
16
|
+
autorequire:
|
17
17
|
bindir: bin
|
18
18
|
cert_chain: []
|
19
|
-
date:
|
19
|
+
date: 2021-06-11 00:00:00.000000000 Z
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
22
22
|
name: em-synchrony
|
@@ -102,10 +102,10 @@ licenses:
|
|
102
102
|
metadata:
|
103
103
|
bug_tracker_uri: https://github.com/redis/redis-rb/issues
|
104
104
|
changelog_uri: https://github.com/redis/redis-rb/blob/master/CHANGELOG.md
|
105
|
-
documentation_uri: https://www.rubydoc.info/gems/redis/4.
|
105
|
+
documentation_uri: https://www.rubydoc.info/gems/redis/4.3.0
|
106
106
|
homepage_uri: https://github.com/redis/redis-rb
|
107
|
-
source_code_uri: https://github.com/redis/redis-rb/tree/v4.
|
108
|
-
post_install_message:
|
107
|
+
source_code_uri: https://github.com/redis/redis-rb/tree/v4.3.0
|
108
|
+
post_install_message:
|
109
109
|
rdoc_options: []
|
110
110
|
require_paths:
|
111
111
|
- lib
|
@@ -120,8 +120,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
120
120
|
- !ruby/object:Gem::Version
|
121
121
|
version: '0'
|
122
122
|
requirements: []
|
123
|
-
rubygems_version: 3.
|
124
|
-
signing_key:
|
123
|
+
rubygems_version: 3.1.2
|
124
|
+
signing_key:
|
125
125
|
specification_version: 4
|
126
126
|
summary: A Ruby client library for Redis
|
127
127
|
test_files: []
|