redis 4.3.1 → 4.5.1
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 +29 -0
- data/lib/redis/client.rb +18 -2
- data/lib/redis/cluster/command_loader.rb +6 -7
- data/lib/redis/cluster/node.rb +2 -1
- data/lib/redis/cluster.rb +8 -12
- data/lib/redis/connection/command_helper.rb +2 -0
- data/lib/redis/connection/ruby.rb +5 -4
- data/lib/redis/distributed.rb +47 -0
- data/lib/redis/version.rb +1 -1
- data/lib/redis.rb +282 -3
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7a7232fef186e6d6a11a90d8dd9aa7c71114f017e0afe9378999a96c9e6b4e05
|
4
|
+
data.tar.gz: 689f176b87909bf61eb60e57d1eb795198162a7e039104c80facf36880964bda
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 22b11dee92e298b46bb94a707156d3dbf9afb83c8e9e8cbf82cf366d582a7c1b7295d7f09a0fec01965245f4800c2482c0559d646f46ff1c6bba6423ab398ba9
|
7
|
+
data.tar.gz: 9a74ba29c8cb3d7634a44c78e30018a048e19cca66c7fad6a226722183f66546bebb370c92ecdf20522bb35ee72a09b2fda64891ff6d94db1702375bd1ba6b46
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,34 @@
|
|
1
1
|
# Unreleased
|
2
2
|
|
3
|
+
# 4.5.1
|
4
|
+
|
5
|
+
* Restore the accidential auth behavior of redis-rb 4.3.0 with a warning. If provided with the `default` user's password, but a wrong username,
|
6
|
+
redis-rb will first try to connect as the provided user, but then will fallback to connect as the `default` user with the provided password.
|
7
|
+
This behavior is deprecated and will be removed in Redis 4.6.0. Fix #1038.
|
8
|
+
|
9
|
+
# 4.5.0
|
10
|
+
|
11
|
+
* Handle parts of the command using incompatible encodings. See #1037.
|
12
|
+
* Add GET option to SET command. See #1036.
|
13
|
+
* Add ZRANDMEMBER command. See #1035.
|
14
|
+
* Add LMOVE/BLMOVE commands. See #1034.
|
15
|
+
* Add ZMSCORE command. See #1032.
|
16
|
+
* Add LT/GT options to ZADD. See #1033.
|
17
|
+
* Add SMISMEMBER command. See #1031.
|
18
|
+
* Add EXAT/PXAT options to SET. See #1028.
|
19
|
+
* Add GETDEL/GETEX commands. See #1024.
|
20
|
+
* `Redis#exists` now returns an Integer by default, as warned since 4.2.0. The old behavior can be restored with `Redis.exists_returns_integer = false`.
|
21
|
+
* Fix Redis < 6 detection during connect. See #1025.
|
22
|
+
* Fix fetching command details in Redis cluster when the first node is unhealthy. See #1026.
|
23
|
+
|
24
|
+
# 4.4.0
|
25
|
+
|
26
|
+
* Redis cluster: fix cross-slot validation in pipelines. Fix ##1019.
|
27
|
+
* Add support for `XAUTOCLAIM`. See #1018.
|
28
|
+
* Properly issue `READONLY` when reconnecting to replicas. Fix #1017.
|
29
|
+
* Make `del` a noop if passed an empty list of keys. See #998.
|
30
|
+
* Add support for `ZINTER`. See #995.
|
31
|
+
|
3
32
|
# 4.3.1
|
4
33
|
|
5
34
|
* Fix password authentication against redis server 5 and older.
|
data/lib/redis/client.rb
CHANGED
@@ -119,13 +119,29 @@ class Redis
|
|
119
119
|
if username
|
120
120
|
begin
|
121
121
|
call [:auth, username, password]
|
122
|
-
rescue CommandError # Likely on Redis < 6
|
123
|
-
|
122
|
+
rescue CommandError => err # Likely on Redis < 6
|
123
|
+
if err.message.match?(/ERR wrong number of arguments for \'auth\' command/)
|
124
|
+
call [:auth, password]
|
125
|
+
elsif err.message.match?(/WRONGPASS invalid username-password pair/)
|
126
|
+
begin
|
127
|
+
call [:auth, password]
|
128
|
+
rescue CommandError
|
129
|
+
raise err
|
130
|
+
end
|
131
|
+
::Kernel.warn(
|
132
|
+
"[redis-rb] The Redis connection was configured with username #{username.inspect}, but" \
|
133
|
+
" the provided password was for the default user. This will start failing in redis-rb 4.6."
|
134
|
+
)
|
135
|
+
else
|
136
|
+
raise
|
137
|
+
end
|
124
138
|
end
|
125
139
|
else
|
126
140
|
call [:auth, password]
|
127
141
|
end
|
128
142
|
end
|
143
|
+
|
144
|
+
call [:readonly] if @options[:readonly]
|
129
145
|
call [:select, db] if db != 0
|
130
146
|
call [:client, :setname, @options[:id]] if @options[:id]
|
131
147
|
@connector.check(self)
|
@@ -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
|
-
|
17
|
-
|
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
|
-
|
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
|
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.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)
|
@@ -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
|
@@ -12,11 +12,13 @@ class Redis
|
|
12
12
|
if i.is_a? Array
|
13
13
|
i.each do |j|
|
14
14
|
j = j.to_s
|
15
|
+
j = j.encoding == Encoding::BINARY ? j : j.b
|
15
16
|
command << "$#{j.bytesize}"
|
16
17
|
command << j
|
17
18
|
end
|
18
19
|
else
|
19
20
|
i = i.to_s
|
21
|
+
i = i.encoding == Encoding::BINARY ? i : i.b
|
20
22
|
command << "$#{i.bytesize}"
|
21
23
|
command << i
|
22
24
|
end
|
@@ -21,7 +21,7 @@ class Redis
|
|
21
21
|
super(*args)
|
22
22
|
|
23
23
|
@timeout = @write_timeout = nil
|
24
|
-
@buffer = "".
|
24
|
+
@buffer = "".b
|
25
25
|
end
|
26
26
|
|
27
27
|
def timeout=(timeout)
|
@@ -35,7 +35,8 @@ class Redis
|
|
35
35
|
def read(nbytes)
|
36
36
|
result = @buffer.slice!(0, nbytes)
|
37
37
|
|
38
|
-
|
38
|
+
buffer = String.new(capacity: nbytes, encoding: Encoding::ASCII_8BIT)
|
39
|
+
result << _read_from_socket(nbytes - result.bytesize, buffer) while result.bytesize < nbytes
|
39
40
|
|
40
41
|
result
|
41
42
|
end
|
@@ -48,9 +49,9 @@ class Redis
|
|
48
49
|
@buffer.slice!(0, crlf + CRLF.bytesize)
|
49
50
|
end
|
50
51
|
|
51
|
-
def _read_from_socket(nbytes)
|
52
|
+
def _read_from_socket(nbytes, buffer = nil)
|
52
53
|
loop do
|
53
|
-
case chunk = read_nonblock(nbytes, exception: false)
|
54
|
+
case chunk = read_nonblock(nbytes, buffer, exception: false)
|
54
55
|
when :wait_readable
|
55
56
|
unless wait_readable(@timeout)
|
56
57
|
raise Redis::TimeoutError
|
data/lib/redis/distributed.rb
CHANGED
@@ -316,6 +316,16 @@ class Redis
|
|
316
316
|
node_for(key).get(key)
|
317
317
|
end
|
318
318
|
|
319
|
+
# Get the value of a key and delete it.
|
320
|
+
def getdel(key)
|
321
|
+
node_for(key).getdel(key)
|
322
|
+
end
|
323
|
+
|
324
|
+
# Get the value of a key and sets its time to live based on options.
|
325
|
+
def getex(key, **options)
|
326
|
+
node_for(key).getex(key, **options)
|
327
|
+
end
|
328
|
+
|
319
329
|
# Get the values of all the given keys as an Array.
|
320
330
|
def mget(*keys)
|
321
331
|
mapped_mget(*keys).values_at(*keys)
|
@@ -393,6 +403,21 @@ class Redis
|
|
393
403
|
node_for(key).llen(key)
|
394
404
|
end
|
395
405
|
|
406
|
+
# Remove the first/last element in a list, append/prepend it to another list and return it.
|
407
|
+
def lmove(source, destination, where_source, where_destination)
|
408
|
+
ensure_same_node(:lmove, [source, destination]) do |node|
|
409
|
+
node.lmove(source, destination, where_source, where_destination)
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
# Remove the first/last element in a list and append/prepend it
|
414
|
+
# to another list and return it, or block until one is available.
|
415
|
+
def blmove(source, destination, where_source, where_destination, timeout: 0)
|
416
|
+
ensure_same_node(:lmove, [source, destination]) do |node|
|
417
|
+
node.blmove(source, destination, where_source, where_destination, timeout: timeout)
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
396
421
|
# Prepend one or more values to a list.
|
397
422
|
def lpush(key, value)
|
398
423
|
node_for(key).lpush(key, value)
|
@@ -542,6 +567,11 @@ class Redis
|
|
542
567
|
node_for(key).sismember(key, member)
|
543
568
|
end
|
544
569
|
|
570
|
+
# Determine if multiple values are members of a set.
|
571
|
+
def smismember(key, *members)
|
572
|
+
node_for(key).smismember(key, *members)
|
573
|
+
end
|
574
|
+
|
545
575
|
# Get all the members in a set.
|
546
576
|
def smembers(key)
|
547
577
|
node_for(key).smembers(key)
|
@@ -626,6 +656,16 @@ class Redis
|
|
626
656
|
node_for(key).zscore(key, member)
|
627
657
|
end
|
628
658
|
|
659
|
+
# Get one or more random members from a sorted set.
|
660
|
+
def zrandmember(key, count = nil, **options)
|
661
|
+
node_for(key).zrandmember(key, count, **options)
|
662
|
+
end
|
663
|
+
|
664
|
+
# Get the scores associated with the given members in a sorted set.
|
665
|
+
def zmscore(key, *members)
|
666
|
+
node_for(key).zmscore(key, *members)
|
667
|
+
end
|
668
|
+
|
629
669
|
# Return a range of members in a sorted set, by index.
|
630
670
|
def zrange(key, start, stop, **options)
|
631
671
|
node_for(key).zrange(key, start, stop, **options)
|
@@ -674,6 +714,13 @@ class Redis
|
|
674
714
|
node_for(key).zcount(key, min, max)
|
675
715
|
end
|
676
716
|
|
717
|
+
# Get the intersection of multiple sorted sets
|
718
|
+
def zinter(*keys, **options)
|
719
|
+
ensure_same_node(:zinter, keys) do |node|
|
720
|
+
node.zinter(*keys, **options)
|
721
|
+
end
|
722
|
+
end
|
723
|
+
|
677
724
|
# Intersect multiple sorted sets and store the resulting sorted set in a new
|
678
725
|
# key.
|
679
726
|
def zinterstore(destination, keys, **options)
|
data/lib/redis/version.rb
CHANGED
data/lib/redis.rb
CHANGED
@@ -4,6 +4,8 @@ require "monitor"
|
|
4
4
|
require_relative "redis/errors"
|
5
5
|
|
6
6
|
class Redis
|
7
|
+
@exists_returns_integer = true
|
8
|
+
|
7
9
|
class << self
|
8
10
|
attr_reader :exists_returns_integer
|
9
11
|
|
@@ -555,6 +557,9 @@ class Redis
|
|
555
557
|
# @param [String, Array<String>] keys
|
556
558
|
# @return [Integer] number of keys that were deleted
|
557
559
|
def del(*keys)
|
560
|
+
keys.flatten!(1)
|
561
|
+
return 0 if keys.empty?
|
562
|
+
|
558
563
|
synchronize do |client|
|
559
564
|
client.call([:del] + keys)
|
560
565
|
end
|
@@ -831,17 +836,23 @@ class Redis
|
|
831
836
|
# @param [Hash] options
|
832
837
|
# - `:ex => Integer`: Set the specified expire time, in seconds.
|
833
838
|
# - `:px => Integer`: Set the specified expire time, in milliseconds.
|
839
|
+
# - `:exat => Integer` : Set the specified Unix time at which the key will expire, in seconds.
|
840
|
+
# - `:pxat => Integer` : Set the specified Unix time at which the key will expire, in milliseconds.
|
834
841
|
# - `:nx => true`: Only set the key if it does not already exist.
|
835
842
|
# - `:xx => true`: Only set the key if it already exist.
|
836
843
|
# - `:keepttl => true`: Retain the time to live associated with the key.
|
844
|
+
# - `:get => true`: Return the old string stored at key, or nil if key did not exist.
|
837
845
|
# @return [String, Boolean] `"OK"` or true, false if `:nx => true` or `:xx => true`
|
838
|
-
def set(key, value, ex: nil, px: nil, nx: nil, xx: nil, keepttl: nil)
|
846
|
+
def set(key, value, ex: nil, px: nil, exat: nil, pxat: nil, nx: nil, xx: nil, keepttl: nil, get: nil)
|
839
847
|
args = [:set, key, value.to_s]
|
840
848
|
args << "EX" << ex if ex
|
841
849
|
args << "PX" << px if px
|
850
|
+
args << "EXAT" << exat if exat
|
851
|
+
args << "PXAT" << pxat if pxat
|
842
852
|
args << "NX" if nx
|
843
853
|
args << "XX" if xx
|
844
854
|
args << "KEEPTTL" if keepttl
|
855
|
+
args << "GET" if get
|
845
856
|
|
846
857
|
synchronize do |client|
|
847
858
|
if nx || xx
|
@@ -1107,6 +1118,45 @@ class Redis
|
|
1107
1118
|
end
|
1108
1119
|
end
|
1109
1120
|
|
1121
|
+
# Get the value of key and delete the key. This command is similar to GET,
|
1122
|
+
# except for the fact that it also deletes the key on success.
|
1123
|
+
#
|
1124
|
+
# @param [String] key
|
1125
|
+
# @return [String] the old value stored in the key, or `nil` if the key
|
1126
|
+
# did not exist
|
1127
|
+
def getdel(key)
|
1128
|
+
synchronize do |client|
|
1129
|
+
client.call([:getdel, key])
|
1130
|
+
end
|
1131
|
+
end
|
1132
|
+
|
1133
|
+
# Get the value of key and optionally set its expiration. GETEX is similar to
|
1134
|
+
# GET, but is a write command with additional options. When no options are
|
1135
|
+
# provided, GETEX behaves like GET.
|
1136
|
+
#
|
1137
|
+
# @param [String] key
|
1138
|
+
# @param [Hash] options
|
1139
|
+
# - `:ex => Integer`: Set the specified expire time, in seconds.
|
1140
|
+
# - `:px => Integer`: Set the specified expire time, in milliseconds.
|
1141
|
+
# - `:exat => true`: Set the specified Unix time at which the key will
|
1142
|
+
# expire, in seconds.
|
1143
|
+
# - `:pxat => true`: Set the specified Unix time at which the key will
|
1144
|
+
# expire, in milliseconds.
|
1145
|
+
# - `:persist => true`: Remove the time to live associated with the key.
|
1146
|
+
# @return [String] The value of key, or nil when key does not exist.
|
1147
|
+
def getex(key, ex: nil, px: nil, exat: nil, pxat: nil, persist: false)
|
1148
|
+
args = [:getex, key]
|
1149
|
+
args << "EX" << ex if ex
|
1150
|
+
args << "PX" << px if px
|
1151
|
+
args << "EXAT" << exat if exat
|
1152
|
+
args << "PXAT" << pxat if pxat
|
1153
|
+
args << "PERSIST" if persist
|
1154
|
+
|
1155
|
+
synchronize do |client|
|
1156
|
+
client.call(args)
|
1157
|
+
end
|
1158
|
+
end
|
1159
|
+
|
1110
1160
|
# Get the length of the value stored in a key.
|
1111
1161
|
#
|
1112
1162
|
# @param [String] key
|
@@ -1128,6 +1178,59 @@ class Redis
|
|
1128
1178
|
end
|
1129
1179
|
end
|
1130
1180
|
|
1181
|
+
# Remove the first/last element in a list, append/prepend it to another list and return it.
|
1182
|
+
#
|
1183
|
+
# @param [String] source source key
|
1184
|
+
# @param [String] destination destination key
|
1185
|
+
# @param [String, Symbol] where_source from where to remove the element from the source list
|
1186
|
+
# e.g. 'LEFT' - from head, 'RIGHT' - from tail
|
1187
|
+
# @param [String, Symbol] where_destination where to push the element to the source list
|
1188
|
+
# e.g. 'LEFT' - to head, 'RIGHT' - to tail
|
1189
|
+
#
|
1190
|
+
# @return [nil, String] the element, or nil when the source key does not exist
|
1191
|
+
#
|
1192
|
+
# @note This command comes in place of the now deprecated RPOPLPUSH.
|
1193
|
+
# Doing LMOVE RIGHT LEFT is equivalent.
|
1194
|
+
def lmove(source, destination, where_source, where_destination)
|
1195
|
+
where_source, where_destination = _normalize_move_wheres(where_source, where_destination)
|
1196
|
+
|
1197
|
+
synchronize do |client|
|
1198
|
+
client.call([:lmove, source, destination, where_source, where_destination])
|
1199
|
+
end
|
1200
|
+
end
|
1201
|
+
|
1202
|
+
# Remove the first/last element in a list and append/prepend it
|
1203
|
+
# to another list and return it, or block until one is available.
|
1204
|
+
#
|
1205
|
+
# @example With timeout
|
1206
|
+
# element = redis.blmove("foo", "bar", "LEFT", "RIGHT", timeout: 5)
|
1207
|
+
# # => nil on timeout
|
1208
|
+
# # => "element" on success
|
1209
|
+
# @example Without timeout
|
1210
|
+
# element = redis.blmove("foo", "bar", "LEFT", "RIGHT")
|
1211
|
+
# # => "element"
|
1212
|
+
#
|
1213
|
+
# @param [String] source source key
|
1214
|
+
# @param [String] destination destination key
|
1215
|
+
# @param [String, Symbol] where_source from where to remove the element from the source list
|
1216
|
+
# e.g. 'LEFT' - from head, 'RIGHT' - from tail
|
1217
|
+
# @param [String, Symbol] where_destination where to push the element to the source list
|
1218
|
+
# e.g. 'LEFT' - to head, 'RIGHT' - to tail
|
1219
|
+
# @param [Hash] options
|
1220
|
+
# - `:timeout => Numeric`: timeout in seconds, defaults to no timeout
|
1221
|
+
#
|
1222
|
+
# @return [nil, String] the element, or nil when the source key does not exist or the timeout expired
|
1223
|
+
#
|
1224
|
+
def blmove(source, destination, where_source, where_destination, timeout: 0)
|
1225
|
+
where_source, where_destination = _normalize_move_wheres(where_source, where_destination)
|
1226
|
+
|
1227
|
+
synchronize do |client|
|
1228
|
+
command = [:blmove, source, destination, where_source, where_destination, timeout]
|
1229
|
+
timeout += client.timeout if timeout > 0
|
1230
|
+
client.call_with_timeout(command, timeout)
|
1231
|
+
end
|
1232
|
+
end
|
1233
|
+
|
1131
1234
|
# Prepend one or more values to a list, creating the list if it doesn't exist
|
1132
1235
|
#
|
1133
1236
|
# @param [String] key
|
@@ -1476,6 +1579,19 @@ class Redis
|
|
1476
1579
|
end
|
1477
1580
|
end
|
1478
1581
|
|
1582
|
+
# Determine if multiple values are members of a set.
|
1583
|
+
#
|
1584
|
+
# @param [String] key
|
1585
|
+
# @param [String, Array<String>] members
|
1586
|
+
# @return [Array<Boolean>]
|
1587
|
+
def smismember(key, *members)
|
1588
|
+
synchronize do |client|
|
1589
|
+
client.call([:smismember, key, *members]) do |reply|
|
1590
|
+
reply.map(&Boolify)
|
1591
|
+
end
|
1592
|
+
end
|
1593
|
+
end
|
1594
|
+
|
1479
1595
|
# Get all the members in a set.
|
1480
1596
|
#
|
1481
1597
|
# @param [String] key
|
@@ -1580,6 +1696,10 @@ class Redis
|
|
1580
1696
|
# add elements)
|
1581
1697
|
# - `:nx => true`: Don't update already existing elements (always
|
1582
1698
|
# add new elements)
|
1699
|
+
# - `:lt => true`: Only update existing elements if the new score
|
1700
|
+
# is less than the current score
|
1701
|
+
# - `:gt => true`: Only update existing elements if the new score
|
1702
|
+
# is greater than the current score
|
1583
1703
|
# - `:ch => true`: Modify the return value from the number of new
|
1584
1704
|
# elements added, to the total number of elements changed (CH is an
|
1585
1705
|
# abbreviation of changed); changed elements are new elements added
|
@@ -1594,10 +1714,12 @@ class Redis
|
|
1594
1714
|
# pairs that were **added** to the sorted set.
|
1595
1715
|
# - `Float` when option :incr is specified, holding the score of the member
|
1596
1716
|
# after incrementing it.
|
1597
|
-
def zadd(key, *args, nx: nil, xx: nil, ch: nil, incr: nil)
|
1717
|
+
def zadd(key, *args, nx: nil, xx: nil, lt: nil, gt: nil, ch: nil, incr: nil)
|
1598
1718
|
command = [:zadd, key]
|
1599
1719
|
command << "NX" if nx
|
1600
1720
|
command << "XX" if xx
|
1721
|
+
command << "LT" if lt
|
1722
|
+
command << "GT" if gt
|
1601
1723
|
command << "CH" if ch
|
1602
1724
|
command << "INCR" if incr
|
1603
1725
|
|
@@ -1760,6 +1882,63 @@ class Redis
|
|
1760
1882
|
end
|
1761
1883
|
end
|
1762
1884
|
|
1885
|
+
# Get the scores associated with the given members in a sorted set.
|
1886
|
+
#
|
1887
|
+
# @example Get the scores for members "a" and "b"
|
1888
|
+
# redis.zmscore("zset", "a", "b")
|
1889
|
+
# # => [32.0, 48.0]
|
1890
|
+
#
|
1891
|
+
# @param [String] key
|
1892
|
+
# @param [String, Array<String>] members
|
1893
|
+
# @return [Array<Float>] scores of the members
|
1894
|
+
def zmscore(key, *members)
|
1895
|
+
synchronize do |client|
|
1896
|
+
client.call([:zmscore, key, *members]) do |reply|
|
1897
|
+
reply.map(&Floatify)
|
1898
|
+
end
|
1899
|
+
end
|
1900
|
+
end
|
1901
|
+
|
1902
|
+
# Get one or more random members from a sorted set.
|
1903
|
+
#
|
1904
|
+
# @example Get one random member
|
1905
|
+
# redis.zrandmember("zset")
|
1906
|
+
# # => "a"
|
1907
|
+
# @example Get multiple random members
|
1908
|
+
# redis.zrandmember("zset", 2)
|
1909
|
+
# # => ["a", "b"]
|
1910
|
+
# @example Gem multiple random members with scores
|
1911
|
+
# redis.zrandmember("zset", 2, with_scores: true)
|
1912
|
+
# # => [["a", 2.0], ["b", 3.0]]
|
1913
|
+
#
|
1914
|
+
# @param [String] key
|
1915
|
+
# @param [Integer] count
|
1916
|
+
# @param [Hash] options
|
1917
|
+
# - `:with_scores => true`: include scores in output
|
1918
|
+
#
|
1919
|
+
# @return [nil, String, Array<String>, Array<[String, Float]>]
|
1920
|
+
# - when `key` does not exist or set is empty, `nil`
|
1921
|
+
# - when `count` is not specified, a member
|
1922
|
+
# - when `count` is specified and `:with_scores` is not specified, an array of members
|
1923
|
+
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
1924
|
+
def zrandmember(key, count = nil, withscores: false, with_scores: withscores)
|
1925
|
+
if with_scores && count.nil?
|
1926
|
+
raise ArgumentError, "count argument must be specified"
|
1927
|
+
end
|
1928
|
+
|
1929
|
+
args = [:zrandmember, key]
|
1930
|
+
args << count if count
|
1931
|
+
|
1932
|
+
if with_scores
|
1933
|
+
args << "WITHSCORES"
|
1934
|
+
block = FloatifyPairs
|
1935
|
+
end
|
1936
|
+
|
1937
|
+
synchronize do |client|
|
1938
|
+
client.call(args, &block)
|
1939
|
+
end
|
1940
|
+
end
|
1941
|
+
|
1763
1942
|
# Return a range of members in a sorted set, by index.
|
1764
1943
|
#
|
1765
1944
|
# @example Retrieve all members from a sorted set
|
@@ -2062,6 +2241,45 @@ class Redis
|
|
2062
2241
|
end
|
2063
2242
|
end
|
2064
2243
|
|
2244
|
+
# Return the intersection of multiple sorted sets
|
2245
|
+
#
|
2246
|
+
# @example Retrieve the intersection of `2*zsetA` and `1*zsetB`
|
2247
|
+
# redis.zinter("zsetA", "zsetB", :weights => [2.0, 1.0])
|
2248
|
+
# # => ["v1", "v2"]
|
2249
|
+
# @example Retrieve the intersection of `2*zsetA` and `1*zsetB`, and their scores
|
2250
|
+
# redis.zinter("zsetA", "zsetB", :weights => [2.0, 1.0], :with_scores => true)
|
2251
|
+
# # => [["v1", 3.0], ["v2", 6.0]]
|
2252
|
+
#
|
2253
|
+
# @param [String, Array<String>] keys one or more keys to intersect
|
2254
|
+
# @param [Hash] options
|
2255
|
+
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
2256
|
+
# sorted sets
|
2257
|
+
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
2258
|
+
# - `:with_scores => true`: include scores in output
|
2259
|
+
#
|
2260
|
+
# @return [Array<String>, Array<[String, Float]>]
|
2261
|
+
# - when `:with_scores` is not specified, an array of members
|
2262
|
+
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
2263
|
+
def zinter(*keys, weights: nil, aggregate: nil, with_scores: false)
|
2264
|
+
args = [:zinter, keys.size, *keys]
|
2265
|
+
|
2266
|
+
if weights
|
2267
|
+
args << "WEIGHTS"
|
2268
|
+
args.concat(weights)
|
2269
|
+
end
|
2270
|
+
|
2271
|
+
args << "AGGREGATE" << aggregate if aggregate
|
2272
|
+
|
2273
|
+
if with_scores
|
2274
|
+
args << "WITHSCORES"
|
2275
|
+
block = FloatifyPairs
|
2276
|
+
end
|
2277
|
+
|
2278
|
+
synchronize do |client|
|
2279
|
+
client.call(args, &block)
|
2280
|
+
end
|
2281
|
+
end
|
2282
|
+
|
2065
2283
|
# Intersect multiple sorted sets and store the resulting sorted set in a new
|
2066
2284
|
# key.
|
2067
2285
|
#
|
@@ -3238,6 +3456,38 @@ class Redis
|
|
3238
3456
|
synchronize { |client| client.call(args, &blk) }
|
3239
3457
|
end
|
3240
3458
|
|
3459
|
+
# Transfers ownership of pending stream entries that match the specified criteria.
|
3460
|
+
#
|
3461
|
+
# @example Claim next pending message stuck > 5 minutes and mark as retry
|
3462
|
+
# redis.xautoclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0')
|
3463
|
+
# @example Claim 50 next pending messages stuck > 5 minutes and mark as retry
|
3464
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0', count: 50)
|
3465
|
+
# @example Claim next pending message stuck > 5 minutes and don't mark as retry
|
3466
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0', justid: true)
|
3467
|
+
# @example Claim next pending message after this id stuck > 5 minutes and mark as retry
|
3468
|
+
# redis.xautoclaim('mystream', 'mygroup', 'consumer1', 3600000, '1641321233-0')
|
3469
|
+
#
|
3470
|
+
# @param key [String] the stream key
|
3471
|
+
# @param group [String] the consumer group name
|
3472
|
+
# @param consumer [String] the consumer name
|
3473
|
+
# @param min_idle_time [Integer] the number of milliseconds
|
3474
|
+
# @param start [String] entry id to start scanning from or 0-0 for everything
|
3475
|
+
# @param count [Integer] number of messages to claim (default 1)
|
3476
|
+
# @param justid [Boolean] whether to fetch just an array of entry ids or not.
|
3477
|
+
# Does not increment retry count when true
|
3478
|
+
#
|
3479
|
+
# @return [Hash{String => Hash}] the entries successfully claimed
|
3480
|
+
# @return [Array<String>] the entry ids successfully claimed if justid option is `true`
|
3481
|
+
def xautoclaim(key, group, consumer, min_idle_time, start, count: nil, justid: false)
|
3482
|
+
args = [:xautoclaim, key, group, consumer, min_idle_time, start]
|
3483
|
+
if count
|
3484
|
+
args << 'COUNT' << count.to_s
|
3485
|
+
end
|
3486
|
+
args << 'JUSTID' if justid
|
3487
|
+
blk = justid ? HashifyStreamAutoclaimJustId : HashifyStreamAutoclaim
|
3488
|
+
synchronize { |client| client.call(args, &blk) }
|
3489
|
+
end
|
3490
|
+
|
3241
3491
|
# Fetches not acknowledging pending entries
|
3242
3492
|
#
|
3243
3493
|
# @example With key and group
|
@@ -3444,10 +3694,24 @@ class Redis
|
|
3444
3694
|
|
3445
3695
|
HashifyStreamEntries = lambda { |reply|
|
3446
3696
|
reply.compact.map do |entry_id, values|
|
3447
|
-
[entry_id, values
|
3697
|
+
[entry_id, values&.each_slice(2)&.to_h]
|
3448
3698
|
end
|
3449
3699
|
}
|
3450
3700
|
|
3701
|
+
HashifyStreamAutoclaim = lambda { |reply|
|
3702
|
+
{
|
3703
|
+
'next' => reply[0],
|
3704
|
+
'entries' => reply[1].map { |entry| [entry[0], entry[1].each_slice(2).to_h] }
|
3705
|
+
}
|
3706
|
+
}
|
3707
|
+
|
3708
|
+
HashifyStreamAutoclaimJustId = lambda { |reply|
|
3709
|
+
{
|
3710
|
+
'next' => reply[0],
|
3711
|
+
'entries' => reply[1]
|
3712
|
+
}
|
3713
|
+
}
|
3714
|
+
|
3451
3715
|
HashifyStreamPendings = lambda { |reply|
|
3452
3716
|
{
|
3453
3717
|
'size' => reply[0],
|
@@ -3547,6 +3811,21 @@ class Redis
|
|
3547
3811
|
end
|
3548
3812
|
end
|
3549
3813
|
end
|
3814
|
+
|
3815
|
+
def _normalize_move_wheres(where_source, where_destination)
|
3816
|
+
where_source = where_source.to_s.upcase
|
3817
|
+
where_destination = where_destination.to_s.upcase
|
3818
|
+
|
3819
|
+
if where_source != "LEFT" && where_source != "RIGHT"
|
3820
|
+
raise ArgumentError, "where_source must be 'LEFT' or 'RIGHT'"
|
3821
|
+
end
|
3822
|
+
|
3823
|
+
if where_destination != "LEFT" && where_destination != "RIGHT"
|
3824
|
+
raise ArgumentError, "where_destination must be 'LEFT' or 'RIGHT'"
|
3825
|
+
end
|
3826
|
+
|
3827
|
+
[where_source, where_destination]
|
3828
|
+
end
|
3550
3829
|
end
|
3551
3830
|
|
3552
3831
|
require_relative "redis/version"
|
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.5.1
|
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: 2021-
|
19
|
+
date: 2021-10-15 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.5.1
|
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.5.1
|
108
108
|
post_install_message:
|
109
109
|
rdoc_options: []
|
110
110
|
require_paths:
|
@@ -113,7 +113,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
113
113
|
requirements:
|
114
114
|
- - ">="
|
115
115
|
- !ruby/object:Gem::Version
|
116
|
-
version: 2.
|
116
|
+
version: 2.4.0
|
117
117
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
118
|
requirements:
|
119
119
|
- - ">="
|