redis 4.1.4 → 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 +77 -0
- data/README.md +27 -17
- data/lib/redis/client.rb +108 -74
- data/lib/redis/cluster/command_loader.rb +6 -7
- data/lib/redis/cluster/node.rb +5 -1
- data/lib/redis/cluster/option.rb +9 -3
- data/lib/redis/cluster/slot.rb +28 -14
- data/lib/redis/cluster/slot_loader.rb +2 -3
- data/lib/redis/cluster.rb +13 -13
- data/lib/redis/connection/command_helper.rb +4 -2
- data/lib/redis/connection/hiredis.rb +3 -3
- data/lib/redis/connection/registry.rb +1 -1
- data/lib/redis/connection/ruby.rb +92 -109
- data/lib/redis/connection/synchrony.rb +8 -4
- data/lib/redis/connection.rb +1 -0
- data/lib/redis/distributed.rb +161 -63
- data/lib/redis/errors.rb +1 -0
- data/lib/redis/hash_ring.rb +14 -14
- data/lib/redis/pipeline.rb +6 -8
- data/lib/redis/subscribe.rb +10 -12
- data/lib/redis/version.rb +2 -1
- data/lib/redis.rb +597 -261
- metadata +15 -10
data/lib/redis.rb
CHANGED
@@ -4,12 +4,28 @@ require "monitor"
|
|
4
4
|
require_relative "redis/errors"
|
5
5
|
|
6
6
|
class Redis
|
7
|
-
|
8
|
-
|
7
|
+
@exists_returns_integer = true
|
8
|
+
|
9
|
+
class << self
|
10
|
+
attr_reader :exists_returns_integer
|
11
|
+
|
12
|
+
def exists_returns_integer=(value)
|
13
|
+
unless value
|
14
|
+
message = "`Redis#exists(key)` will return an Integer by default in redis-rb 4.3. The option to explicitly " \
|
15
|
+
"disable this behaviour via `Redis.exists_returns_integer` will be removed in 5.0. You should use " \
|
16
|
+
"`exists?` instead."
|
17
|
+
|
18
|
+
::Kernel.warn(message)
|
19
|
+
end
|
20
|
+
|
21
|
+
@exists_returns_integer = value
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_writer :current
|
9
25
|
end
|
10
26
|
|
11
|
-
def self.current
|
12
|
-
@current
|
27
|
+
def self.current
|
28
|
+
@current ||= Redis.new
|
13
29
|
end
|
14
30
|
|
15
31
|
include MonitorMixin
|
@@ -17,17 +33,22 @@ class Redis
|
|
17
33
|
# Create a new client instance
|
18
34
|
#
|
19
35
|
# @param [Hash] options
|
20
|
-
# @option options [String] :url (value of the environment variable REDIS_URL) a Redis URL, for a TCP connection:
|
36
|
+
# @option options [String] :url (value of the environment variable REDIS_URL) a Redis URL, for a TCP connection:
|
37
|
+
# `redis://:[password]@[hostname]:[port]/[db]` (password, port and database are optional), for a unix socket
|
38
|
+
# connection: `unix://[path to Redis socket]`. This overrides all other options.
|
21
39
|
# @option options [String] :host ("127.0.0.1") server hostname
|
22
40
|
# @option options [Integer] :port (6379) server port
|
23
41
|
# @option options [String] :path path to server socket (overrides host and port)
|
24
42
|
# @option options [Float] :timeout (5.0) timeout in seconds
|
25
43
|
# @option options [Float] :connect_timeout (same as timeout) timeout for initial connect in seconds
|
44
|
+
# @option options [String] :username Username to authenticate against server
|
26
45
|
# @option options [String] :password Password to authenticate against server
|
27
46
|
# @option options [Integer] :db (0) Database to select after initial connect
|
28
47
|
# @option options [Symbol] :driver Driver to use, currently supported: `:ruby`, `:hiredis`, `:synchrony`
|
29
|
-
# @option options [String] :id ID for the client connection, assigns name to current connection by sending
|
30
|
-
#
|
48
|
+
# @option options [String] :id ID for the client connection, assigns name to current connection by sending
|
49
|
+
# `CLIENT SETNAME`
|
50
|
+
# @option options [Hash, Integer] :tcp_keepalive Keepalive values, if Integer `intvl` and `probe` are calculated
|
51
|
+
# based on the value, if Hash `time`, `intvl` and `probes` can be specified as a Integer
|
31
52
|
# @option options [Integer] :reconnect_attempts Number of attempts trying to connect
|
32
53
|
# @option options [Boolean] :inherit_socket (false) Whether to use socket in forked process or not
|
33
54
|
# @option options [Array] :sentinels List of sentinels to contact
|
@@ -52,7 +73,7 @@ class Redis
|
|
52
73
|
end
|
53
74
|
|
54
75
|
# Run code with the client reconnecting
|
55
|
-
def with_reconnect(val=true, &blk)
|
76
|
+
def with_reconnect(val = true, &blk)
|
56
77
|
synchronize do |client|
|
57
78
|
client.with_reconnect(val, &blk)
|
58
79
|
end
|
@@ -125,12 +146,13 @@ class Redis
|
|
125
146
|
|
126
147
|
# Authenticate to the server.
|
127
148
|
#
|
128
|
-
# @param [String]
|
129
|
-
#
|
149
|
+
# @param [Array<String>] args includes both username and password
|
150
|
+
# or only password
|
130
151
|
# @return [String] `OK`
|
131
|
-
|
152
|
+
# @see https://redis.io/commands/auth AUTH command
|
153
|
+
def auth(*args)
|
132
154
|
synchronize do |client|
|
133
|
-
client.call([:auth,
|
155
|
+
client.call([:auth, *args])
|
134
156
|
end
|
135
157
|
end
|
136
158
|
|
@@ -205,7 +227,7 @@ class Redis
|
|
205
227
|
def config(action, *args)
|
206
228
|
synchronize do |client|
|
207
229
|
client.call([:config, action] + args) do |reply|
|
208
|
-
if reply.
|
230
|
+
if reply.is_a?(Array) && action == :get
|
209
231
|
Hashify.call(reply)
|
210
232
|
else
|
211
233
|
reply
|
@@ -256,7 +278,7 @@ class Redis
|
|
256
278
|
def flushall(options = nil)
|
257
279
|
synchronize do |client|
|
258
280
|
if options && options[:async]
|
259
|
-
client.call([
|
281
|
+
client.call(%i[flushall async])
|
260
282
|
else
|
261
283
|
client.call([:flushall])
|
262
284
|
end
|
@@ -271,7 +293,7 @@ class Redis
|
|
271
293
|
def flushdb(options = nil)
|
272
294
|
synchronize do |client|
|
273
295
|
if options && options[:async]
|
274
|
-
client.call([
|
296
|
+
client.call(%i[flushdb async])
|
275
297
|
else
|
276
298
|
client.call([:flushdb])
|
277
299
|
end
|
@@ -285,7 +307,7 @@ class Redis
|
|
285
307
|
def info(cmd = nil)
|
286
308
|
synchronize do |client|
|
287
309
|
client.call([:info, cmd].compact) do |reply|
|
288
|
-
if reply.
|
310
|
+
if reply.is_a?(String)
|
289
311
|
reply = HashifyInfo.call(reply)
|
290
312
|
|
291
313
|
if cmd && cmd.to_s == "commandstats"
|
@@ -358,7 +380,7 @@ class Redis
|
|
358
380
|
# @param [String] subcommand e.g. `get`, `len`, `reset`
|
359
381
|
# @param [Integer] length maximum number of entries to return
|
360
382
|
# @return [Array<String>, Integer, String] depends on subcommand
|
361
|
-
def slowlog(subcommand, length=nil)
|
383
|
+
def slowlog(subcommand, length = nil)
|
362
384
|
synchronize do |client|
|
363
385
|
args = [:slowlog, subcommand]
|
364
386
|
args << length if length
|
@@ -383,7 +405,7 @@ class Redis
|
|
383
405
|
def time
|
384
406
|
synchronize do |client|
|
385
407
|
client.call([:time]) do |reply|
|
386
|
-
reply
|
408
|
+
reply&.map(&:to_i)
|
387
409
|
end
|
388
410
|
end
|
389
411
|
end
|
@@ -496,9 +518,9 @@ class Redis
|
|
496
518
|
# - `:replace => Boolean`: if false, raises an error if key already exists
|
497
519
|
# @raise [Redis::CommandError]
|
498
520
|
# @return [String] `"OK"`
|
499
|
-
def restore(key, ttl, serialized_value,
|
521
|
+
def restore(key, ttl, serialized_value, replace: nil)
|
500
522
|
args = [:restore, key, ttl, serialized_value]
|
501
|
-
args << 'REPLACE' if
|
523
|
+
args << 'REPLACE' if replace
|
502
524
|
|
503
525
|
synchronize do |client|
|
504
526
|
client.call(args)
|
@@ -535,6 +557,9 @@ class Redis
|
|
535
557
|
# @param [String, Array<String>] keys
|
536
558
|
# @return [Integer] number of keys that were deleted
|
537
559
|
def del(*keys)
|
560
|
+
keys.flatten!(1)
|
561
|
+
return 0 if keys.empty?
|
562
|
+
|
538
563
|
synchronize do |client|
|
539
564
|
client.call([:del] + keys)
|
540
565
|
end
|
@@ -550,13 +575,43 @@ class Redis
|
|
550
575
|
end
|
551
576
|
end
|
552
577
|
|
553
|
-
# Determine
|
578
|
+
# Determine how many of the keys exists.
|
554
579
|
#
|
555
|
-
# @param [String]
|
580
|
+
# @param [String, Array<String>] keys
|
581
|
+
# @return [Integer]
|
582
|
+
def exists(*keys)
|
583
|
+
if !Redis.exists_returns_integer && keys.size == 1
|
584
|
+
if Redis.exists_returns_integer.nil?
|
585
|
+
message = "`Redis#exists(key)` will return an Integer in redis-rb 4.3. `exists?` returns a boolean, you " \
|
586
|
+
"should use it instead. To opt-in to the new behavior now you can set Redis.exists_returns_integer = " \
|
587
|
+
"true. To disable this message and keep the current (boolean) behaviour of 'exists' you can set " \
|
588
|
+
"`Redis.exists_returns_integer = false`, but this option will be removed in 5.0. " \
|
589
|
+
"(#{::Kernel.caller(1, 1).first})\n"
|
590
|
+
|
591
|
+
::Kernel.warn(message)
|
592
|
+
end
|
593
|
+
|
594
|
+
exists?(*keys)
|
595
|
+
else
|
596
|
+
_exists(*keys)
|
597
|
+
end
|
598
|
+
end
|
599
|
+
|
600
|
+
def _exists(*keys)
|
601
|
+
synchronize do |client|
|
602
|
+
client.call([:exists, *keys])
|
603
|
+
end
|
604
|
+
end
|
605
|
+
|
606
|
+
# Determine if any of the keys exists.
|
607
|
+
#
|
608
|
+
# @param [String, Array<String>] keys
|
556
609
|
# @return [Boolean]
|
557
|
-
def exists(
|
610
|
+
def exists?(*keys)
|
558
611
|
synchronize do |client|
|
559
|
-
client.call([:exists,
|
612
|
+
client.call([:exists, *keys]) do |value|
|
613
|
+
value > 0
|
614
|
+
end
|
560
615
|
end
|
561
616
|
end
|
562
617
|
|
@@ -567,7 +622,7 @@ class Redis
|
|
567
622
|
def keys(pattern = "*")
|
568
623
|
synchronize do |client|
|
569
624
|
client.call([:keys, pattern]) do |reply|
|
570
|
-
if reply.
|
625
|
+
if reply.is_a?(String)
|
571
626
|
reply.split(" ")
|
572
627
|
else
|
573
628
|
reply
|
@@ -663,30 +718,27 @@ class Redis
|
|
663
718
|
# elements where every element is an array with the result for every
|
664
719
|
# element specified in `:get`
|
665
720
|
# - when `:store` is specified, the number of elements in the stored result
|
666
|
-
def sort(key,
|
667
|
-
args = []
|
668
|
-
|
669
|
-
by = options[:by]
|
670
|
-
args.concat(["BY", by]) if by
|
721
|
+
def sort(key, by: nil, limit: nil, get: nil, order: nil, store: nil)
|
722
|
+
args = [:sort, key]
|
723
|
+
args << "BY" << by if by
|
671
724
|
|
672
|
-
|
673
|
-
|
725
|
+
if limit
|
726
|
+
args << "LIMIT"
|
727
|
+
args.concat(limit)
|
728
|
+
end
|
674
729
|
|
675
|
-
get = Array(
|
676
|
-
|
730
|
+
get = Array(get)
|
731
|
+
get.each do |item|
|
732
|
+
args << "GET" << item
|
733
|
+
end
|
677
734
|
|
678
|
-
order = options[:order]
|
679
735
|
args.concat(order.split(" ")) if order
|
680
|
-
|
681
|
-
store = options[:store]
|
682
|
-
args.concat(["STORE", store]) if store
|
736
|
+
args << "STORE" << store if store
|
683
737
|
|
684
738
|
synchronize do |client|
|
685
|
-
client.call(
|
739
|
+
client.call(args) do |reply|
|
686
740
|
if get.size > 1 && !store
|
687
|
-
if reply
|
688
|
-
reply.each_slice(get.size).to_a
|
689
|
-
end
|
741
|
+
reply.each_slice(get.size).to_a if reply
|
690
742
|
else
|
691
743
|
reply
|
692
744
|
end
|
@@ -784,29 +836,29 @@ class Redis
|
|
784
836
|
# @param [Hash] options
|
785
837
|
# - `:ex => Integer`: Set the specified expire time, in seconds.
|
786
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.
|
787
841
|
# - `:nx => true`: Only set the key if it does not already exist.
|
788
842
|
# - `:xx => true`: Only set the key if it already exist.
|
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.
|
789
845
|
# @return [String, Boolean] `"OK"` or true, false if `:nx => true` or `:xx => true`
|
790
|
-
def set(key, value,
|
791
|
-
args = []
|
792
|
-
|
793
|
-
|
794
|
-
args
|
795
|
-
|
796
|
-
|
797
|
-
args
|
798
|
-
|
799
|
-
|
800
|
-
args.concat(["NX"]) if nx
|
801
|
-
|
802
|
-
xx = options[:xx]
|
803
|
-
args.concat(["XX"]) if xx
|
846
|
+
def set(key, value, ex: nil, px: nil, exat: nil, pxat: nil, nx: nil, xx: nil, keepttl: nil, get: nil)
|
847
|
+
args = [:set, key, value.to_s]
|
848
|
+
args << "EX" << ex if ex
|
849
|
+
args << "PX" << px if px
|
850
|
+
args << "EXAT" << exat if exat
|
851
|
+
args << "PXAT" << pxat if pxat
|
852
|
+
args << "NX" if nx
|
853
|
+
args << "XX" if xx
|
854
|
+
args << "KEEPTTL" if keepttl
|
855
|
+
args << "GET" if get
|
804
856
|
|
805
857
|
synchronize do |client|
|
806
858
|
if nx || xx
|
807
|
-
client.call(
|
859
|
+
client.call(args, &BoolifySet)
|
808
860
|
else
|
809
|
-
client.call(
|
861
|
+
client.call(args)
|
810
862
|
end
|
811
863
|
end
|
812
864
|
end
|
@@ -888,7 +940,7 @@ class Redis
|
|
888
940
|
# @see #mapped_msetnx
|
889
941
|
def msetnx(*args)
|
890
942
|
synchronize do |client|
|
891
|
-
client.call([:msetnx
|
943
|
+
client.call([:msetnx, *args], &Boolify)
|
892
944
|
end
|
893
945
|
end
|
894
946
|
|
@@ -928,7 +980,7 @@ class Redis
|
|
928
980
|
# @see #mapped_mget
|
929
981
|
def mget(*keys, &blk)
|
930
982
|
synchronize do |client|
|
931
|
-
client.call([:mget
|
983
|
+
client.call([:mget, *keys], &blk)
|
932
984
|
end
|
933
985
|
end
|
934
986
|
|
@@ -944,7 +996,7 @@ class Redis
|
|
944
996
|
# @see #mget
|
945
997
|
def mapped_mget(*keys)
|
946
998
|
mget(*keys) do |reply|
|
947
|
-
if reply.
|
999
|
+
if reply.is_a?(Array)
|
948
1000
|
Hash[keys.zip(reply)]
|
949
1001
|
else
|
950
1002
|
reply
|
@@ -1031,7 +1083,7 @@ class Redis
|
|
1031
1083
|
# @return [Integer] the length of the string stored in `destkey`
|
1032
1084
|
def bitop(operation, destkey, *keys)
|
1033
1085
|
synchronize do |client|
|
1034
|
-
client.call([:bitop, operation, destkey
|
1086
|
+
client.call([:bitop, operation, destkey, *keys])
|
1035
1087
|
end
|
1036
1088
|
end
|
1037
1089
|
|
@@ -1043,10 +1095,8 @@ class Redis
|
|
1043
1095
|
# @param [Integer] stop stop index
|
1044
1096
|
# @return [Integer] the position of the first 1/0 bit.
|
1045
1097
|
# -1 if looking for 1 and it is not found or start and stop are given.
|
1046
|
-
def bitpos(key, bit, start=nil, stop=nil)
|
1047
|
-
|
1048
|
-
raise(ArgumentError, 'stop parameter specified without start parameter')
|
1049
|
-
end
|
1098
|
+
def bitpos(key, bit, start = nil, stop = nil)
|
1099
|
+
raise(ArgumentError, 'stop parameter specified without start parameter') if stop && !start
|
1050
1100
|
|
1051
1101
|
synchronize do |client|
|
1052
1102
|
command = [:bitpos, key, bit]
|
@@ -1068,6 +1118,45 @@ class Redis
|
|
1068
1118
|
end
|
1069
1119
|
end
|
1070
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
|
+
|
1071
1160
|
# Get the length of the value stored in a key.
|
1072
1161
|
#
|
1073
1162
|
# @param [String] key
|
@@ -1089,6 +1178,59 @@ class Redis
|
|
1089
1178
|
end
|
1090
1179
|
end
|
1091
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
|
+
|
1092
1234
|
# Prepend one or more values to a list, creating the list if it doesn't exist
|
1093
1235
|
#
|
1094
1236
|
# @param [String] key
|
@@ -1133,23 +1275,29 @@ class Redis
|
|
1133
1275
|
end
|
1134
1276
|
end
|
1135
1277
|
|
1136
|
-
# Remove and get the first
|
1278
|
+
# Remove and get the first elements in a list.
|
1137
1279
|
#
|
1138
1280
|
# @param [String] key
|
1139
|
-
# @
|
1140
|
-
|
1281
|
+
# @param [Integer] count number of elements to remove
|
1282
|
+
# @return [String, Array<String>] the values of the first elements
|
1283
|
+
def lpop(key, count = nil)
|
1141
1284
|
synchronize do |client|
|
1142
|
-
|
1285
|
+
command = [:lpop, key]
|
1286
|
+
command << count if count
|
1287
|
+
client.call(command)
|
1143
1288
|
end
|
1144
1289
|
end
|
1145
1290
|
|
1146
|
-
# Remove and get the last
|
1291
|
+
# Remove and get the last elements in a list.
|
1147
1292
|
#
|
1148
1293
|
# @param [String] key
|
1149
|
-
# @
|
1150
|
-
|
1294
|
+
# @param [Integer] count number of elements to remove
|
1295
|
+
# @return [String, Array<String>] the values of the last elements
|
1296
|
+
def rpop(key, count = nil)
|
1151
1297
|
synchronize do |client|
|
1152
|
-
|
1298
|
+
command = [:rpop, key]
|
1299
|
+
command << count if count
|
1300
|
+
client.call(command)
|
1153
1301
|
end
|
1154
1302
|
end
|
1155
1303
|
|
@@ -1240,15 +1388,7 @@ class Redis
|
|
1240
1388
|
# @return [nil, String]
|
1241
1389
|
# - `nil` when the operation timed out
|
1242
1390
|
# - the element was popped and pushed otherwise
|
1243
|
-
def brpoplpush(source, destination,
|
1244
|
-
case options
|
1245
|
-
when Integer
|
1246
|
-
# Issue deprecation notice in obnoxious mode...
|
1247
|
-
options = { :timeout => options }
|
1248
|
-
end
|
1249
|
-
|
1250
|
-
timeout = options[:timeout] || 0
|
1251
|
-
|
1391
|
+
def brpoplpush(source, destination, deprecated_timeout = 0, timeout: deprecated_timeout)
|
1252
1392
|
synchronize do |client|
|
1253
1393
|
command = [:brpoplpush, source, destination, timeout]
|
1254
1394
|
timeout += client.timeout if timeout > 0
|
@@ -1439,6 +1579,19 @@ class Redis
|
|
1439
1579
|
end
|
1440
1580
|
end
|
1441
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
|
+
|
1442
1595
|
# Get all the members in a set.
|
1443
1596
|
#
|
1444
1597
|
# @param [String] key
|
@@ -1455,7 +1608,7 @@ class Redis
|
|
1455
1608
|
# @return [Array<String>] members in the difference
|
1456
1609
|
def sdiff(*keys)
|
1457
1610
|
synchronize do |client|
|
1458
|
-
client.call([:sdiff
|
1611
|
+
client.call([:sdiff, *keys])
|
1459
1612
|
end
|
1460
1613
|
end
|
1461
1614
|
|
@@ -1466,7 +1619,7 @@ class Redis
|
|
1466
1619
|
# @return [Integer] number of elements in the resulting set
|
1467
1620
|
def sdiffstore(destination, *keys)
|
1468
1621
|
synchronize do |client|
|
1469
|
-
client.call([:sdiffstore, destination
|
1622
|
+
client.call([:sdiffstore, destination, *keys])
|
1470
1623
|
end
|
1471
1624
|
end
|
1472
1625
|
|
@@ -1476,7 +1629,7 @@ class Redis
|
|
1476
1629
|
# @return [Array<String>] members in the intersection
|
1477
1630
|
def sinter(*keys)
|
1478
1631
|
synchronize do |client|
|
1479
|
-
client.call([:sinter
|
1632
|
+
client.call([:sinter, *keys])
|
1480
1633
|
end
|
1481
1634
|
end
|
1482
1635
|
|
@@ -1487,7 +1640,7 @@ class Redis
|
|
1487
1640
|
# @return [Integer] number of elements in the resulting set
|
1488
1641
|
def sinterstore(destination, *keys)
|
1489
1642
|
synchronize do |client|
|
1490
|
-
client.call([:sinterstore, destination
|
1643
|
+
client.call([:sinterstore, destination, *keys])
|
1491
1644
|
end
|
1492
1645
|
end
|
1493
1646
|
|
@@ -1497,7 +1650,7 @@ class Redis
|
|
1497
1650
|
# @return [Array<String>] members in the union
|
1498
1651
|
def sunion(*keys)
|
1499
1652
|
synchronize do |client|
|
1500
|
-
client.call([:sunion
|
1653
|
+
client.call([:sunion, *keys])
|
1501
1654
|
end
|
1502
1655
|
end
|
1503
1656
|
|
@@ -1508,7 +1661,7 @@ class Redis
|
|
1508
1661
|
# @return [Integer] number of elements in the resulting set
|
1509
1662
|
def sunionstore(destination, *keys)
|
1510
1663
|
synchronize do |client|
|
1511
|
-
client.call([:sunionstore, destination
|
1664
|
+
client.call([:sunionstore, destination, *keys])
|
1512
1665
|
end
|
1513
1666
|
end
|
1514
1667
|
|
@@ -1543,6 +1696,10 @@ class Redis
|
|
1543
1696
|
# add elements)
|
1544
1697
|
# - `:nx => true`: Don't update already existing elements (always
|
1545
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
|
1546
1703
|
# - `:ch => true`: Modify the return value from the number of new
|
1547
1704
|
# elements added, to the total number of elements changed (CH is an
|
1548
1705
|
# abbreviation of changed); changed elements are new elements added
|
@@ -1557,31 +1714,22 @@ class Redis
|
|
1557
1714
|
# pairs that were **added** to the sorted set.
|
1558
1715
|
# - `Float` when option :incr is specified, holding the score of the member
|
1559
1716
|
# after incrementing it.
|
1560
|
-
def zadd(key, *args
|
1561
|
-
|
1562
|
-
if
|
1563
|
-
|
1564
|
-
|
1565
|
-
|
1566
|
-
|
1567
|
-
|
1568
|
-
xx = options[:xx]
|
1569
|
-
zadd_options << "XX" if xx
|
1570
|
-
|
1571
|
-
ch = options[:ch]
|
1572
|
-
zadd_options << "CH" if ch
|
1573
|
-
|
1574
|
-
incr = options[:incr]
|
1575
|
-
zadd_options << "INCR" if incr
|
1576
|
-
end
|
1717
|
+
def zadd(key, *args, nx: nil, xx: nil, lt: nil, gt: nil, ch: nil, incr: nil)
|
1718
|
+
command = [:zadd, key]
|
1719
|
+
command << "NX" if nx
|
1720
|
+
command << "XX" if xx
|
1721
|
+
command << "LT" if lt
|
1722
|
+
command << "GT" if gt
|
1723
|
+
command << "CH" if ch
|
1724
|
+
command << "INCR" if incr
|
1577
1725
|
|
1578
1726
|
synchronize do |client|
|
1579
1727
|
if args.size == 1 && args[0].is_a?(Array)
|
1580
1728
|
# Variadic: return float if INCR, integer if !INCR
|
1581
|
-
client.call(
|
1729
|
+
client.call(command + args[0], &(incr ? Floatify : nil))
|
1582
1730
|
elsif args.size == 2
|
1583
1731
|
# Single pair: return float if INCR, boolean if !INCR
|
1584
|
-
client.call(
|
1732
|
+
client.call(command + args, &(incr ? Floatify : Boolify))
|
1585
1733
|
else
|
1586
1734
|
raise ArgumentError, "wrong number of arguments"
|
1587
1735
|
end
|
@@ -1734,6 +1882,63 @@ class Redis
|
|
1734
1882
|
end
|
1735
1883
|
end
|
1736
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
|
+
|
1737
1942
|
# Return a range of members in a sorted set, by index.
|
1738
1943
|
#
|
1739
1944
|
# @example Retrieve all members from a sorted set
|
@@ -1752,10 +1957,8 @@ class Redis
|
|
1752
1957
|
# @return [Array<String>, Array<[String, Float]>]
|
1753
1958
|
# - when `:with_scores` is not specified, an array of members
|
1754
1959
|
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
1755
|
-
def zrange(key, start, stop,
|
1756
|
-
args = []
|
1757
|
-
|
1758
|
-
with_scores = options[:with_scores] || options[:withscores]
|
1960
|
+
def zrange(key, start, stop, withscores: false, with_scores: withscores)
|
1961
|
+
args = [:zrange, key, start, stop]
|
1759
1962
|
|
1760
1963
|
if with_scores
|
1761
1964
|
args << "WITHSCORES"
|
@@ -1763,7 +1966,7 @@ class Redis
|
|
1763
1966
|
end
|
1764
1967
|
|
1765
1968
|
synchronize do |client|
|
1766
|
-
client.call(
|
1969
|
+
client.call(args, &block)
|
1767
1970
|
end
|
1768
1971
|
end
|
1769
1972
|
|
@@ -1778,10 +1981,8 @@ class Redis
|
|
1778
1981
|
# # => [["b", 64.0], ["a", 32.0]]
|
1779
1982
|
#
|
1780
1983
|
# @see #zrange
|
1781
|
-
def zrevrange(key, start, stop,
|
1782
|
-
args = []
|
1783
|
-
|
1784
|
-
with_scores = options[:with_scores] || options[:withscores]
|
1984
|
+
def zrevrange(key, start, stop, withscores: false, with_scores: withscores)
|
1985
|
+
args = [:zrevrange, key, start, stop]
|
1785
1986
|
|
1786
1987
|
if with_scores
|
1787
1988
|
args << "WITHSCORES"
|
@@ -1789,7 +1990,7 @@ class Redis
|
|
1789
1990
|
end
|
1790
1991
|
|
1791
1992
|
synchronize do |client|
|
1792
|
-
client.call(
|
1993
|
+
client.call(args, &block)
|
1793
1994
|
end
|
1794
1995
|
end
|
1795
1996
|
|
@@ -1880,14 +2081,16 @@ class Redis
|
|
1880
2081
|
# `count` members
|
1881
2082
|
#
|
1882
2083
|
# @return [Array<String>, Array<[String, Float]>]
|
1883
|
-
def zrangebylex(key, min, max,
|
1884
|
-
args = []
|
2084
|
+
def zrangebylex(key, min, max, limit: nil)
|
2085
|
+
args = [:zrangebylex, key, min, max]
|
1885
2086
|
|
1886
|
-
|
1887
|
-
|
2087
|
+
if limit
|
2088
|
+
args << "LIMIT"
|
2089
|
+
args.concat(limit)
|
2090
|
+
end
|
1888
2091
|
|
1889
2092
|
synchronize do |client|
|
1890
|
-
client.call(
|
2093
|
+
client.call(args)
|
1891
2094
|
end
|
1892
2095
|
end
|
1893
2096
|
|
@@ -1902,14 +2105,16 @@ class Redis
|
|
1902
2105
|
# # => ["abbygail", "abby"]
|
1903
2106
|
#
|
1904
2107
|
# @see #zrangebylex
|
1905
|
-
def zrevrangebylex(key, max, min,
|
1906
|
-
args = []
|
2108
|
+
def zrevrangebylex(key, max, min, limit: nil)
|
2109
|
+
args = [:zrevrangebylex, key, max, min]
|
1907
2110
|
|
1908
|
-
|
1909
|
-
|
2111
|
+
if limit
|
2112
|
+
args << "LIMIT"
|
2113
|
+
args.concat(limit)
|
2114
|
+
end
|
1910
2115
|
|
1911
2116
|
synchronize do |client|
|
1912
|
-
client.call(
|
2117
|
+
client.call(args)
|
1913
2118
|
end
|
1914
2119
|
end
|
1915
2120
|
|
@@ -1940,21 +2145,21 @@ class Redis
|
|
1940
2145
|
# @return [Array<String>, Array<[String, Float]>]
|
1941
2146
|
# - when `:with_scores` is not specified, an array of members
|
1942
2147
|
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
1943
|
-
def zrangebyscore(key, min, max,
|
1944
|
-
args = []
|
1945
|
-
|
1946
|
-
with_scores = options[:with_scores] || options[:withscores]
|
2148
|
+
def zrangebyscore(key, min, max, withscores: false, with_scores: withscores, limit: nil)
|
2149
|
+
args = [:zrangebyscore, key, min, max]
|
1947
2150
|
|
1948
2151
|
if with_scores
|
1949
2152
|
args << "WITHSCORES"
|
1950
2153
|
block = FloatifyPairs
|
1951
2154
|
end
|
1952
2155
|
|
1953
|
-
|
1954
|
-
|
2156
|
+
if limit
|
2157
|
+
args << "LIMIT"
|
2158
|
+
args.concat(limit)
|
2159
|
+
end
|
1955
2160
|
|
1956
2161
|
synchronize do |client|
|
1957
|
-
client.call(
|
2162
|
+
client.call(args, &block)
|
1958
2163
|
end
|
1959
2164
|
end
|
1960
2165
|
|
@@ -1972,21 +2177,21 @@ class Redis
|
|
1972
2177
|
# # => [["b", 64.0], ["a", 32.0]]
|
1973
2178
|
#
|
1974
2179
|
# @see #zrangebyscore
|
1975
|
-
def zrevrangebyscore(key, max, min,
|
1976
|
-
args = []
|
1977
|
-
|
1978
|
-
with_scores = options[:with_scores] || options[:withscores]
|
2180
|
+
def zrevrangebyscore(key, max, min, withscores: false, with_scores: withscores, limit: nil)
|
2181
|
+
args = [:zrevrangebyscore, key, max, min]
|
1979
2182
|
|
1980
2183
|
if with_scores
|
1981
|
-
args <<
|
2184
|
+
args << "WITHSCORES"
|
1982
2185
|
block = FloatifyPairs
|
1983
2186
|
end
|
1984
2187
|
|
1985
|
-
|
1986
|
-
|
2188
|
+
if limit
|
2189
|
+
args << "LIMIT"
|
2190
|
+
args.concat(limit)
|
2191
|
+
end
|
1987
2192
|
|
1988
2193
|
synchronize do |client|
|
1989
|
-
client.call(
|
2194
|
+
client.call(args, &block)
|
1990
2195
|
end
|
1991
2196
|
end
|
1992
2197
|
|
@@ -2036,6 +2241,45 @@ class Redis
|
|
2036
2241
|
end
|
2037
2242
|
end
|
2038
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
|
+
|
2039
2283
|
# Intersect multiple sorted sets and store the resulting sorted set in a new
|
2040
2284
|
# key.
|
2041
2285
|
#
|
@@ -2050,17 +2294,18 @@ class Redis
|
|
2050
2294
|
# sorted sets
|
2051
2295
|
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
2052
2296
|
# @return [Integer] number of elements in the resulting sorted set
|
2053
|
-
def zinterstore(destination, keys,
|
2054
|
-
args = []
|
2297
|
+
def zinterstore(destination, keys, weights: nil, aggregate: nil)
|
2298
|
+
args = [:zinterstore, destination, keys.size, *keys]
|
2055
2299
|
|
2056
|
-
|
2057
|
-
|
2300
|
+
if weights
|
2301
|
+
args << "WEIGHTS"
|
2302
|
+
args.concat(weights)
|
2303
|
+
end
|
2058
2304
|
|
2059
|
-
aggregate
|
2060
|
-
args.concat(["AGGREGATE", aggregate]) if aggregate
|
2305
|
+
args << "AGGREGATE" << aggregate if aggregate
|
2061
2306
|
|
2062
2307
|
synchronize do |client|
|
2063
|
-
client.call(
|
2308
|
+
client.call(args)
|
2064
2309
|
end
|
2065
2310
|
end
|
2066
2311
|
|
@@ -2077,17 +2322,18 @@ class Redis
|
|
2077
2322
|
# sorted sets
|
2078
2323
|
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
2079
2324
|
# @return [Integer] number of elements in the resulting sorted set
|
2080
|
-
def zunionstore(destination, keys,
|
2081
|
-
args = []
|
2325
|
+
def zunionstore(destination, keys, weights: nil, aggregate: nil)
|
2326
|
+
args = [:zunionstore, destination, keys.size, *keys]
|
2082
2327
|
|
2083
|
-
|
2084
|
-
|
2328
|
+
if weights
|
2329
|
+
args << "WEIGHTS"
|
2330
|
+
args.concat(weights)
|
2331
|
+
end
|
2085
2332
|
|
2086
|
-
aggregate
|
2087
|
-
args.concat(["AGGREGATE", aggregate]) if aggregate
|
2333
|
+
args << "AGGREGATE" << aggregate if aggregate
|
2088
2334
|
|
2089
2335
|
synchronize do |client|
|
2090
|
-
client.call(
|
2336
|
+
client.call(args)
|
2091
2337
|
end
|
2092
2338
|
end
|
2093
2339
|
|
@@ -2101,15 +2347,20 @@ class Redis
|
|
2101
2347
|
end
|
2102
2348
|
end
|
2103
2349
|
|
2104
|
-
# Set
|
2350
|
+
# Set one or more hash values.
|
2351
|
+
#
|
2352
|
+
# @example
|
2353
|
+
# redis.hset("hash", "f1", "v1", "f2", "v2") # => 2
|
2354
|
+
# redis.hset("hash", { "f1" => "v1", "f2" => "v2" }) # => 2
|
2105
2355
|
#
|
2106
2356
|
# @param [String] key
|
2107
|
-
# @param [String]
|
2108
|
-
# @
|
2109
|
-
|
2110
|
-
|
2357
|
+
# @param [Array<String> | Hash<String, String>] attrs array or hash of fields and values
|
2358
|
+
# @return [Integer] The number of fields that were added to the hash
|
2359
|
+
def hset(key, *attrs)
|
2360
|
+
attrs = attrs.first.flatten if attrs.size == 1 && attrs.first.is_a?(Hash)
|
2361
|
+
|
2111
2362
|
synchronize do |client|
|
2112
|
-
client.call([:hset, key,
|
2363
|
+
client.call([:hset, key, *attrs])
|
2113
2364
|
end
|
2114
2365
|
end
|
2115
2366
|
|
@@ -2198,7 +2449,7 @@ class Redis
|
|
2198
2449
|
# @see #hmget
|
2199
2450
|
def mapped_hmget(key, *fields)
|
2200
2451
|
hmget(key, *fields) do |reply|
|
2201
|
-
if reply.
|
2452
|
+
if reply.is_a?(Array)
|
2202
2453
|
Hash[fields.zip(reply)]
|
2203
2454
|
else
|
2204
2455
|
reply
|
@@ -2291,20 +2542,21 @@ class Redis
|
|
2291
2542
|
|
2292
2543
|
def subscribed?
|
2293
2544
|
synchronize do |client|
|
2294
|
-
client.
|
2545
|
+
client.is_a? SubscribedClient
|
2295
2546
|
end
|
2296
2547
|
end
|
2297
2548
|
|
2298
2549
|
# Listen for messages published to the given channels.
|
2299
2550
|
def subscribe(*channels, &block)
|
2300
|
-
synchronize do |
|
2551
|
+
synchronize do |_client|
|
2301
2552
|
_subscription(:subscribe, 0, channels, block)
|
2302
2553
|
end
|
2303
2554
|
end
|
2304
2555
|
|
2305
|
-
# Listen for messages published to the given channels. Throw a timeout error
|
2556
|
+
# Listen for messages published to the given channels. Throw a timeout error
|
2557
|
+
# if there is no messages for a timeout period.
|
2306
2558
|
def subscribe_with_timeout(timeout, *channels, &block)
|
2307
|
-
synchronize do |
|
2559
|
+
synchronize do |_client|
|
2308
2560
|
_subscription(:subscribe_with_timeout, timeout, channels, block)
|
2309
2561
|
end
|
2310
2562
|
end
|
@@ -2312,21 +2564,23 @@ class Redis
|
|
2312
2564
|
# Stop listening for messages posted to the given channels.
|
2313
2565
|
def unsubscribe(*channels)
|
2314
2566
|
synchronize do |client|
|
2315
|
-
raise
|
2567
|
+
raise "Can't unsubscribe if not subscribed." unless subscribed?
|
2568
|
+
|
2316
2569
|
client.unsubscribe(*channels)
|
2317
2570
|
end
|
2318
2571
|
end
|
2319
2572
|
|
2320
2573
|
# Listen for messages published to channels matching the given patterns.
|
2321
2574
|
def psubscribe(*channels, &block)
|
2322
|
-
synchronize do |
|
2575
|
+
synchronize do |_client|
|
2323
2576
|
_subscription(:psubscribe, 0, channels, block)
|
2324
2577
|
end
|
2325
2578
|
end
|
2326
2579
|
|
2327
|
-
# Listen for messages published to channels matching the given patterns.
|
2580
|
+
# Listen for messages published to channels matching the given patterns.
|
2581
|
+
# Throw a timeout error if there is no messages for a timeout period.
|
2328
2582
|
def psubscribe_with_timeout(timeout, *channels, &block)
|
2329
|
-
synchronize do |
|
2583
|
+
synchronize do |_client|
|
2330
2584
|
_subscription(:psubscribe_with_timeout, timeout, channels, block)
|
2331
2585
|
end
|
2332
2586
|
end
|
@@ -2334,7 +2588,8 @@ class Redis
|
|
2334
2588
|
# Stop listening for messages posted to channels matching the given patterns.
|
2335
2589
|
def punsubscribe(*channels)
|
2336
2590
|
synchronize do |client|
|
2337
|
-
raise
|
2591
|
+
raise "Can't unsubscribe if not subscribed." unless subscribed?
|
2592
|
+
|
2338
2593
|
client.punsubscribe(*channels)
|
2339
2594
|
end
|
2340
2595
|
end
|
@@ -2379,7 +2634,7 @@ class Redis
|
|
2379
2634
|
# @see #multi
|
2380
2635
|
def watch(*keys)
|
2381
2636
|
synchronize do |client|
|
2382
|
-
res = client.call([:watch
|
2637
|
+
res = client.call([:watch, *keys])
|
2383
2638
|
|
2384
2639
|
if block_given?
|
2385
2640
|
begin
|
@@ -2409,14 +2664,13 @@ class Redis
|
|
2409
2664
|
end
|
2410
2665
|
|
2411
2666
|
def pipelined
|
2412
|
-
synchronize do |
|
2667
|
+
synchronize do |prior_client|
|
2413
2668
|
begin
|
2414
|
-
|
2415
|
-
original, @client = @client, pipeline
|
2669
|
+
@client = Pipeline.new(prior_client)
|
2416
2670
|
yield(self)
|
2417
|
-
|
2671
|
+
prior_client.call_pipeline(@client)
|
2418
2672
|
ensure
|
2419
|
-
@client =
|
2673
|
+
@client = prior_client
|
2420
2674
|
end
|
2421
2675
|
end
|
2422
2676
|
end
|
@@ -2452,17 +2706,16 @@ class Redis
|
|
2452
2706
|
# @see #watch
|
2453
2707
|
# @see #unwatch
|
2454
2708
|
def multi
|
2455
|
-
synchronize do |
|
2709
|
+
synchronize do |prior_client|
|
2456
2710
|
if !block_given?
|
2457
|
-
|
2711
|
+
prior_client.call([:multi])
|
2458
2712
|
else
|
2459
2713
|
begin
|
2460
|
-
|
2461
|
-
original, @client = @client, pipeline
|
2714
|
+
@client = Pipeline::Multi.new(prior_client)
|
2462
2715
|
yield(self)
|
2463
|
-
|
2716
|
+
prior_client.call_pipeline(@client)
|
2464
2717
|
ensure
|
2465
|
-
@client =
|
2718
|
+
@client = prior_client
|
2466
2719
|
end
|
2467
2720
|
end
|
2468
2721
|
end
|
@@ -2609,18 +2862,13 @@ class Redis
|
|
2609
2862
|
_eval(:evalsha, args)
|
2610
2863
|
end
|
2611
2864
|
|
2612
|
-
def _scan(command, cursor, args,
|
2865
|
+
def _scan(command, cursor, args, match: nil, count: nil, type: nil, &block)
|
2613
2866
|
# SSCAN/ZSCAN/HSCAN already prepend the key to +args+.
|
2614
2867
|
|
2615
2868
|
args << cursor
|
2616
|
-
|
2617
|
-
|
2618
|
-
|
2619
|
-
end
|
2620
|
-
|
2621
|
-
if count = options[:count]
|
2622
|
-
args.concat(["COUNT", count])
|
2623
|
-
end
|
2869
|
+
args << "MATCH" << match if match
|
2870
|
+
args << "COUNT" << count if count
|
2871
|
+
args << "TYPE" << type if type
|
2624
2872
|
|
2625
2873
|
synchronize do |client|
|
2626
2874
|
client.call([command] + args, &block)
|
@@ -2635,15 +2883,19 @@ class Redis
|
|
2635
2883
|
# @example Retrieve a batch of keys matching a pattern
|
2636
2884
|
# redis.scan(4, :match => "key:1?")
|
2637
2885
|
# # => ["92", ["key:13", "key:18"]]
|
2886
|
+
# @example Retrieve a batch of keys of a certain type
|
2887
|
+
# redis.scan(92, :type => "zset")
|
2888
|
+
# # => ["173", ["sortedset:14", "sortedset:78"]]
|
2638
2889
|
#
|
2639
2890
|
# @param [String, Integer] cursor the cursor of the iteration
|
2640
2891
|
# @param [Hash] options
|
2641
2892
|
# - `:match => String`: only return keys matching the pattern
|
2642
2893
|
# - `:count => Integer`: return count keys at most per iteration
|
2894
|
+
# - `:type => String`: return keys only of the given type
|
2643
2895
|
#
|
2644
2896
|
# @return [String, Array<String>] the next cursor and all found keys
|
2645
|
-
def scan(cursor, options
|
2646
|
-
_scan(:scan, cursor, [], options)
|
2897
|
+
def scan(cursor, **options)
|
2898
|
+
_scan(:scan, cursor, [], **options)
|
2647
2899
|
end
|
2648
2900
|
|
2649
2901
|
# Scan the keyspace
|
@@ -2655,17 +2907,23 @@ class Redis
|
|
2655
2907
|
# redis.scan_each(:match => "key:1?") {|key| puts key}
|
2656
2908
|
# # => key:13
|
2657
2909
|
# # => key:18
|
2910
|
+
# @example Execute block for each key of a type
|
2911
|
+
# redis.scan_each(:type => "hash") {|key| puts redis.type(key)}
|
2912
|
+
# # => "hash"
|
2913
|
+
# # => "hash"
|
2658
2914
|
#
|
2659
2915
|
# @param [Hash] options
|
2660
2916
|
# - `:match => String`: only return keys matching the pattern
|
2661
2917
|
# - `:count => Integer`: return count keys at most per iteration
|
2918
|
+
# - `:type => String`: return keys only of the given type
|
2662
2919
|
#
|
2663
2920
|
# @return [Enumerator] an enumerator for all found keys
|
2664
|
-
def scan_each(options
|
2665
|
-
return to_enum(:scan_each, options) unless block_given?
|
2921
|
+
def scan_each(**options, &block)
|
2922
|
+
return to_enum(:scan_each, **options) unless block_given?
|
2923
|
+
|
2666
2924
|
cursor = 0
|
2667
2925
|
loop do
|
2668
|
-
cursor, keys = scan(cursor, options)
|
2926
|
+
cursor, keys = scan(cursor, **options)
|
2669
2927
|
keys.each(&block)
|
2670
2928
|
break if cursor == "0"
|
2671
2929
|
end
|
@@ -2682,8 +2940,8 @@ class Redis
|
|
2682
2940
|
# - `:count => Integer`: return count keys at most per iteration
|
2683
2941
|
#
|
2684
2942
|
# @return [String, Array<[String, String]>] the next cursor and all found keys
|
2685
|
-
def hscan(key, cursor, options
|
2686
|
-
_scan(:hscan, cursor, [key], options) do |reply|
|
2943
|
+
def hscan(key, cursor, **options)
|
2944
|
+
_scan(:hscan, cursor, [key], **options) do |reply|
|
2687
2945
|
[reply[0], reply[1].each_slice(2).to_a]
|
2688
2946
|
end
|
2689
2947
|
end
|
@@ -2699,11 +2957,12 @@ class Redis
|
|
2699
2957
|
# - `:count => Integer`: return count keys at most per iteration
|
2700
2958
|
#
|
2701
2959
|
# @return [Enumerator] an enumerator for all found keys
|
2702
|
-
def hscan_each(key, options
|
2703
|
-
return to_enum(:hscan_each, key, options) unless block_given?
|
2960
|
+
def hscan_each(key, **options, &block)
|
2961
|
+
return to_enum(:hscan_each, key, **options) unless block_given?
|
2962
|
+
|
2704
2963
|
cursor = 0
|
2705
2964
|
loop do
|
2706
|
-
cursor, values = hscan(key, cursor, options)
|
2965
|
+
cursor, values = hscan(key, cursor, **options)
|
2707
2966
|
values.each(&block)
|
2708
2967
|
break if cursor == "0"
|
2709
2968
|
end
|
@@ -2721,8 +2980,8 @@ class Redis
|
|
2721
2980
|
#
|
2722
2981
|
# @return [String, Array<[String, Float]>] the next cursor and all found
|
2723
2982
|
# members and scores
|
2724
|
-
def zscan(key, cursor, options
|
2725
|
-
_scan(:zscan, cursor, [key], options) do |reply|
|
2983
|
+
def zscan(key, cursor, **options)
|
2984
|
+
_scan(:zscan, cursor, [key], **options) do |reply|
|
2726
2985
|
[reply[0], FloatifyPairs.call(reply[1])]
|
2727
2986
|
end
|
2728
2987
|
end
|
@@ -2738,11 +2997,12 @@ class Redis
|
|
2738
2997
|
# - `:count => Integer`: return count keys at most per iteration
|
2739
2998
|
#
|
2740
2999
|
# @return [Enumerator] an enumerator for all found scores and members
|
2741
|
-
def zscan_each(key, options
|
2742
|
-
return to_enum(:zscan_each, key, options) unless block_given?
|
3000
|
+
def zscan_each(key, **options, &block)
|
3001
|
+
return to_enum(:zscan_each, key, **options) unless block_given?
|
3002
|
+
|
2743
3003
|
cursor = 0
|
2744
3004
|
loop do
|
2745
|
-
cursor, values = zscan(key, cursor, options)
|
3005
|
+
cursor, values = zscan(key, cursor, **options)
|
2746
3006
|
values.each(&block)
|
2747
3007
|
break if cursor == "0"
|
2748
3008
|
end
|
@@ -2759,8 +3019,8 @@ class Redis
|
|
2759
3019
|
# - `:count => Integer`: return count keys at most per iteration
|
2760
3020
|
#
|
2761
3021
|
# @return [String, Array<String>] the next cursor and all found members
|
2762
|
-
def sscan(key, cursor, options
|
2763
|
-
_scan(:sscan, cursor, [key], options)
|
3022
|
+
def sscan(key, cursor, **options)
|
3023
|
+
_scan(:sscan, cursor, [key], **options)
|
2764
3024
|
end
|
2765
3025
|
|
2766
3026
|
# Scan a set
|
@@ -2774,11 +3034,12 @@ class Redis
|
|
2774
3034
|
# - `:count => Integer`: return count keys at most per iteration
|
2775
3035
|
#
|
2776
3036
|
# @return [Enumerator] an enumerator for all keys in the set
|
2777
|
-
def sscan_each(key, options
|
2778
|
-
return to_enum(:sscan_each, key, options) unless block_given?
|
3037
|
+
def sscan_each(key, **options, &block)
|
3038
|
+
return to_enum(:sscan_each, key, **options) unless block_given?
|
3039
|
+
|
2779
3040
|
cursor = 0
|
2780
3041
|
loop do
|
2781
|
-
cursor, keys = sscan(key, cursor, options)
|
3042
|
+
cursor, keys = sscan(key, cursor, **options)
|
2782
3043
|
keys.each(&block)
|
2783
3044
|
break if cursor == "0"
|
2784
3045
|
end
|
@@ -2842,12 +3103,12 @@ class Redis
|
|
2842
3103
|
end
|
2843
3104
|
end
|
2844
3105
|
|
2845
|
-
|
2846
3106
|
# Query a sorted set representing a geospatial index to fetch members matching a
|
2847
3107
|
# given maximum distance from a point
|
2848
3108
|
#
|
2849
3109
|
# @param [Array] args key, longitude, latitude, radius, unit(m|km|ft|mi)
|
2850
|
-
# @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest
|
3110
|
+
# @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest
|
3111
|
+
# or the farthest to the nearest relative to the center
|
2851
3112
|
# @param [Integer] count limit the results to the first N matching items
|
2852
3113
|
# @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
|
2853
3114
|
# @return [Array<String>] may be changed with `options`
|
@@ -2864,7 +3125,8 @@ class Redis
|
|
2864
3125
|
# given maximum distance from an already existing member
|
2865
3126
|
#
|
2866
3127
|
# @param [Array] args key, member, radius, unit(m|km|ft|mi)
|
2867
|
-
# @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest or the farthest
|
3128
|
+
# @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest or the farthest
|
3129
|
+
# to the nearest relative to the center
|
2868
3130
|
# @param [Integer] count limit the results to the first N matching items
|
2869
3131
|
# @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
|
2870
3132
|
# @return [Array<String>] may be changed with `options`
|
@@ -2881,7 +3143,8 @@ class Redis
|
|
2881
3143
|
#
|
2882
3144
|
# @param [String] key
|
2883
3145
|
# @param [String, Array<String>] member one member or array of members
|
2884
|
-
# @return [Array<Array<String>, nil>] returns array of elements, where each
|
3146
|
+
# @return [Array<Array<String>, nil>] returns array of elements, where each
|
3147
|
+
# element is either array of longitude and latitude or nil
|
2885
3148
|
def geopos(key, member)
|
2886
3149
|
synchronize do |client|
|
2887
3150
|
client.call([:geopos, key, member])
|
@@ -2945,10 +3208,14 @@ class Redis
|
|
2945
3208
|
# @option opts [Boolean] :approximate whether to add `~` modifier of maxlen or not
|
2946
3209
|
#
|
2947
3210
|
# @return [String] the entry id
|
2948
|
-
def xadd(key, entry,
|
3211
|
+
def xadd(key, entry, approximate: nil, maxlen: nil, id: '*')
|
2949
3212
|
args = [:xadd, key]
|
2950
|
-
|
2951
|
-
|
3213
|
+
if maxlen
|
3214
|
+
args << "MAXLEN"
|
3215
|
+
args << "~" if approximate
|
3216
|
+
args << maxlen
|
3217
|
+
end
|
3218
|
+
args << id
|
2952
3219
|
args.concat(entry.to_a.flatten)
|
2953
3220
|
synchronize { |client| client.call(args) }
|
2954
3221
|
end
|
@@ -3003,8 +3270,8 @@ class Redis
|
|
3003
3270
|
# @param count [Integer] the number of entries as limit
|
3004
3271
|
#
|
3005
3272
|
# @return [Array<Array<String, Hash>>] the ids and entries pairs
|
3006
|
-
def xrange(key, start = '-',
|
3007
|
-
args = [:xrange, key, start,
|
3273
|
+
def xrange(key, start = '-', range_end = '+', count: nil)
|
3274
|
+
args = [:xrange, key, start, range_end]
|
3008
3275
|
args.concat(['COUNT', count]) if count
|
3009
3276
|
synchronize { |client| client.call(args, &HashifyStreamEntries) }
|
3010
3277
|
end
|
@@ -3026,8 +3293,8 @@ class Redis
|
|
3026
3293
|
# @params count [Integer] the number of entries as limit
|
3027
3294
|
#
|
3028
3295
|
# @return [Array<Array<String, Hash>>] the ids and entries pairs
|
3029
|
-
def xrevrange(key,
|
3030
|
-
args = [:xrevrange, key,
|
3296
|
+
def xrevrange(key, range_end = '+', start = '-', count: nil)
|
3297
|
+
args = [:xrevrange, key, range_end, start]
|
3031
3298
|
args.concat(['COUNT', count]) if count
|
3032
3299
|
synchronize { |client| client.call(args, &HashifyStreamEntries) }
|
3033
3300
|
end
|
@@ -3119,12 +3386,12 @@ class Redis
|
|
3119
3386
|
# @option opts [Boolean] :noack whether message loss is acceptable or not
|
3120
3387
|
#
|
3121
3388
|
# @return [Hash{String => Hash{String => Hash}}] the entries
|
3122
|
-
def xreadgroup(group, consumer, keys, ids,
|
3389
|
+
def xreadgroup(group, consumer, keys, ids, count: nil, block: nil, noack: nil)
|
3123
3390
|
args = [:xreadgroup, 'GROUP', group, consumer]
|
3124
|
-
args << 'COUNT' <<
|
3125
|
-
args << 'BLOCK' <<
|
3126
|
-
args << 'NOACK' if
|
3127
|
-
_xread(args, keys, ids,
|
3391
|
+
args << 'COUNT' << count if count
|
3392
|
+
args << 'BLOCK' << block.to_i if block
|
3393
|
+
args << 'NOACK' if noack
|
3394
|
+
_xread(args, keys, ids, block)
|
3128
3395
|
end
|
3129
3396
|
|
3130
3397
|
# Removes one or multiple entries from the pending entries list of a stream consumer group.
|
@@ -3189,6 +3456,38 @@ class Redis
|
|
3189
3456
|
synchronize { |client| client.call(args, &blk) }
|
3190
3457
|
end
|
3191
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
|
+
|
3192
3491
|
# Fetches not acknowledging pending entries
|
3193
3492
|
#
|
3194
3493
|
# @example With key and group
|
@@ -3234,8 +3533,8 @@ class Redis
|
|
3234
3533
|
when "get-master-addr-by-name"
|
3235
3534
|
reply
|
3236
3535
|
else
|
3237
|
-
if reply.
|
3238
|
-
if reply[0].
|
3536
|
+
if reply.is_a?(Array)
|
3537
|
+
if reply[0].is_a?(Array)
|
3239
3538
|
reply.map(&Hashify)
|
3240
3539
|
else
|
3241
3540
|
Hashify.call(reply)
|
@@ -3259,12 +3558,17 @@ class Redis
|
|
3259
3558
|
def cluster(subcommand, *args)
|
3260
3559
|
subcommand = subcommand.to_s.downcase
|
3261
3560
|
block = case subcommand
|
3262
|
-
|
3263
|
-
|
3264
|
-
|
3265
|
-
|
3266
|
-
|
3267
|
-
|
3561
|
+
when 'slots'
|
3562
|
+
HashifyClusterSlots
|
3563
|
+
when 'nodes'
|
3564
|
+
HashifyClusterNodes
|
3565
|
+
when 'slaves'
|
3566
|
+
HashifyClusterSlaves
|
3567
|
+
when 'info'
|
3568
|
+
HashifyInfo
|
3569
|
+
else
|
3570
|
+
Noop
|
3571
|
+
end
|
3268
3572
|
|
3269
3573
|
# @see https://github.com/antirez/redis/blob/unstable/src/redis-trib.rb#L127 raw reply expected
|
3270
3574
|
block = Noop unless @cluster_mode
|
@@ -3299,21 +3603,21 @@ class Redis
|
|
3299
3603
|
return @original_client.connection_info if @cluster_mode
|
3300
3604
|
|
3301
3605
|
{
|
3302
|
-
host:
|
3303
|
-
port:
|
3304
|
-
db:
|
3305
|
-
id:
|
3606
|
+
host: @original_client.host,
|
3607
|
+
port: @original_client.port,
|
3608
|
+
db: @original_client.db,
|
3609
|
+
id: @original_client.id,
|
3306
3610
|
location: @original_client.location
|
3307
3611
|
}
|
3308
3612
|
end
|
3309
3613
|
|
3310
|
-
def method_missing(command, *args)
|
3614
|
+
def method_missing(command, *args) # rubocop:disable Style/MissingRespondToMissing
|
3311
3615
|
synchronize do |client|
|
3312
3616
|
client.call([command] + args)
|
3313
3617
|
end
|
3314
3618
|
end
|
3315
3619
|
|
3316
|
-
private
|
3620
|
+
private
|
3317
3621
|
|
3318
3622
|
# Commands returning 1 for true and 0 for false may be executed in a pipeline
|
3319
3623
|
# where the method call will return nil. Propagate the nil instead of falsely
|
@@ -3385,18 +3689,35 @@ private
|
|
3385
3689
|
end
|
3386
3690
|
}
|
3387
3691
|
|
3692
|
+
EMPTY_STREAM_RESPONSE = [nil].freeze
|
3693
|
+
private_constant :EMPTY_STREAM_RESPONSE
|
3694
|
+
|
3388
3695
|
HashifyStreamEntries = lambda { |reply|
|
3389
|
-
reply.map do |entry_id, values|
|
3390
|
-
[entry_id, values
|
3696
|
+
reply.compact.map do |entry_id, values|
|
3697
|
+
[entry_id, values&.each_slice(2)&.to_h]
|
3391
3698
|
end
|
3392
3699
|
}
|
3393
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
|
+
|
3394
3715
|
HashifyStreamPendings = lambda { |reply|
|
3395
3716
|
{
|
3396
|
-
'size'
|
3717
|
+
'size' => reply[0],
|
3397
3718
|
'min_entry_id' => reply[1],
|
3398
3719
|
'max_entry_id' => reply[2],
|
3399
|
-
'consumers'
|
3720
|
+
'consumers' => reply[3].nil? ? {} : reply[3].to_h
|
3400
3721
|
}
|
3401
3722
|
}
|
3402
3723
|
|
@@ -3405,8 +3726,8 @@ private
|
|
3405
3726
|
{
|
3406
3727
|
'entry_id' => arr[0],
|
3407
3728
|
'consumer' => arr[1],
|
3408
|
-
'elapsed'
|
3409
|
-
'count'
|
3729
|
+
'elapsed' => arr[2],
|
3730
|
+
'count' => arr[3]
|
3410
3731
|
}
|
3411
3732
|
end
|
3412
3733
|
}
|
@@ -3414,15 +3735,15 @@ private
|
|
3414
3735
|
HashifyClusterNodeInfo = lambda { |str|
|
3415
3736
|
arr = str.split(' ')
|
3416
3737
|
{
|
3417
|
-
'node_id'
|
3418
|
-
'ip_port'
|
3419
|
-
'flags'
|
3738
|
+
'node_id' => arr[0],
|
3739
|
+
'ip_port' => arr[1],
|
3740
|
+
'flags' => arr[2].split(','),
|
3420
3741
|
'master_node_id' => arr[3],
|
3421
|
-
'ping_sent'
|
3422
|
-
'pong_recv'
|
3423
|
-
'config_epoch'
|
3424
|
-
'link_state'
|
3425
|
-
'slots'
|
3742
|
+
'ping_sent' => arr[4],
|
3743
|
+
'pong_recv' => arr[5],
|
3744
|
+
'config_epoch' => arr[6],
|
3745
|
+
'link_state' => arr[7],
|
3746
|
+
'slots' => arr[8].nil? ? nil : Range.new(*arr[8].split('-'))
|
3426
3747
|
}
|
3427
3748
|
}
|
3428
3749
|
|
@@ -3433,9 +3754,9 @@ private
|
|
3433
3754
|
replicas = arr[3..-1].map { |r| { 'ip' => r[0], 'port' => r[1], 'node_id' => r[2] } }
|
3434
3755
|
{
|
3435
3756
|
'start_slot' => first_slot,
|
3436
|
-
'end_slot'
|
3437
|
-
'master'
|
3438
|
-
'replicas'
|
3757
|
+
'end_slot' => last_slot,
|
3758
|
+
'master' => master,
|
3759
|
+
'replicas' => replicas
|
3439
3760
|
}
|
3440
3761
|
end
|
3441
3762
|
}
|
@@ -3490,6 +3811,21 @@ private
|
|
3490
3811
|
end
|
3491
3812
|
end
|
3492
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
|
3493
3829
|
end
|
3494
3830
|
|
3495
3831
|
require_relative "redis/version"
|