redis 4.2.4 → 4.4.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 +21 -0
- data/README.md +16 -11
- data/lib/redis.rb +119 -13
- data/lib/redis/client.rb +23 -4
- data/lib/redis/cluster.rb +9 -13
- data/lib/redis/cluster/node.rb +2 -1
- data/lib/redis/cluster/option.rb +5 -2
- data/lib/redis/connection/ruby.rb +9 -21
- data/lib/redis/distributed.rb +13 -6
- data/lib/redis/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d5ff2ee4b6a6f2b087ac26bf96a3c1769cf42f70ea90008361157f7ef04cdb14
|
4
|
+
data.tar.gz: f2c24654294c4fa81a5cff4ddb545bc59e6f85b64ee61273278cb1e286a4edb8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0d88c6621659a178dca04d3ca62f25520f5f1a846b6300f93fb008966551fb4795a3e6c37810f99ea18f77a6292b955fa180ae3e06f8c90df7e6a3ca27087ae1
|
7
|
+
data.tar.gz: 1427cc268e867872388f214184d23a5c6062104fcb9d1e50d6026dd9daf6e51b9833866fee9fcf80c7a64e4de705295de8399d8e8ac22fa890bf358bd63ff707
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,26 @@
|
|
1
1
|
# Unreleased
|
2
2
|
|
3
|
+
# 4.4.0
|
4
|
+
|
5
|
+
* Redis cluster: fix cross-slot validation in pipelines. Fix ##1019.
|
6
|
+
* Add support for `XAUTOCLAIM`. See #1018.
|
7
|
+
* Properly issue `READONLY` when reconnecting to replicas. Fix #1017.
|
8
|
+
* Make `del` a noop if passed an empty list of keys. See #998.
|
9
|
+
* Add support for `ZINTER`. See #995.
|
10
|
+
|
11
|
+
# 4.3.1
|
12
|
+
|
13
|
+
* Fix password authentication against redis server 5 and older.
|
14
|
+
|
15
|
+
# 4.3.0
|
16
|
+
|
17
|
+
* Add the TYPE argument to scan and scan_each. See #985.
|
18
|
+
* Support AUTH command for ACL. See #967.
|
19
|
+
|
20
|
+
# 4.2.5
|
21
|
+
|
22
|
+
* Optimize the ruby connector write buffering. See #964.
|
23
|
+
|
3
24
|
# 4.2.4
|
4
25
|
|
5
26
|
* Fix bytesize calculations in the ruby connector, and work on a copy of the buffer. Fix #961, #962.
|
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
|
@@ -440,7 +446,7 @@ redis = Redis.new(:driver => :synchrony)
|
|
440
446
|
## Testing
|
441
447
|
|
442
448
|
This library is tested against recent Ruby and Redis versions.
|
443
|
-
Check [
|
449
|
+
Check [Github Actions][gh-actions-link] for the exact versions supported.
|
444
450
|
|
445
451
|
## See Also
|
446
452
|
|
@@ -459,12 +465,11 @@ client and evangelized Redis in Rubyland. Thank you, Ezra.
|
|
459
465
|
requests.
|
460
466
|
|
461
467
|
|
462
|
-
[inchpages-image]:
|
463
|
-
[inchpages-link]:
|
464
|
-
[redis-commands]:
|
465
|
-
[redis-home]:
|
466
|
-
[redis-url]:
|
467
|
-
[
|
468
|
-
[
|
469
|
-
[
|
470
|
-
[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
|
|
@@ -553,6 +555,9 @@ class Redis
|
|
553
555
|
# @param [String, Array<String>] keys
|
554
556
|
# @return [Integer] number of keys that were deleted
|
555
557
|
def del(*keys)
|
558
|
+
keys.flatten!(1)
|
559
|
+
return 0 if keys.empty?
|
560
|
+
|
556
561
|
synchronize do |client|
|
557
562
|
client.call([:del] + keys)
|
558
563
|
end
|
@@ -1170,23 +1175,29 @@ class Redis
|
|
1170
1175
|
end
|
1171
1176
|
end
|
1172
1177
|
|
1173
|
-
# Remove and get the first
|
1178
|
+
# Remove and get the first elements in a list.
|
1174
1179
|
#
|
1175
1180
|
# @param [String] key
|
1176
|
-
# @
|
1177
|
-
|
1181
|
+
# @param [Integer] count number of elements to remove
|
1182
|
+
# @return [String, Array<String>] the values of the first elements
|
1183
|
+
def lpop(key, count = nil)
|
1178
1184
|
synchronize do |client|
|
1179
|
-
|
1185
|
+
command = [:lpop, key]
|
1186
|
+
command << count if count
|
1187
|
+
client.call(command)
|
1180
1188
|
end
|
1181
1189
|
end
|
1182
1190
|
|
1183
|
-
# Remove and get the last
|
1191
|
+
# Remove and get the last elements in a list.
|
1184
1192
|
#
|
1185
1193
|
# @param [String] key
|
1186
|
-
# @
|
1187
|
-
|
1194
|
+
# @param [Integer] count number of elements to remove
|
1195
|
+
# @return [String, Array<String>] the values of the last elements
|
1196
|
+
def rpop(key, count = nil)
|
1188
1197
|
synchronize do |client|
|
1189
|
-
|
1198
|
+
command = [:rpop, key]
|
1199
|
+
command << count if count
|
1200
|
+
client.call(command)
|
1190
1201
|
end
|
1191
1202
|
end
|
1192
1203
|
|
@@ -2054,6 +2065,45 @@ class Redis
|
|
2054
2065
|
end
|
2055
2066
|
end
|
2056
2067
|
|
2068
|
+
# Return the intersection of multiple sorted sets
|
2069
|
+
#
|
2070
|
+
# @example Retrieve the intersection of `2*zsetA` and `1*zsetB`
|
2071
|
+
# redis.zinter("zsetA", "zsetB", :weights => [2.0, 1.0])
|
2072
|
+
# # => ["v1", "v2"]
|
2073
|
+
# @example Retrieve the intersection of `2*zsetA` and `1*zsetB`, and their scores
|
2074
|
+
# redis.zinter("zsetA", "zsetB", :weights => [2.0, 1.0], :with_scores => true)
|
2075
|
+
# # => [["v1", 3.0], ["v2", 6.0]]
|
2076
|
+
#
|
2077
|
+
# @param [String, Array<String>] keys one or more keys to intersect
|
2078
|
+
# @param [Hash] options
|
2079
|
+
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
2080
|
+
# sorted sets
|
2081
|
+
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
2082
|
+
# - `:with_scores => true`: include scores in output
|
2083
|
+
#
|
2084
|
+
# @return [Array<String>, Array<[String, Float]>]
|
2085
|
+
# - when `:with_scores` is not specified, an array of members
|
2086
|
+
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
2087
|
+
def zinter(*keys, weights: nil, aggregate: nil, with_scores: false)
|
2088
|
+
args = [:zinter, keys.size, *keys]
|
2089
|
+
|
2090
|
+
if weights
|
2091
|
+
args << "WEIGHTS"
|
2092
|
+
args.concat(weights)
|
2093
|
+
end
|
2094
|
+
|
2095
|
+
args << "AGGREGATE" << aggregate if aggregate
|
2096
|
+
|
2097
|
+
if with_scores
|
2098
|
+
args << "WITHSCORES"
|
2099
|
+
block = FloatifyPairs
|
2100
|
+
end
|
2101
|
+
|
2102
|
+
synchronize do |client|
|
2103
|
+
client.call(args, &block)
|
2104
|
+
end
|
2105
|
+
end
|
2106
|
+
|
2057
2107
|
# Intersect multiple sorted sets and store the resulting sorted set in a new
|
2058
2108
|
# key.
|
2059
2109
|
#
|
@@ -2636,12 +2686,13 @@ class Redis
|
|
2636
2686
|
_eval(:evalsha, args)
|
2637
2687
|
end
|
2638
2688
|
|
2639
|
-
def _scan(command, cursor, args, match: nil, count: nil, &block)
|
2689
|
+
def _scan(command, cursor, args, match: nil, count: nil, type: nil, &block)
|
2640
2690
|
# SSCAN/ZSCAN/HSCAN already prepend the key to +args+.
|
2641
2691
|
|
2642
2692
|
args << cursor
|
2643
2693
|
args << "MATCH" << match if match
|
2644
2694
|
args << "COUNT" << count if count
|
2695
|
+
args << "TYPE" << type if type
|
2645
2696
|
|
2646
2697
|
synchronize do |client|
|
2647
2698
|
client.call([command] + args, &block)
|
@@ -2656,11 +2707,15 @@ class Redis
|
|
2656
2707
|
# @example Retrieve a batch of keys matching a pattern
|
2657
2708
|
# redis.scan(4, :match => "key:1?")
|
2658
2709
|
# # => ["92", ["key:13", "key:18"]]
|
2710
|
+
# @example Retrieve a batch of keys of a certain type
|
2711
|
+
# redis.scan(92, :type => "zset")
|
2712
|
+
# # => ["173", ["sortedset:14", "sortedset:78"]]
|
2659
2713
|
#
|
2660
2714
|
# @param [String, Integer] cursor the cursor of the iteration
|
2661
2715
|
# @param [Hash] options
|
2662
2716
|
# - `:match => String`: only return keys matching the pattern
|
2663
2717
|
# - `:count => Integer`: return count keys at most per iteration
|
2718
|
+
# - `:type => String`: return keys only of the given type
|
2664
2719
|
#
|
2665
2720
|
# @return [String, Array<String>] the next cursor and all found keys
|
2666
2721
|
def scan(cursor, **options)
|
@@ -2676,10 +2731,15 @@ class Redis
|
|
2676
2731
|
# redis.scan_each(:match => "key:1?") {|key| puts key}
|
2677
2732
|
# # => key:13
|
2678
2733
|
# # => key:18
|
2734
|
+
# @example Execute block for each key of a type
|
2735
|
+
# redis.scan_each(:type => "hash") {|key| puts redis.type(key)}
|
2736
|
+
# # => "hash"
|
2737
|
+
# # => "hash"
|
2679
2738
|
#
|
2680
2739
|
# @param [Hash] options
|
2681
2740
|
# - `:match => String`: only return keys matching the pattern
|
2682
2741
|
# - `:count => Integer`: return count keys at most per iteration
|
2742
|
+
# - `:type => String`: return keys only of the given type
|
2683
2743
|
#
|
2684
2744
|
# @return [Enumerator] an enumerator for all found keys
|
2685
2745
|
def scan_each(**options, &block)
|
@@ -3220,6 +3280,38 @@ class Redis
|
|
3220
3280
|
synchronize { |client| client.call(args, &blk) }
|
3221
3281
|
end
|
3222
3282
|
|
3283
|
+
# Transfers ownership of pending stream entries that match the specified criteria.
|
3284
|
+
#
|
3285
|
+
# @example Claim next pending message stuck > 5 minutes and mark as retry
|
3286
|
+
# redis.xautoclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0')
|
3287
|
+
# @example Claim 50 next pending messages stuck > 5 minutes and mark as retry
|
3288
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0', count: 50)
|
3289
|
+
# @example Claim next pending message stuck > 5 minutes and don't mark as retry
|
3290
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0', justid: true)
|
3291
|
+
# @example Claim next pending message after this id stuck > 5 minutes and mark as retry
|
3292
|
+
# redis.xautoclaim('mystream', 'mygroup', 'consumer1', 3600000, '1641321233-0')
|
3293
|
+
#
|
3294
|
+
# @param key [String] the stream key
|
3295
|
+
# @param group [String] the consumer group name
|
3296
|
+
# @param consumer [String] the consumer name
|
3297
|
+
# @param min_idle_time [Integer] the number of milliseconds
|
3298
|
+
# @param start [String] entry id to start scanning from or 0-0 for everything
|
3299
|
+
# @param count [Integer] number of messages to claim (default 1)
|
3300
|
+
# @param justid [Boolean] whether to fetch just an array of entry ids or not.
|
3301
|
+
# Does not increment retry count when true
|
3302
|
+
#
|
3303
|
+
# @return [Hash{String => Hash}] the entries successfully claimed
|
3304
|
+
# @return [Array<String>] the entry ids successfully claimed if justid option is `true`
|
3305
|
+
def xautoclaim(key, group, consumer, min_idle_time, start, count: nil, justid: false)
|
3306
|
+
args = [:xautoclaim, key, group, consumer, min_idle_time, start]
|
3307
|
+
if count
|
3308
|
+
args << 'COUNT' << count.to_s
|
3309
|
+
end
|
3310
|
+
args << 'JUSTID' if justid
|
3311
|
+
blk = justid ? HashifyStreamAutoclaimJustId : HashifyStreamAutoclaim
|
3312
|
+
synchronize { |client| client.call(args, &blk) }
|
3313
|
+
end
|
3314
|
+
|
3223
3315
|
# Fetches not acknowledging pending entries
|
3224
3316
|
#
|
3225
3317
|
# @example With key and group
|
@@ -3430,6 +3522,20 @@ class Redis
|
|
3430
3522
|
end
|
3431
3523
|
}
|
3432
3524
|
|
3525
|
+
HashifyStreamAutoclaim = lambda { |reply|
|
3526
|
+
{
|
3527
|
+
'next' => reply[0],
|
3528
|
+
'entries' => reply[1].map { |entry| [entry[0], entry[1].each_slice(2).to_h] }
|
3529
|
+
}
|
3530
|
+
}
|
3531
|
+
|
3532
|
+
HashifyStreamAutoclaimJustId = lambda { |reply|
|
3533
|
+
{
|
3534
|
+
'next' => reply[0],
|
3535
|
+
'entries' => reply[1]
|
3536
|
+
}
|
3537
|
+
}
|
3538
|
+
|
3433
3539
|
HashifyStreamPendings = lambda { |reply|
|
3434
3540
|
{
|
3435
3541
|
'size' => reply[0],
|
data/lib/redis/client.rb
CHANGED
@@ -17,6 +17,7 @@ class Redis
|
|
17
17
|
write_timeout: nil,
|
18
18
|
connect_timeout: nil,
|
19
19
|
timeout: 5.0,
|
20
|
+
username: nil,
|
20
21
|
password: nil,
|
21
22
|
db: 0,
|
22
23
|
driver: nil,
|
@@ -61,6 +62,10 @@ class Redis
|
|
61
62
|
@options[:read_timeout]
|
62
63
|
end
|
63
64
|
|
65
|
+
def username
|
66
|
+
@options[:username]
|
67
|
+
end
|
68
|
+
|
64
69
|
def password
|
65
70
|
@options[:password]
|
66
71
|
end
|
@@ -110,7 +115,19 @@ class Redis
|
|
110
115
|
# Don't try to reconnect when the connection is fresh
|
111
116
|
with_reconnect(false) do
|
112
117
|
establish_connection
|
113
|
-
|
118
|
+
if password
|
119
|
+
if username
|
120
|
+
begin
|
121
|
+
call [:auth, username, password]
|
122
|
+
rescue CommandError # Likely on Redis < 6
|
123
|
+
call [:auth, password]
|
124
|
+
end
|
125
|
+
else
|
126
|
+
call [:auth, password]
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
call [:readonly] if @options[:readonly]
|
114
131
|
call [:select, db] if db != 0
|
115
132
|
call [:client, :setname, @options[:id]] if @options[:id]
|
116
133
|
@connector.check(self)
|
@@ -131,7 +148,7 @@ class Redis
|
|
131
148
|
reply = process([command]) { read }
|
132
149
|
raise reply if reply.is_a?(CommandError)
|
133
150
|
|
134
|
-
if block_given?
|
151
|
+
if block_given? && reply != 'QUEUED'
|
135
152
|
yield reply
|
136
153
|
else
|
137
154
|
reply
|
@@ -434,7 +451,8 @@ class Redis
|
|
434
451
|
defaults[:scheme] = uri.scheme
|
435
452
|
defaults[:host] = uri.host if uri.host
|
436
453
|
defaults[:port] = uri.port if uri.port
|
437
|
-
defaults[:
|
454
|
+
defaults[:username] = CGI.unescape(uri.user) if uri.user && !uri.user.empty?
|
455
|
+
defaults[:password] = CGI.unescape(uri.password) if uri.password && !uri.password.empty?
|
438
456
|
defaults[:db] = uri.path[1..-1].to_i if uri.path
|
439
457
|
defaults[:role] = :master
|
440
458
|
else
|
@@ -510,7 +528,7 @@ class Redis
|
|
510
528
|
require_relative "connection/#{driver}"
|
511
529
|
rescue LoadError, NameError
|
512
530
|
begin
|
513
|
-
require "connection/#{driver}"
|
531
|
+
require "redis/connection/#{driver}"
|
514
532
|
rescue LoadError, NameError => error
|
515
533
|
raise "Cannot load driver #{driver.inspect}: #{error.message}"
|
516
534
|
end
|
@@ -579,6 +597,7 @@ class Redis
|
|
579
597
|
client = Client.new(@options.merge({
|
580
598
|
host: sentinel[:host] || sentinel["host"],
|
581
599
|
port: sentinel[:port] || sentinel["port"],
|
600
|
+
username: sentinel[:username] || sentinel["username"],
|
582
601
|
password: sentinel[:password] || sentinel["password"],
|
583
602
|
reconnect_attempts: 0
|
584
603
|
}))
|
data/lib/redis/cluster.rb
CHANGED
@@ -78,11 +78,13 @@ class Redis
|
|
78
78
|
end
|
79
79
|
|
80
80
|
def call_pipeline(pipeline)
|
81
|
-
node_keys
|
82
|
-
|
81
|
+
node_keys = pipeline.commands.map { |cmd| find_node_key(cmd, primary_only: true) }.compact.uniq
|
82
|
+
if node_keys.size > 1
|
83
|
+
raise(CrossSlotPipeliningError,
|
84
|
+
pipeline.commands.map { |cmd| @command.extract_first_key(cmd) }.reject(&:empty?).uniq)
|
85
|
+
end
|
83
86
|
|
84
|
-
|
85
|
-
try_send(node, :call_pipeline, pipeline)
|
87
|
+
try_send(find_node(node_keys.first), :call_pipeline, pipeline)
|
86
88
|
end
|
87
89
|
|
88
90
|
def call_with_timeout(command, timeout, &block)
|
@@ -128,7 +130,7 @@ class Redis
|
|
128
130
|
def send_command(command, &block)
|
129
131
|
cmd = command.first.to_s.downcase
|
130
132
|
case cmd
|
131
|
-
when 'auth', 'bgrewriteaof', 'bgsave', 'quit', 'save'
|
133
|
+
when 'acl', 'auth', 'bgrewriteaof', 'bgsave', 'quit', 'save'
|
132
134
|
@node.call_all(command, &block).first
|
133
135
|
when 'flushall', 'flushdb'
|
134
136
|
@node.call_master(command, &block).first
|
@@ -253,14 +255,14 @@ class Redis
|
|
253
255
|
find_node(node_key)
|
254
256
|
end
|
255
257
|
|
256
|
-
def find_node_key(command)
|
258
|
+
def find_node_key(command, primary_only: false)
|
257
259
|
key = @command.extract_first_key(command)
|
258
260
|
return if key.empty?
|
259
261
|
|
260
262
|
slot = KeySlotConverter.convert(key)
|
261
263
|
return unless @slot.exists?(slot)
|
262
264
|
|
263
|
-
if @command.should_send_to_master?(command)
|
265
|
+
if @command.should_send_to_master?(command) || primary_only
|
264
266
|
@slot.find_node_key_of_master(slot)
|
265
267
|
else
|
266
268
|
@slot.find_node_key_of_slave(slot)
|
@@ -285,11 +287,5 @@ class Redis
|
|
285
287
|
@node.map(&:disconnect)
|
286
288
|
@node, @slot = fetch_cluster_info!(@option)
|
287
289
|
end
|
288
|
-
|
289
|
-
def extract_keys_in_pipeline(pipeline)
|
290
|
-
node_keys = pipeline.commands.map { |cmd| find_node_key(cmd) }.compact.uniq
|
291
|
-
command_keys = pipeline.commands.map { |cmd| @command.extract_first_key(cmd) }.reject(&:empty?)
|
292
|
-
[node_keys, command_keys]
|
293
|
-
end
|
294
290
|
end
|
295
291
|
end
|
data/lib/redis/cluster/node.rb
CHANGED
@@ -76,8 +76,9 @@ class Redis
|
|
76
76
|
clients = options.map do |node_key, option|
|
77
77
|
next if replica_disabled? && slave?(node_key)
|
78
78
|
|
79
|
+
option = option.merge(readonly: true) if slave?(node_key)
|
80
|
+
|
79
81
|
client = Client.new(option)
|
80
|
-
client.call(%i[readonly]) if slave?(node_key)
|
81
82
|
[node_key, client]
|
82
83
|
end
|
83
84
|
|
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
|
|
@@ -67,10 +67,13 @@ class Redis
|
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
|
-
def
|
70
|
+
def write(buffer)
|
71
|
+
return super(buffer) unless @write_timeout
|
72
|
+
|
73
|
+
bytes_to_write = buffer.bytesize
|
71
74
|
total_bytes_written = 0
|
72
75
|
loop do
|
73
|
-
case bytes_written = write_nonblock(
|
76
|
+
case bytes_written = write_nonblock(buffer, exception: false)
|
74
77
|
when :wait_readable
|
75
78
|
unless wait_readable(@write_timeout)
|
76
79
|
raise Redis::TimeoutError
|
@@ -83,28 +86,13 @@ class Redis
|
|
83
86
|
raise Errno::ECONNRESET
|
84
87
|
when Integer
|
85
88
|
total_bytes_written += bytes_written
|
86
|
-
|
87
|
-
|
88
|
-
else
|
89
|
+
|
90
|
+
if total_bytes_written >= bytes_to_write
|
89
91
|
return total_bytes_written
|
90
92
|
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
93
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
data = data.b
|
99
|
-
length = data.bytesize
|
100
|
-
total_count = 0
|
101
|
-
loop do
|
102
|
-
count = _write_to_socket(data)
|
103
|
-
|
104
|
-
total_count += count
|
105
|
-
return total_count if total_count >= length
|
106
|
-
|
107
|
-
data = data.byteslice(count..-1)
|
94
|
+
buffer = buffer.byteslice(bytes_written..-1)
|
95
|
+
end
|
108
96
|
end
|
109
97
|
end
|
110
98
|
end
|
data/lib/redis/distributed.rb
CHANGED
@@ -413,14 +413,14 @@ class Redis
|
|
413
413
|
node_for(key).rpushx(key, value)
|
414
414
|
end
|
415
415
|
|
416
|
-
# Remove and get the first
|
417
|
-
def lpop(key)
|
418
|
-
node_for(key).lpop(key)
|
416
|
+
# Remove and get the first elements in a list.
|
417
|
+
def lpop(key, count = nil)
|
418
|
+
node_for(key).lpop(key, count)
|
419
419
|
end
|
420
420
|
|
421
|
-
# Remove and get the last
|
422
|
-
def rpop(key)
|
423
|
-
node_for(key).rpop(key)
|
421
|
+
# Remove and get the last elements in a list.
|
422
|
+
def rpop(key, count = nil)
|
423
|
+
node_for(key).rpop(key, count)
|
424
424
|
end
|
425
425
|
|
426
426
|
# Remove the last element in a list, append it to another list and return
|
@@ -674,6 +674,13 @@ class Redis
|
|
674
674
|
node_for(key).zcount(key, min, max)
|
675
675
|
end
|
676
676
|
|
677
|
+
# Get the intersection of multiple sorted sets
|
678
|
+
def zinter(*keys, **options)
|
679
|
+
ensure_same_node(:zinter, keys) do |node|
|
680
|
+
node.zinter(*keys, **options)
|
681
|
+
end
|
682
|
+
end
|
683
|
+
|
677
684
|
# Intersect multiple sorted sets and store the resulting sorted set in a new
|
678
685
|
# key.
|
679
686
|
def zinterstore(destination, keys, **options)
|
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.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ezra Zygmuntowicz
|
@@ -16,7 +16,7 @@ authors:
|
|
16
16
|
autorequire:
|
17
17
|
bindir: bin
|
18
18
|
cert_chain: []
|
19
|
-
date:
|
19
|
+
date: 2021-07-28 00:00:00.000000000 Z
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
22
22
|
name: em-synchrony
|
@@ -102,9 +102,9 @@ 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.4.0
|
106
106
|
homepage_uri: https://github.com/redis/redis-rb
|
107
|
-
source_code_uri: https://github.com/redis/redis-rb/tree/v4.
|
107
|
+
source_code_uri: https://github.com/redis/redis-rb/tree/v4.4.0
|
108
108
|
post_install_message:
|
109
109
|
rdoc_options: []
|
110
110
|
require_paths:
|