redis 3.3.5 → 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 +5 -5
- data/CHANGELOG.md +161 -2
- data/README.md +144 -79
- data/lib/redis/client.rb +166 -90
- data/lib/redis/cluster/command.rb +81 -0
- data/lib/redis/cluster/command_loader.rb +33 -0
- data/lib/redis/cluster/key_slot_converter.rb +72 -0
- data/lib/redis/cluster/node.rb +108 -0
- data/lib/redis/cluster/node_key.rb +31 -0
- data/lib/redis/cluster/node_loader.rb +37 -0
- data/lib/redis/cluster/option.rb +93 -0
- data/lib/redis/cluster/slot.rb +86 -0
- data/lib/redis/cluster/slot_loader.rb +49 -0
- data/lib/redis/cluster.rb +291 -0
- data/lib/redis/connection/command_helper.rb +7 -10
- data/lib/redis/connection/hiredis.rb +6 -5
- data/lib/redis/connection/registry.rb +2 -1
- data/lib/redis/connection/ruby.rb +128 -129
- data/lib/redis/connection/synchrony.rb +21 -8
- data/lib/redis/connection.rb +4 -2
- data/lib/redis/distributed.rb +194 -72
- data/lib/redis/errors.rb +48 -0
- data/lib/redis/hash_ring.rb +30 -73
- data/lib/redis/pipeline.rb +55 -15
- data/lib/redis/subscribe.rb +11 -12
- data/lib/redis/version.rb +3 -1
- data/lib/redis.rb +1451 -403
- metadata +49 -202
- data/.gitignore +0 -16
- data/.travis/Gemfile +0 -11
- data/.travis.yml +0 -89
- data/.yardopts +0 -3
- data/Gemfile +0 -4
- data/Rakefile +0 -87
- data/benchmarking/logging.rb +0 -71
- data/benchmarking/pipeline.rb +0 -51
- data/benchmarking/speed.rb +0 -21
- data/benchmarking/suite.rb +0 -24
- data/benchmarking/worker.rb +0 -71
- data/examples/basic.rb +0 -15
- data/examples/consistency.rb +0 -114
- data/examples/dist_redis.rb +0 -43
- data/examples/incr-decr.rb +0 -17
- data/examples/list.rb +0 -26
- data/examples/pubsub.rb +0 -37
- data/examples/sentinel/sentinel.conf +0 -9
- data/examples/sentinel/start +0 -49
- data/examples/sentinel.rb +0 -41
- data/examples/sets.rb +0 -36
- data/examples/unicorn/config.ru +0 -3
- data/examples/unicorn/unicorn.rb +0 -20
- data/redis.gemspec +0 -44
- data/test/bitpos_test.rb +0 -69
- data/test/blocking_commands_test.rb +0 -42
- data/test/client_test.rb +0 -59
- data/test/command_map_test.rb +0 -30
- data/test/commands_on_hashes_test.rb +0 -21
- data/test/commands_on_hyper_log_log_test.rb +0 -21
- data/test/commands_on_lists_test.rb +0 -20
- data/test/commands_on_sets_test.rb +0 -77
- data/test/commands_on_sorted_sets_test.rb +0 -137
- data/test/commands_on_strings_test.rb +0 -101
- data/test/commands_on_value_types_test.rb +0 -133
- data/test/connection_handling_test.rb +0 -277
- data/test/connection_test.rb +0 -57
- data/test/db/.gitkeep +0 -0
- data/test/distributed_blocking_commands_test.rb +0 -46
- data/test/distributed_commands_on_hashes_test.rb +0 -10
- data/test/distributed_commands_on_hyper_log_log_test.rb +0 -33
- data/test/distributed_commands_on_lists_test.rb +0 -22
- data/test/distributed_commands_on_sets_test.rb +0 -83
- data/test/distributed_commands_on_sorted_sets_test.rb +0 -18
- data/test/distributed_commands_on_strings_test.rb +0 -59
- data/test/distributed_commands_on_value_types_test.rb +0 -95
- data/test/distributed_commands_requiring_clustering_test.rb +0 -164
- data/test/distributed_connection_handling_test.rb +0 -23
- data/test/distributed_internals_test.rb +0 -79
- data/test/distributed_key_tags_test.rb +0 -52
- data/test/distributed_persistence_control_commands_test.rb +0 -26
- data/test/distributed_publish_subscribe_test.rb +0 -92
- data/test/distributed_remote_server_control_commands_test.rb +0 -66
- data/test/distributed_scripting_test.rb +0 -102
- data/test/distributed_sorting_test.rb +0 -20
- data/test/distributed_test.rb +0 -58
- data/test/distributed_transactions_test.rb +0 -32
- data/test/encoding_test.rb +0 -18
- data/test/error_replies_test.rb +0 -59
- data/test/fork_safety_test.rb +0 -65
- data/test/helper.rb +0 -232
- data/test/helper_test.rb +0 -24
- data/test/internals_test.rb +0 -417
- data/test/lint/blocking_commands.rb +0 -150
- data/test/lint/hashes.rb +0 -162
- data/test/lint/hyper_log_log.rb +0 -60
- data/test/lint/lists.rb +0 -143
- data/test/lint/sets.rb +0 -140
- data/test/lint/sorted_sets.rb +0 -316
- data/test/lint/strings.rb +0 -260
- data/test/lint/value_types.rb +0 -122
- data/test/persistence_control_commands_test.rb +0 -26
- data/test/pipelining_commands_test.rb +0 -242
- data/test/publish_subscribe_test.rb +0 -282
- data/test/remote_server_control_commands_test.rb +0 -118
- data/test/scanning_test.rb +0 -413
- data/test/scripting_test.rb +0 -78
- data/test/sentinel_command_test.rb +0 -80
- data/test/sentinel_test.rb +0 -255
- data/test/sorting_test.rb +0 -59
- data/test/ssl_test.rb +0 -73
- data/test/support/connection/hiredis.rb +0 -1
- data/test/support/connection/ruby.rb +0 -1
- data/test/support/connection/synchrony.rb +0 -17
- data/test/support/redis_mock.rb +0 -130
- data/test/support/ssl/gen_certs.sh +0 -31
- data/test/support/ssl/trusted-ca.crt +0 -25
- data/test/support/ssl/trusted-ca.key +0 -27
- data/test/support/ssl/trusted-cert.crt +0 -81
- data/test/support/ssl/trusted-cert.key +0 -28
- data/test/support/ssl/untrusted-ca.crt +0 -26
- data/test/support/ssl/untrusted-ca.key +0 -27
- data/test/support/ssl/untrusted-cert.crt +0 -82
- data/test/support/ssl/untrusted-cert.key +0 -28
- data/test/support/wire/synchrony.rb +0 -24
- data/test/support/wire/thread.rb +0 -5
- data/test/synchrony_driver.rb +0 -88
- data/test/test.conf.erb +0 -9
- data/test/thread_safety_test.rb +0 -62
- data/test/transactions_test.rb +0 -264
- data/test/unknown_commands_test.rb +0 -14
- data/test/url_param_test.rb +0 -138
data/lib/redis.rb
CHANGED
@@ -1,54 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "monitor"
|
2
|
-
|
4
|
+
require_relative "redis/errors"
|
3
5
|
|
4
6
|
class Redis
|
7
|
+
@exists_returns_integer = true
|
5
8
|
|
6
|
-
|
7
|
-
|
8
|
-
end
|
9
|
+
class << self
|
10
|
+
attr_reader :exists_returns_integer
|
9
11
|
|
10
|
-
|
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
|
11
20
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
new(options)
|
21
|
+
@exists_returns_integer = value
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_writer :current
|
17
25
|
end
|
18
26
|
|
19
27
|
def self.current
|
20
28
|
@current ||= Redis.new
|
21
29
|
end
|
22
30
|
|
23
|
-
def self.current=(redis)
|
24
|
-
@current = redis
|
25
|
-
end
|
26
|
-
|
27
31
|
include MonitorMixin
|
28
32
|
|
29
33
|
# Create a new client instance
|
30
34
|
#
|
31
35
|
# @param [Hash] options
|
32
|
-
# @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.
|
33
39
|
# @option options [String] :host ("127.0.0.1") server hostname
|
34
|
-
# @option options [
|
40
|
+
# @option options [Integer] :port (6379) server port
|
35
41
|
# @option options [String] :path path to server socket (overrides host and port)
|
36
42
|
# @option options [Float] :timeout (5.0) timeout in seconds
|
37
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
|
38
45
|
# @option options [String] :password Password to authenticate against server
|
39
|
-
# @option options [
|
46
|
+
# @option options [Integer] :db (0) Database to select after initial connect
|
40
47
|
# @option options [Symbol] :driver Driver to use, currently supported: `:ruby`, `:hiredis`, `:synchrony`
|
41
|
-
# @option options [String] :id ID for the client connection, assigns name to current connection by sending
|
42
|
-
#
|
43
|
-
# @option options [
|
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
|
52
|
+
# @option options [Integer] :reconnect_attempts Number of attempts trying to connect
|
44
53
|
# @option options [Boolean] :inherit_socket (false) Whether to use socket in forked process or not
|
45
54
|
# @option options [Array] :sentinels List of sentinels to contact
|
46
55
|
# @option options [Symbol] :role (:master) Role to fetch via Sentinel, either `:master` or `:slave`
|
56
|
+
# @option options [Array<String, Hash{Symbol => String, Integer}>] :cluster List of cluster nodes to contact
|
57
|
+
# @option options [Boolean] :replica Whether to use readonly replica nodes in Redis Cluster or not
|
58
|
+
# @option options [Class] :connector Class of custom connector
|
47
59
|
#
|
48
60
|
# @return [Redis] a new client instance
|
49
61
|
def initialize(options = {})
|
50
62
|
@options = options.dup
|
51
|
-
@
|
63
|
+
@cluster_mode = options.key?(:cluster)
|
64
|
+
client = @cluster_mode ? Cluster : Client
|
65
|
+
@original_client = @client = client.new(options)
|
52
66
|
@queue = Hash.new { |h, k| h[k] = [] }
|
53
67
|
|
54
68
|
super() # Monitor#initialize
|
@@ -59,7 +73,7 @@ class Redis
|
|
59
73
|
end
|
60
74
|
|
61
75
|
# Run code with the client reconnecting
|
62
|
-
def with_reconnect(val=true, &blk)
|
76
|
+
def with_reconnect(val = true, &blk)
|
63
77
|
synchronize do |client|
|
64
78
|
client.with_reconnect(val, &blk)
|
65
79
|
end
|
@@ -102,7 +116,9 @@ class Redis
|
|
102
116
|
# See http://redis.io/topics/pipelining for more details.
|
103
117
|
#
|
104
118
|
def queue(*command)
|
105
|
-
|
119
|
+
synchronize do
|
120
|
+
@queue[Thread.current.object_id] << command
|
121
|
+
end
|
106
122
|
end
|
107
123
|
|
108
124
|
# Sends all commands in the queue.
|
@@ -112,27 +128,37 @@ class Redis
|
|
112
128
|
def commit
|
113
129
|
synchronize do |client|
|
114
130
|
begin
|
115
|
-
|
131
|
+
pipeline = Pipeline.new(client)
|
132
|
+
@queue[Thread.current.object_id].each do |command|
|
133
|
+
pipeline.call(command)
|
134
|
+
end
|
135
|
+
|
136
|
+
client.call_pipelined(pipeline)
|
116
137
|
ensure
|
117
138
|
@queue.delete(Thread.current.object_id)
|
118
139
|
end
|
119
140
|
end
|
120
141
|
end
|
121
142
|
|
143
|
+
def _client
|
144
|
+
@client
|
145
|
+
end
|
146
|
+
|
122
147
|
# Authenticate to the server.
|
123
148
|
#
|
124
|
-
# @param [String]
|
125
|
-
#
|
149
|
+
# @param [Array<String>] args includes both username and password
|
150
|
+
# or only password
|
126
151
|
# @return [String] `OK`
|
127
|
-
|
152
|
+
# @see https://redis.io/commands/auth AUTH command
|
153
|
+
def auth(*args)
|
128
154
|
synchronize do |client|
|
129
|
-
client.call([:auth,
|
155
|
+
client.call([:auth, *args])
|
130
156
|
end
|
131
157
|
end
|
132
158
|
|
133
159
|
# Change the selected database for the current connection.
|
134
160
|
#
|
135
|
-
# @param [
|
161
|
+
# @param [Integer] db zero-based index of the DB to use (0 to 15)
|
136
162
|
# @return [String] `OK`
|
137
163
|
def select(db)
|
138
164
|
synchronize do |client|
|
@@ -143,10 +169,11 @@ class Redis
|
|
143
169
|
|
144
170
|
# Ping the server.
|
145
171
|
#
|
172
|
+
# @param [optional, String] message
|
146
173
|
# @return [String] `PONG`
|
147
|
-
def ping
|
174
|
+
def ping(message = nil)
|
148
175
|
synchronize do |client|
|
149
|
-
client.call([:ping])
|
176
|
+
client.call([:ping, message].compact)
|
150
177
|
end
|
151
178
|
end
|
152
179
|
|
@@ -200,7 +227,7 @@ class Redis
|
|
200
227
|
def config(action, *args)
|
201
228
|
synchronize do |client|
|
202
229
|
client.call([:config, action] + args) do |reply|
|
203
|
-
if reply.
|
230
|
+
if reply.is_a?(Array) && action == :get
|
204
231
|
Hashify.call(reply)
|
205
232
|
else
|
206
233
|
reply
|
@@ -209,9 +236,28 @@ class Redis
|
|
209
236
|
end
|
210
237
|
end
|
211
238
|
|
239
|
+
# Manage client connections.
|
240
|
+
#
|
241
|
+
# @param [String, Symbol] subcommand e.g. `kill`, `list`, `getname`, `setname`
|
242
|
+
# @return [String, Hash] depends on subcommand
|
243
|
+
def client(subcommand = nil, *args)
|
244
|
+
synchronize do |client|
|
245
|
+
client.call([:client, subcommand] + args) do |reply|
|
246
|
+
if subcommand.to_s == "list"
|
247
|
+
reply.lines.map do |line|
|
248
|
+
entries = line.chomp.split(/[ =]/)
|
249
|
+
Hash[entries.each_slice(2).to_a]
|
250
|
+
end
|
251
|
+
else
|
252
|
+
reply
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
212
258
|
# Return the number of keys in the selected database.
|
213
259
|
#
|
214
|
-
# @return [
|
260
|
+
# @return [Integer]
|
215
261
|
def dbsize
|
216
262
|
synchronize do |client|
|
217
263
|
client.call([:dbsize])
|
@@ -226,19 +272,31 @@ class Redis
|
|
226
272
|
|
227
273
|
# Remove all keys from all databases.
|
228
274
|
#
|
275
|
+
# @param [Hash] options
|
276
|
+
# - `:async => Boolean`: async flush (default: false)
|
229
277
|
# @return [String] `OK`
|
230
|
-
def flushall
|
278
|
+
def flushall(options = nil)
|
231
279
|
synchronize do |client|
|
232
|
-
|
280
|
+
if options && options[:async]
|
281
|
+
client.call(%i[flushall async])
|
282
|
+
else
|
283
|
+
client.call([:flushall])
|
284
|
+
end
|
233
285
|
end
|
234
286
|
end
|
235
287
|
|
236
288
|
# Remove all keys from the current database.
|
237
289
|
#
|
290
|
+
# @param [Hash] options
|
291
|
+
# - `:async => Boolean`: async flush (default: false)
|
238
292
|
# @return [String] `OK`
|
239
|
-
def flushdb
|
293
|
+
def flushdb(options = nil)
|
240
294
|
synchronize do |client|
|
241
|
-
|
295
|
+
if options && options[:async]
|
296
|
+
client.call(%i[flushdb async])
|
297
|
+
else
|
298
|
+
client.call([:flushdb])
|
299
|
+
end
|
242
300
|
end
|
243
301
|
end
|
244
302
|
|
@@ -249,10 +307,8 @@ class Redis
|
|
249
307
|
def info(cmd = nil)
|
250
308
|
synchronize do |client|
|
251
309
|
client.call([:info, cmd].compact) do |reply|
|
252
|
-
if reply.
|
253
|
-
reply =
|
254
|
-
line.split(":", 2) unless line =~ /^(#|$)/
|
255
|
-
end.compact]
|
310
|
+
if reply.is_a?(String)
|
311
|
+
reply = HashifyInfo.call(reply)
|
256
312
|
|
257
313
|
if cmd && cmd.to_s == "commandstats"
|
258
314
|
# Extract nested hashes for INFO COMMANDSTATS
|
@@ -270,7 +326,7 @@ class Redis
|
|
270
326
|
|
271
327
|
# Get the UNIX time stamp of the last successful save to disk.
|
272
328
|
#
|
273
|
-
# @return [
|
329
|
+
# @return [Integer]
|
274
330
|
def lastsave
|
275
331
|
synchronize do |client|
|
276
332
|
client.call([:lastsave])
|
@@ -322,9 +378,9 @@ class Redis
|
|
322
378
|
# Interact with the slowlog (get, len, reset)
|
323
379
|
#
|
324
380
|
# @param [String] subcommand e.g. `get`, `len`, `reset`
|
325
|
-
# @param [
|
326
|
-
# @return [Array<String>,
|
327
|
-
def slowlog(subcommand, length=nil)
|
381
|
+
# @param [Integer] length maximum number of entries to return
|
382
|
+
# @return [Array<String>, Integer, String] depends on subcommand
|
383
|
+
def slowlog(subcommand, length = nil)
|
328
384
|
synchronize do |client|
|
329
385
|
args = [:slowlog, subcommand]
|
330
386
|
args << length if length
|
@@ -344,12 +400,12 @@ class Redis
|
|
344
400
|
# @example
|
345
401
|
# r.time # => [ 1333093196, 606806 ]
|
346
402
|
#
|
347
|
-
# @return [Array<
|
403
|
+
# @return [Array<Integer>] tuple of seconds since UNIX epoch and
|
348
404
|
# microseconds in the current second
|
349
405
|
def time
|
350
406
|
synchronize do |client|
|
351
407
|
client.call([:time]) do |reply|
|
352
|
-
reply
|
408
|
+
reply&.map(&:to_i)
|
353
409
|
end
|
354
410
|
end
|
355
411
|
end
|
@@ -367,7 +423,7 @@ class Redis
|
|
367
423
|
# Set a key's time to live in seconds.
|
368
424
|
#
|
369
425
|
# @param [String] key
|
370
|
-
# @param [
|
426
|
+
# @param [Integer] seconds time to live
|
371
427
|
# @return [Boolean] whether the timeout was set or not
|
372
428
|
def expire(key, seconds)
|
373
429
|
synchronize do |client|
|
@@ -378,7 +434,7 @@ class Redis
|
|
378
434
|
# Set the expiration for a key as a UNIX timestamp.
|
379
435
|
#
|
380
436
|
# @param [String] key
|
381
|
-
# @param [
|
437
|
+
# @param [Integer] unix_time expiry time specified as a UNIX timestamp
|
382
438
|
# @return [Boolean] whether the timeout was set or not
|
383
439
|
def expireat(key, unix_time)
|
384
440
|
synchronize do |client|
|
@@ -389,7 +445,7 @@ class Redis
|
|
389
445
|
# Get the time to live (in seconds) for a key.
|
390
446
|
#
|
391
447
|
# @param [String] key
|
392
|
-
# @return [
|
448
|
+
# @return [Integer] remaining time to live in seconds.
|
393
449
|
#
|
394
450
|
# In Redis 2.6 or older the command returns -1 if the key does not exist or if
|
395
451
|
# the key exist but has no associated expire.
|
@@ -407,7 +463,7 @@ class Redis
|
|
407
463
|
# Set a key's time to live in milliseconds.
|
408
464
|
#
|
409
465
|
# @param [String] key
|
410
|
-
# @param [
|
466
|
+
# @param [Integer] milliseconds time to live
|
411
467
|
# @return [Boolean] whether the timeout was set or not
|
412
468
|
def pexpire(key, milliseconds)
|
413
469
|
synchronize do |client|
|
@@ -418,7 +474,7 @@ class Redis
|
|
418
474
|
# Set the expiration for a key as number of milliseconds from UNIX Epoch.
|
419
475
|
#
|
420
476
|
# @param [String] key
|
421
|
-
# @param [
|
477
|
+
# @param [Integer] ms_unix_time expiry time specified as number of milliseconds from UNIX Epoch.
|
422
478
|
# @return [Boolean] whether the timeout was set or not
|
423
479
|
def pexpireat(key, ms_unix_time)
|
424
480
|
synchronize do |client|
|
@@ -429,7 +485,7 @@ class Redis
|
|
429
485
|
# Get the time to live (in milliseconds) for a key.
|
430
486
|
#
|
431
487
|
# @param [String] key
|
432
|
-
# @return [
|
488
|
+
# @return [Integer] remaining time to live in milliseconds
|
433
489
|
# In Redis 2.6 or older the command returns -1 if the key does not exist or if
|
434
490
|
# the key exist but has no associated expire.
|
435
491
|
#
|
@@ -458,50 +514,104 @@ class Redis
|
|
458
514
|
# @param [String] key
|
459
515
|
# @param [String] ttl
|
460
516
|
# @param [String] serialized_value
|
517
|
+
# @param [Hash] options
|
518
|
+
# - `:replace => Boolean`: if false, raises an error if key already exists
|
519
|
+
# @raise [Redis::CommandError]
|
461
520
|
# @return [String] `"OK"`
|
462
|
-
def restore(key, ttl, serialized_value)
|
521
|
+
def restore(key, ttl, serialized_value, replace: nil)
|
522
|
+
args = [:restore, key, ttl, serialized_value]
|
523
|
+
args << 'REPLACE' if replace
|
524
|
+
|
463
525
|
synchronize do |client|
|
464
|
-
client.call(
|
526
|
+
client.call(args)
|
465
527
|
end
|
466
528
|
end
|
467
529
|
|
468
530
|
# Transfer a key from the connected instance to another instance.
|
469
531
|
#
|
470
|
-
# @param [String] key
|
532
|
+
# @param [String, Array<String>] key
|
471
533
|
# @param [Hash] options
|
472
534
|
# - `:host => String`: host of instance to migrate to
|
473
535
|
# - `:port => Integer`: port of instance to migrate to
|
474
536
|
# - `:db => Integer`: database to migrate to (default: same as source)
|
475
537
|
# - `:timeout => Integer`: timeout (default: same as connection timeout)
|
538
|
+
# - `:copy => Boolean`: Do not remove the key from the local instance.
|
539
|
+
# - `:replace => Boolean`: Replace existing key on the remote instance.
|
476
540
|
# @return [String] `"OK"`
|
477
541
|
def migrate(key, options)
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
542
|
+
args = [:migrate]
|
543
|
+
args << (options[:host] || raise(':host not specified'))
|
544
|
+
args << (options[:port] || raise(':port not specified'))
|
545
|
+
args << (key.is_a?(String) ? key : '')
|
546
|
+
args << (options[:db] || @client.db).to_i
|
547
|
+
args << (options[:timeout] || @client.timeout).to_i
|
548
|
+
args << 'COPY' if options[:copy]
|
549
|
+
args << 'REPLACE' if options[:replace]
|
550
|
+
args += ['KEYS', *key] if key.is_a?(Array)
|
482
551
|
|
483
|
-
synchronize
|
484
|
-
client.call([:migrate, host, port, key, db, timeout])
|
485
|
-
end
|
552
|
+
synchronize { |client| client.call(args) }
|
486
553
|
end
|
487
554
|
|
488
555
|
# Delete one or more keys.
|
489
556
|
#
|
490
557
|
# @param [String, Array<String>] keys
|
491
|
-
# @return [
|
558
|
+
# @return [Integer] number of keys that were deleted
|
492
559
|
def del(*keys)
|
560
|
+
keys.flatten!(1)
|
561
|
+
return 0 if keys.empty?
|
562
|
+
|
493
563
|
synchronize do |client|
|
494
564
|
client.call([:del] + keys)
|
495
565
|
end
|
496
566
|
end
|
497
567
|
|
498
|
-
#
|
568
|
+
# Unlink one or more keys.
|
499
569
|
#
|
500
|
-
# @param [String]
|
570
|
+
# @param [String, Array<String>] keys
|
571
|
+
# @return [Integer] number of keys that were unlinked
|
572
|
+
def unlink(*keys)
|
573
|
+
synchronize do |client|
|
574
|
+
client.call([:unlink] + keys)
|
575
|
+
end
|
576
|
+
end
|
577
|
+
|
578
|
+
# Determine how many of the keys exists.
|
579
|
+
#
|
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
|
501
609
|
# @return [Boolean]
|
502
|
-
def exists(
|
610
|
+
def exists?(*keys)
|
503
611
|
synchronize do |client|
|
504
|
-
client.call([:exists,
|
612
|
+
client.call([:exists, *keys]) do |value|
|
613
|
+
value > 0
|
614
|
+
end
|
505
615
|
end
|
506
616
|
end
|
507
617
|
|
@@ -512,7 +622,7 @@ class Redis
|
|
512
622
|
def keys(pattern = "*")
|
513
623
|
synchronize do |client|
|
514
624
|
client.call([:keys, pattern]) do |reply|
|
515
|
-
if reply.
|
625
|
+
if reply.is_a?(String)
|
516
626
|
reply.split(" ")
|
517
627
|
else
|
518
628
|
reply
|
@@ -538,7 +648,7 @@ class Redis
|
|
538
648
|
# # => "bar"
|
539
649
|
#
|
540
650
|
# @param [String] key
|
541
|
-
# @param [
|
651
|
+
# @param [Integer] db
|
542
652
|
# @return [Boolean] whether the key was moved or not
|
543
653
|
def move(key, db)
|
544
654
|
synchronize do |client|
|
@@ -602,36 +712,33 @@ class Redis
|
|
602
712
|
# - `:order => String`: combination of `ASC`, `DESC` and optionally `ALPHA`
|
603
713
|
# - `:store => String`: key to store the result at
|
604
714
|
#
|
605
|
-
# @return [Array<String>, Array<Array<String>>,
|
715
|
+
# @return [Array<String>, Array<Array<String>>, Integer]
|
606
716
|
# - when `:get` is not specified, or holds a single element, an array of elements
|
607
717
|
# - when `:get` is specified, and holds more than one element, an array of
|
608
718
|
# elements where every element is an array with the result for every
|
609
719
|
# element specified in `:get`
|
610
720
|
# - when `:store` is specified, the number of elements in the stored result
|
611
|
-
def sort(key,
|
612
|
-
args = []
|
721
|
+
def sort(key, by: nil, limit: nil, get: nil, order: nil, store: nil)
|
722
|
+
args = [:sort, key]
|
723
|
+
args << "BY" << by if by
|
613
724
|
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
args.concat(["LIMIT"] + limit) if limit
|
725
|
+
if limit
|
726
|
+
args << "LIMIT"
|
727
|
+
args.concat(limit)
|
728
|
+
end
|
619
729
|
|
620
|
-
get = Array(
|
621
|
-
|
730
|
+
get = Array(get)
|
731
|
+
get.each do |item|
|
732
|
+
args << "GET" << item
|
733
|
+
end
|
622
734
|
|
623
|
-
order = options[:order]
|
624
735
|
args.concat(order.split(" ")) if order
|
625
|
-
|
626
|
-
store = options[:store]
|
627
|
-
args.concat(["STORE", store]) if store
|
736
|
+
args << "STORE" << store if store
|
628
737
|
|
629
738
|
synchronize do |client|
|
630
|
-
client.call(
|
739
|
+
client.call(args) do |reply|
|
631
740
|
if get.size > 1 && !store
|
632
|
-
if reply
|
633
|
-
reply.each_slice(get.size).to_a
|
634
|
-
end
|
741
|
+
reply.each_slice(get.size).to_a if reply
|
635
742
|
else
|
636
743
|
reply
|
637
744
|
end
|
@@ -656,7 +763,7 @@ class Redis
|
|
656
763
|
# # => 4
|
657
764
|
#
|
658
765
|
# @param [String] key
|
659
|
-
# @return [
|
766
|
+
# @return [Integer] value after decrementing it
|
660
767
|
def decr(key)
|
661
768
|
synchronize do |client|
|
662
769
|
client.call([:decr, key])
|
@@ -670,8 +777,8 @@ class Redis
|
|
670
777
|
# # => 0
|
671
778
|
#
|
672
779
|
# @param [String] key
|
673
|
-
# @param [
|
674
|
-
# @return [
|
780
|
+
# @param [Integer] decrement
|
781
|
+
# @return [Integer] value after decrementing it
|
675
782
|
def decrby(key, decrement)
|
676
783
|
synchronize do |client|
|
677
784
|
client.call([:decrby, key, decrement])
|
@@ -685,7 +792,7 @@ class Redis
|
|
685
792
|
# # => 6
|
686
793
|
#
|
687
794
|
# @param [String] key
|
688
|
-
# @return [
|
795
|
+
# @return [Integer] value after incrementing it
|
689
796
|
def incr(key)
|
690
797
|
synchronize do |client|
|
691
798
|
client.call([:incr, key])
|
@@ -699,8 +806,8 @@ class Redis
|
|
699
806
|
# # => 10
|
700
807
|
#
|
701
808
|
# @param [String] key
|
702
|
-
# @param [
|
703
|
-
# @return [
|
809
|
+
# @param [Integer] increment
|
810
|
+
# @return [Integer] value after incrementing it
|
704
811
|
def incrby(key, increment)
|
705
812
|
synchronize do |client|
|
706
813
|
client.call([:incrby, key, increment])
|
@@ -727,41 +834,39 @@ class Redis
|
|
727
834
|
# @param [String] key
|
728
835
|
# @param [String] value
|
729
836
|
# @param [Hash] options
|
730
|
-
# - `:ex =>
|
731
|
-
# - `:px =>
|
837
|
+
# - `:ex => Integer`: Set the specified expire time, in seconds.
|
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.
|
732
841
|
# - `:nx => true`: Only set the key if it does not already exist.
|
733
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.
|
734
845
|
# @return [String, Boolean] `"OK"` or true, false if `:nx => true` or `:xx => true`
|
735
|
-
def set(key, value,
|
736
|
-
args = []
|
737
|
-
|
738
|
-
|
739
|
-
args
|
740
|
-
|
741
|
-
|
742
|
-
args
|
743
|
-
|
744
|
-
|
745
|
-
args.concat(["NX"]) if nx
|
746
|
-
|
747
|
-
xx = options[:xx]
|
748
|
-
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
|
749
856
|
|
750
857
|
synchronize do |client|
|
751
858
|
if nx || xx
|
752
|
-
client.call(
|
859
|
+
client.call(args, &BoolifySet)
|
753
860
|
else
|
754
|
-
client.call(
|
861
|
+
client.call(args)
|
755
862
|
end
|
756
863
|
end
|
757
864
|
end
|
758
865
|
|
759
|
-
alias :[]= :set
|
760
|
-
|
761
866
|
# Set the time to live in seconds of a key.
|
762
867
|
#
|
763
868
|
# @param [String] key
|
764
|
-
# @param [
|
869
|
+
# @param [Integer] ttl
|
765
870
|
# @param [String] value
|
766
871
|
# @return [String] `"OK"`
|
767
872
|
def setex(key, ttl, value)
|
@@ -773,7 +878,7 @@ class Redis
|
|
773
878
|
# Set the time to live in milliseconds of a key.
|
774
879
|
#
|
775
880
|
# @param [String] key
|
776
|
-
# @param [
|
881
|
+
# @param [Integer] ttl
|
777
882
|
# @param [String] value
|
778
883
|
# @return [String] `"OK"`
|
779
884
|
def psetex(key, ttl, value)
|
@@ -835,7 +940,7 @@ class Redis
|
|
835
940
|
# @see #mapped_msetnx
|
836
941
|
def msetnx(*args)
|
837
942
|
synchronize do |client|
|
838
|
-
client.call([:msetnx
|
943
|
+
client.call([:msetnx, *args], &Boolify)
|
839
944
|
end
|
840
945
|
end
|
841
946
|
|
@@ -863,12 +968,10 @@ class Redis
|
|
863
968
|
end
|
864
969
|
end
|
865
970
|
|
866
|
-
alias :[] :get
|
867
|
-
|
868
971
|
# Get the values of all the given keys.
|
869
972
|
#
|
870
973
|
# @example
|
871
|
-
# redis.mget("key1", "
|
974
|
+
# redis.mget("key1", "key2")
|
872
975
|
# # => ["v1", "v2"]
|
873
976
|
#
|
874
977
|
# @param [Array<String>] keys
|
@@ -877,7 +980,7 @@ class Redis
|
|
877
980
|
# @see #mapped_mget
|
878
981
|
def mget(*keys, &blk)
|
879
982
|
synchronize do |client|
|
880
|
-
client.call([:mget
|
983
|
+
client.call([:mget, *keys], &blk)
|
881
984
|
end
|
882
985
|
end
|
883
986
|
|
@@ -893,7 +996,7 @@ class Redis
|
|
893
996
|
# @see #mget
|
894
997
|
def mapped_mget(*keys)
|
895
998
|
mget(*keys) do |reply|
|
896
|
-
if reply.
|
999
|
+
if reply.is_a?(Array)
|
897
1000
|
Hash[keys.zip(reply)]
|
898
1001
|
else
|
899
1002
|
reply
|
@@ -904,9 +1007,9 @@ class Redis
|
|
904
1007
|
# Overwrite part of a string at key starting at the specified offset.
|
905
1008
|
#
|
906
1009
|
# @param [String] key
|
907
|
-
# @param [
|
1010
|
+
# @param [Integer] offset byte offset
|
908
1011
|
# @param [String] value
|
909
|
-
# @return [
|
1012
|
+
# @return [Integer] length of the string after it was modified
|
910
1013
|
def setrange(key, offset, value)
|
911
1014
|
synchronize do |client|
|
912
1015
|
client.call([:setrange, key, offset, value.to_s])
|
@@ -916,10 +1019,10 @@ class Redis
|
|
916
1019
|
# Get a substring of the string stored at a key.
|
917
1020
|
#
|
918
1021
|
# @param [String] key
|
919
|
-
# @param [
|
920
|
-
# @param [
|
1022
|
+
# @param [Integer] start zero-based start offset
|
1023
|
+
# @param [Integer] stop zero-based end offset. Use -1 for representing
|
921
1024
|
# the end of the string
|
922
|
-
# @return [
|
1025
|
+
# @return [Integer] `0` or `1`
|
923
1026
|
def getrange(key, start, stop)
|
924
1027
|
synchronize do |client|
|
925
1028
|
client.call([:getrange, key, start, stop])
|
@@ -929,9 +1032,9 @@ class Redis
|
|
929
1032
|
# Sets or clears the bit at offset in the string value stored at key.
|
930
1033
|
#
|
931
1034
|
# @param [String] key
|
932
|
-
# @param [
|
933
|
-
# @param [
|
934
|
-
# @return [
|
1035
|
+
# @param [Integer] offset bit offset
|
1036
|
+
# @param [Integer] value bit value `0` or `1`
|
1037
|
+
# @return [Integer] the original bit value stored at `offset`
|
935
1038
|
def setbit(key, offset, value)
|
936
1039
|
synchronize do |client|
|
937
1040
|
client.call([:setbit, key, offset, value])
|
@@ -941,8 +1044,8 @@ class Redis
|
|
941
1044
|
# Returns the bit value at offset in the string value stored at key.
|
942
1045
|
#
|
943
1046
|
# @param [String] key
|
944
|
-
# @param [
|
945
|
-
# @return [
|
1047
|
+
# @param [Integer] offset bit offset
|
1048
|
+
# @return [Integer] `0` or `1`
|
946
1049
|
def getbit(key, offset)
|
947
1050
|
synchronize do |client|
|
948
1051
|
client.call([:getbit, key, offset])
|
@@ -953,7 +1056,7 @@ class Redis
|
|
953
1056
|
#
|
954
1057
|
# @param [String] key
|
955
1058
|
# @param [String] value value to append
|
956
|
-
# @return [
|
1059
|
+
# @return [Integer] length of the string after appending
|
957
1060
|
def append(key, value)
|
958
1061
|
synchronize do |client|
|
959
1062
|
client.call([:append, key, value])
|
@@ -963,9 +1066,9 @@ class Redis
|
|
963
1066
|
# Count the number of set bits in a range of the string value stored at key.
|
964
1067
|
#
|
965
1068
|
# @param [String] key
|
966
|
-
# @param [
|
967
|
-
# @param [
|
968
|
-
# @return [
|
1069
|
+
# @param [Integer] start start index
|
1070
|
+
# @param [Integer] stop stop index
|
1071
|
+
# @return [Integer] the number of bits set to 1
|
969
1072
|
def bitcount(key, start = 0, stop = -1)
|
970
1073
|
synchronize do |client|
|
971
1074
|
client.call([:bitcount, key, start, stop])
|
@@ -977,25 +1080,23 @@ class Redis
|
|
977
1080
|
# @param [String] operation e.g. `and`, `or`, `xor`, `not`
|
978
1081
|
# @param [String] destkey destination key
|
979
1082
|
# @param [String, Array<String>] keys one or more source keys to perform `operation`
|
980
|
-
# @return [
|
1083
|
+
# @return [Integer] the length of the string stored in `destkey`
|
981
1084
|
def bitop(operation, destkey, *keys)
|
982
1085
|
synchronize do |client|
|
983
|
-
client.call([:bitop, operation, destkey
|
1086
|
+
client.call([:bitop, operation, destkey, *keys])
|
984
1087
|
end
|
985
1088
|
end
|
986
1089
|
|
987
1090
|
# Return the position of the first bit set to 1 or 0 in a string.
|
988
1091
|
#
|
989
1092
|
# @param [String] key
|
990
|
-
# @param [
|
991
|
-
# @param [
|
992
|
-
# @param [
|
993
|
-
# @return [
|
1093
|
+
# @param [Integer] bit whether to look for the first 1 or 0 bit
|
1094
|
+
# @param [Integer] start start index
|
1095
|
+
# @param [Integer] stop stop index
|
1096
|
+
# @return [Integer] the position of the first 1/0 bit.
|
994
1097
|
# -1 if looking for 1 and it is not found or start and stop are given.
|
995
|
-
def bitpos(key, bit, start=nil, stop=nil)
|
996
|
-
|
997
|
-
raise(ArgumentError, 'stop parameter specified without start parameter')
|
998
|
-
end
|
1098
|
+
def bitpos(key, bit, start = nil, stop = nil)
|
1099
|
+
raise(ArgumentError, 'stop parameter specified without start parameter') if stop && !start
|
999
1100
|
|
1000
1101
|
synchronize do |client|
|
1001
1102
|
command = [:bitpos, key, bit]
|
@@ -1017,10 +1118,49 @@ class Redis
|
|
1017
1118
|
end
|
1018
1119
|
end
|
1019
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
|
+
|
1020
1160
|
# Get the length of the value stored in a key.
|
1021
1161
|
#
|
1022
1162
|
# @param [String] key
|
1023
|
-
# @return [
|
1163
|
+
# @return [Integer] the length of the value stored in the key, or 0
|
1024
1164
|
# if the key does not exist
|
1025
1165
|
def strlen(key)
|
1026
1166
|
synchronize do |client|
|
@@ -1031,18 +1171,71 @@ class Redis
|
|
1031
1171
|
# Get the length of a list.
|
1032
1172
|
#
|
1033
1173
|
# @param [String] key
|
1034
|
-
# @return [
|
1174
|
+
# @return [Integer]
|
1035
1175
|
def llen(key)
|
1036
1176
|
synchronize do |client|
|
1037
1177
|
client.call([:llen, key])
|
1038
1178
|
end
|
1039
1179
|
end
|
1040
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
|
+
|
1041
1234
|
# Prepend one or more values to a list, creating the list if it doesn't exist
|
1042
1235
|
#
|
1043
1236
|
# @param [String] key
|
1044
|
-
# @param [String, Array] value string value, or array of string values to push
|
1045
|
-
# @return [
|
1237
|
+
# @param [String, Array<String>] value string value, or array of string values to push
|
1238
|
+
# @return [Integer] the length of the list after the push operation
|
1046
1239
|
def lpush(key, value)
|
1047
1240
|
synchronize do |client|
|
1048
1241
|
client.call([:lpush, key, value])
|
@@ -1053,7 +1246,7 @@ class Redis
|
|
1053
1246
|
#
|
1054
1247
|
# @param [String] key
|
1055
1248
|
# @param [String] value
|
1056
|
-
# @return [
|
1249
|
+
# @return [Integer] the length of the list after the push operation
|
1057
1250
|
def lpushx(key, value)
|
1058
1251
|
synchronize do |client|
|
1059
1252
|
client.call([:lpushx, key, value])
|
@@ -1063,8 +1256,8 @@ class Redis
|
|
1063
1256
|
# Append one or more values to a list, creating the list if it doesn't exist
|
1064
1257
|
#
|
1065
1258
|
# @param [String] key
|
1066
|
-
# @param [String] value
|
1067
|
-
# @return [
|
1259
|
+
# @param [String, Array<String>] value string value, or array of string values to push
|
1260
|
+
# @return [Integer] the length of the list after the push operation
|
1068
1261
|
def rpush(key, value)
|
1069
1262
|
synchronize do |client|
|
1070
1263
|
client.call([:rpush, key, value])
|
@@ -1075,30 +1268,36 @@ class Redis
|
|
1075
1268
|
#
|
1076
1269
|
# @param [String] key
|
1077
1270
|
# @param [String] value
|
1078
|
-
# @return [
|
1271
|
+
# @return [Integer] the length of the list after the push operation
|
1079
1272
|
def rpushx(key, value)
|
1080
1273
|
synchronize do |client|
|
1081
1274
|
client.call([:rpushx, key, value])
|
1082
1275
|
end
|
1083
1276
|
end
|
1084
1277
|
|
1085
|
-
# Remove and get the first
|
1278
|
+
# Remove and get the first elements in a list.
|
1086
1279
|
#
|
1087
1280
|
# @param [String] key
|
1088
|
-
# @
|
1089
|
-
|
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)
|
1090
1284
|
synchronize do |client|
|
1091
|
-
|
1285
|
+
command = [:lpop, key]
|
1286
|
+
command << count if count
|
1287
|
+
client.call(command)
|
1092
1288
|
end
|
1093
1289
|
end
|
1094
1290
|
|
1095
|
-
# Remove and get the last
|
1291
|
+
# Remove and get the last elements in a list.
|
1096
1292
|
#
|
1097
1293
|
# @param [String] key
|
1098
|
-
# @
|
1099
|
-
|
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)
|
1100
1297
|
synchronize do |client|
|
1101
|
-
|
1298
|
+
command = [:rpop, key]
|
1299
|
+
command << count if count
|
1300
|
+
client.call(command)
|
1102
1301
|
end
|
1103
1302
|
end
|
1104
1303
|
|
@@ -1113,28 +1312,27 @@ class Redis
|
|
1113
1312
|
end
|
1114
1313
|
end
|
1115
1314
|
|
1116
|
-
def _bpop(cmd, args)
|
1117
|
-
|
1118
|
-
|
1119
|
-
case args.last
|
1120
|
-
when Hash
|
1315
|
+
def _bpop(cmd, args, &blk)
|
1316
|
+
timeout = if args.last.is_a?(Hash)
|
1121
1317
|
options = args.pop
|
1122
|
-
|
1318
|
+
options[:timeout]
|
1319
|
+
elsif args.last.respond_to?(:to_int)
|
1123
1320
|
# Issue deprecation notice in obnoxious mode...
|
1124
|
-
|
1321
|
+
args.pop.to_int
|
1125
1322
|
end
|
1126
1323
|
|
1324
|
+
timeout ||= 0
|
1325
|
+
|
1127
1326
|
if args.size > 1
|
1128
1327
|
# Issue deprecation notice in obnoxious mode...
|
1129
1328
|
end
|
1130
1329
|
|
1131
1330
|
keys = args.flatten
|
1132
|
-
timeout = options[:timeout] || 0
|
1133
1331
|
|
1134
1332
|
synchronize do |client|
|
1135
1333
|
command = [cmd, keys, timeout]
|
1136
1334
|
timeout += client.timeout if timeout > 0
|
1137
|
-
client.call_with_timeout(command, timeout)
|
1335
|
+
client.call_with_timeout(command, timeout, &blk)
|
1138
1336
|
end
|
1139
1337
|
end
|
1140
1338
|
|
@@ -1154,7 +1352,7 @@ class Redis
|
|
1154
1352
|
# @param [String, Array<String>] keys one or more keys to perform the
|
1155
1353
|
# blocking pop on
|
1156
1354
|
# @param [Hash] options
|
1157
|
-
# - `:timeout =>
|
1355
|
+
# - `:timeout => Integer`: timeout in seconds, defaults to no timeout
|
1158
1356
|
#
|
1159
1357
|
# @return [nil, [String, String]]
|
1160
1358
|
# - `nil` when the operation timed out
|
@@ -1168,7 +1366,7 @@ class Redis
|
|
1168
1366
|
# @param [String, Array<String>] keys one or more keys to perform the
|
1169
1367
|
# blocking pop on
|
1170
1368
|
# @param [Hash] options
|
1171
|
-
# - `:timeout =>
|
1369
|
+
# - `:timeout => Integer`: timeout in seconds, defaults to no timeout
|
1172
1370
|
#
|
1173
1371
|
# @return [nil, [String, String]]
|
1174
1372
|
# - `nil` when the operation timed out
|
@@ -1185,20 +1383,12 @@ class Redis
|
|
1185
1383
|
# @param [String] source source key
|
1186
1384
|
# @param [String] destination destination key
|
1187
1385
|
# @param [Hash] options
|
1188
|
-
# - `:timeout =>
|
1386
|
+
# - `:timeout => Integer`: timeout in seconds, defaults to no timeout
|
1189
1387
|
#
|
1190
1388
|
# @return [nil, String]
|
1191
1389
|
# - `nil` when the operation timed out
|
1192
1390
|
# - the element was popped and pushed otherwise
|
1193
|
-
def brpoplpush(source, destination,
|
1194
|
-
case options
|
1195
|
-
when Integer
|
1196
|
-
# Issue deprecation notice in obnoxious mode...
|
1197
|
-
options = { :timeout => options }
|
1198
|
-
end
|
1199
|
-
|
1200
|
-
timeout = options[:timeout] || 0
|
1201
|
-
|
1391
|
+
def brpoplpush(source, destination, deprecated_timeout = 0, timeout: deprecated_timeout)
|
1202
1392
|
synchronize do |client|
|
1203
1393
|
command = [:brpoplpush, source, destination, timeout]
|
1204
1394
|
timeout += client.timeout if timeout > 0
|
@@ -1209,7 +1399,7 @@ class Redis
|
|
1209
1399
|
# Get an element from a list by its index.
|
1210
1400
|
#
|
1211
1401
|
# @param [String] key
|
1212
|
-
# @param [
|
1402
|
+
# @param [Integer] index
|
1213
1403
|
# @return [String]
|
1214
1404
|
def lindex(key, index)
|
1215
1405
|
synchronize do |client|
|
@@ -1223,7 +1413,7 @@ class Redis
|
|
1223
1413
|
# @param [String, Symbol] where `BEFORE` or `AFTER`
|
1224
1414
|
# @param [String] pivot reference element
|
1225
1415
|
# @param [String] value
|
1226
|
-
# @return [
|
1416
|
+
# @return [Integer] length of the list after the insert operation, or `-1`
|
1227
1417
|
# when the element `pivot` was not found
|
1228
1418
|
def linsert(key, where, pivot, value)
|
1229
1419
|
synchronize do |client|
|
@@ -1234,8 +1424,8 @@ class Redis
|
|
1234
1424
|
# Get a range of elements from a list.
|
1235
1425
|
#
|
1236
1426
|
# @param [String] key
|
1237
|
-
# @param [
|
1238
|
-
# @param [
|
1427
|
+
# @param [Integer] start start index
|
1428
|
+
# @param [Integer] stop stop index
|
1239
1429
|
# @return [Array<String>]
|
1240
1430
|
def lrange(key, start, stop)
|
1241
1431
|
synchronize do |client|
|
@@ -1246,12 +1436,12 @@ class Redis
|
|
1246
1436
|
# Remove elements from a list.
|
1247
1437
|
#
|
1248
1438
|
# @param [String] key
|
1249
|
-
# @param [
|
1439
|
+
# @param [Integer] count number of elements to remove. Use a positive
|
1250
1440
|
# value to remove the first `count` occurrences of `value`. A negative
|
1251
1441
|
# value to remove the last `count` occurrences of `value`. Or zero, to
|
1252
1442
|
# remove all occurrences of `value` from the list.
|
1253
1443
|
# @param [String] value
|
1254
|
-
# @return [
|
1444
|
+
# @return [Integer] the number of removed elements
|
1255
1445
|
def lrem(key, count, value)
|
1256
1446
|
synchronize do |client|
|
1257
1447
|
client.call([:lrem, key, count, value])
|
@@ -1261,7 +1451,7 @@ class Redis
|
|
1261
1451
|
# Set the value of an element in a list by its index.
|
1262
1452
|
#
|
1263
1453
|
# @param [String] key
|
1264
|
-
# @param [
|
1454
|
+
# @param [Integer] index
|
1265
1455
|
# @param [String] value
|
1266
1456
|
# @return [String] `OK`
|
1267
1457
|
def lset(key, index, value)
|
@@ -1273,8 +1463,8 @@ class Redis
|
|
1273
1463
|
# Trim a list to the specified range.
|
1274
1464
|
#
|
1275
1465
|
# @param [String] key
|
1276
|
-
# @param [
|
1277
|
-
# @param [
|
1466
|
+
# @param [Integer] start start index
|
1467
|
+
# @param [Integer] stop stop index
|
1278
1468
|
# @return [String] `OK`
|
1279
1469
|
def ltrim(key, start, stop)
|
1280
1470
|
synchronize do |client|
|
@@ -1285,7 +1475,7 @@ class Redis
|
|
1285
1475
|
# Get the number of members in a set.
|
1286
1476
|
#
|
1287
1477
|
# @param [String] key
|
1288
|
-
# @return [
|
1478
|
+
# @return [Integer]
|
1289
1479
|
def scard(key)
|
1290
1480
|
synchronize do |client|
|
1291
1481
|
client.call([:scard, key])
|
@@ -1296,8 +1486,8 @@ class Redis
|
|
1296
1486
|
#
|
1297
1487
|
# @param [String] key
|
1298
1488
|
# @param [String, Array<String>] member one member, or array of members
|
1299
|
-
# @return [Boolean,
|
1300
|
-
# holding whether or not adding the member succeeded, or `
|
1489
|
+
# @return [Boolean, Integer] `Boolean` when a single member is specified,
|
1490
|
+
# holding whether or not adding the member succeeded, or `Integer` when an
|
1301
1491
|
# array of members is specified, holding the number of members that were
|
1302
1492
|
# successfully added
|
1303
1493
|
def sadd(key, member)
|
@@ -1318,8 +1508,8 @@ class Redis
|
|
1318
1508
|
#
|
1319
1509
|
# @param [String] key
|
1320
1510
|
# @param [String, Array<String>] member one member, or array of members
|
1321
|
-
# @return [Boolean,
|
1322
|
-
# holding whether or not removing the member succeeded, or `
|
1511
|
+
# @return [Boolean, Integer] `Boolean` when a single member is specified,
|
1512
|
+
# holding whether or not removing the member succeeded, or `Integer` when an
|
1323
1513
|
# array of members is specified, holding the number of members that were
|
1324
1514
|
# successfully removed
|
1325
1515
|
def srem(key, member)
|
@@ -1340,7 +1530,7 @@ class Redis
|
|
1340
1530
|
#
|
1341
1531
|
# @param [String] key
|
1342
1532
|
# @return [String]
|
1343
|
-
# @param [
|
1533
|
+
# @param [Integer] count
|
1344
1534
|
def spop(key, count = nil)
|
1345
1535
|
synchronize do |client|
|
1346
1536
|
if count.nil?
|
@@ -1354,7 +1544,7 @@ class Redis
|
|
1354
1544
|
# Get one or more random members from a set.
|
1355
1545
|
#
|
1356
1546
|
# @param [String] key
|
1357
|
-
# @param [
|
1547
|
+
# @param [Integer] count
|
1358
1548
|
# @return [String]
|
1359
1549
|
def srandmember(key, count = nil)
|
1360
1550
|
synchronize do |client|
|
@@ -1389,6 +1579,19 @@ class Redis
|
|
1389
1579
|
end
|
1390
1580
|
end
|
1391
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
|
+
|
1392
1595
|
# Get all the members in a set.
|
1393
1596
|
#
|
1394
1597
|
# @param [String] key
|
@@ -1405,7 +1608,7 @@ class Redis
|
|
1405
1608
|
# @return [Array<String>] members in the difference
|
1406
1609
|
def sdiff(*keys)
|
1407
1610
|
synchronize do |client|
|
1408
|
-
client.call([:sdiff
|
1611
|
+
client.call([:sdiff, *keys])
|
1409
1612
|
end
|
1410
1613
|
end
|
1411
1614
|
|
@@ -1413,10 +1616,10 @@ class Redis
|
|
1413
1616
|
#
|
1414
1617
|
# @param [String] destination destination key
|
1415
1618
|
# @param [String, Array<String>] keys keys pointing to sets to subtract
|
1416
|
-
# @return [
|
1619
|
+
# @return [Integer] number of elements in the resulting set
|
1417
1620
|
def sdiffstore(destination, *keys)
|
1418
1621
|
synchronize do |client|
|
1419
|
-
client.call([:sdiffstore, destination
|
1622
|
+
client.call([:sdiffstore, destination, *keys])
|
1420
1623
|
end
|
1421
1624
|
end
|
1422
1625
|
|
@@ -1426,7 +1629,7 @@ class Redis
|
|
1426
1629
|
# @return [Array<String>] members in the intersection
|
1427
1630
|
def sinter(*keys)
|
1428
1631
|
synchronize do |client|
|
1429
|
-
client.call([:sinter
|
1632
|
+
client.call([:sinter, *keys])
|
1430
1633
|
end
|
1431
1634
|
end
|
1432
1635
|
|
@@ -1434,10 +1637,10 @@ class Redis
|
|
1434
1637
|
#
|
1435
1638
|
# @param [String] destination destination key
|
1436
1639
|
# @param [String, Array<String>] keys keys pointing to sets to intersect
|
1437
|
-
# @return [
|
1640
|
+
# @return [Integer] number of elements in the resulting set
|
1438
1641
|
def sinterstore(destination, *keys)
|
1439
1642
|
synchronize do |client|
|
1440
|
-
client.call([:sinterstore, destination
|
1643
|
+
client.call([:sinterstore, destination, *keys])
|
1441
1644
|
end
|
1442
1645
|
end
|
1443
1646
|
|
@@ -1447,7 +1650,7 @@ class Redis
|
|
1447
1650
|
# @return [Array<String>] members in the union
|
1448
1651
|
def sunion(*keys)
|
1449
1652
|
synchronize do |client|
|
1450
|
-
client.call([:sunion
|
1653
|
+
client.call([:sunion, *keys])
|
1451
1654
|
end
|
1452
1655
|
end
|
1453
1656
|
|
@@ -1455,10 +1658,10 @@ class Redis
|
|
1455
1658
|
#
|
1456
1659
|
# @param [String] destination destination key
|
1457
1660
|
# @param [String, Array<String>] keys keys pointing to sets to unify
|
1458
|
-
# @return [
|
1661
|
+
# @return [Integer] number of elements in the resulting set
|
1459
1662
|
def sunionstore(destination, *keys)
|
1460
1663
|
synchronize do |client|
|
1461
|
-
client.call([:sunionstore, destination
|
1664
|
+
client.call([:sunionstore, destination, *keys])
|
1462
1665
|
end
|
1463
1666
|
end
|
1464
1667
|
|
@@ -1469,7 +1672,7 @@ class Redis
|
|
1469
1672
|
# # => 4
|
1470
1673
|
#
|
1471
1674
|
# @param [String] key
|
1472
|
-
# @return [
|
1675
|
+
# @return [Integer]
|
1473
1676
|
def zcard(key)
|
1474
1677
|
synchronize do |client|
|
1475
1678
|
client.call([:zcard, key])
|
@@ -1493,6 +1696,10 @@ class Redis
|
|
1493
1696
|
# add elements)
|
1494
1697
|
# - `:nx => true`: Don't update already existing elements (always
|
1495
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
|
1496
1703
|
# - `:ch => true`: Modify the return value from the number of new
|
1497
1704
|
# elements added, to the total number of elements changed (CH is an
|
1498
1705
|
# abbreviation of changed); changed elements are new elements added
|
@@ -1500,38 +1707,29 @@ class Redis
|
|
1500
1707
|
# - `:incr => true`: When this option is specified ZADD acts like
|
1501
1708
|
# ZINCRBY; only one score-element pair can be specified in this mode
|
1502
1709
|
#
|
1503
|
-
# @return [Boolean,
|
1710
|
+
# @return [Boolean, Integer, Float]
|
1504
1711
|
# - `Boolean` when a single pair is specified, holding whether or not it was
|
1505
1712
|
# **added** to the sorted set.
|
1506
|
-
# - `
|
1713
|
+
# - `Integer` when an array of pairs is specified, holding the number of
|
1507
1714
|
# pairs that were **added** to the sorted set.
|
1508
1715
|
# - `Float` when option :incr is specified, holding the score of the member
|
1509
1716
|
# after incrementing it.
|
1510
|
-
def zadd(key, *args
|
1511
|
-
|
1512
|
-
if
|
1513
|
-
|
1514
|
-
|
1515
|
-
|
1516
|
-
|
1517
|
-
|
1518
|
-
xx = options[:xx]
|
1519
|
-
zadd_options << "XX" if xx
|
1520
|
-
|
1521
|
-
ch = options[:ch]
|
1522
|
-
zadd_options << "CH" if ch
|
1523
|
-
|
1524
|
-
incr = options[:incr]
|
1525
|
-
zadd_options << "INCR" if incr
|
1526
|
-
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
|
1527
1725
|
|
1528
1726
|
synchronize do |client|
|
1529
1727
|
if args.size == 1 && args[0].is_a?(Array)
|
1530
1728
|
# Variadic: return float if INCR, integer if !INCR
|
1531
|
-
client.call(
|
1729
|
+
client.call(command + args[0], &(incr ? Floatify : nil))
|
1532
1730
|
elsif args.size == 2
|
1533
1731
|
# Single pair: return float if INCR, boolean if !INCR
|
1534
|
-
client.call(
|
1732
|
+
client.call(command + args, &(incr ? Floatify : Boolify))
|
1535
1733
|
else
|
1536
1734
|
raise ArgumentError, "wrong number of arguments"
|
1537
1735
|
end
|
@@ -1566,10 +1764,10 @@ class Redis
|
|
1566
1764
|
# - a single member
|
1567
1765
|
# - an array of members
|
1568
1766
|
#
|
1569
|
-
# @return [Boolean,
|
1767
|
+
# @return [Boolean, Integer]
|
1570
1768
|
# - `Boolean` when a single member is specified, holding whether or not it
|
1571
1769
|
# was removed from the sorted set
|
1572
|
-
# - `
|
1770
|
+
# - `Integer` when an array of pairs is specified, holding the number of
|
1573
1771
|
# members that were removed to the sorted set
|
1574
1772
|
def zrem(key, member)
|
1575
1773
|
synchronize do |client|
|
@@ -1585,6 +1783,90 @@ class Redis
|
|
1585
1783
|
end
|
1586
1784
|
end
|
1587
1785
|
|
1786
|
+
# Removes and returns up to count members with the highest scores in the sorted set stored at key.
|
1787
|
+
#
|
1788
|
+
# @example Popping a member
|
1789
|
+
# redis.zpopmax('zset')
|
1790
|
+
# #=> ['b', 2.0]
|
1791
|
+
# @example With count option
|
1792
|
+
# redis.zpopmax('zset', 2)
|
1793
|
+
# #=> [['b', 2.0], ['a', 1.0]]
|
1794
|
+
#
|
1795
|
+
# @params key [String] a key of the sorted set
|
1796
|
+
# @params count [Integer] a number of members
|
1797
|
+
#
|
1798
|
+
# @return [Array<String, Float>] element and score pair if count is not specified
|
1799
|
+
# @return [Array<Array<String, Float>>] list of popped elements and scores
|
1800
|
+
def zpopmax(key, count = nil)
|
1801
|
+
synchronize do |client|
|
1802
|
+
members = client.call([:zpopmax, key, count].compact, &FloatifyPairs)
|
1803
|
+
count.to_i > 1 ? members : members.first
|
1804
|
+
end
|
1805
|
+
end
|
1806
|
+
|
1807
|
+
# Removes and returns up to count members with the lowest scores in the sorted set stored at key.
|
1808
|
+
#
|
1809
|
+
# @example Popping a member
|
1810
|
+
# redis.zpopmin('zset')
|
1811
|
+
# #=> ['a', 1.0]
|
1812
|
+
# @example With count option
|
1813
|
+
# redis.zpopmin('zset', 2)
|
1814
|
+
# #=> [['a', 1.0], ['b', 2.0]]
|
1815
|
+
#
|
1816
|
+
# @params key [String] a key of the sorted set
|
1817
|
+
# @params count [Integer] a number of members
|
1818
|
+
#
|
1819
|
+
# @return [Array<String, Float>] element and score pair if count is not specified
|
1820
|
+
# @return [Array<Array<String, Float>>] list of popped elements and scores
|
1821
|
+
def zpopmin(key, count = nil)
|
1822
|
+
synchronize do |client|
|
1823
|
+
members = client.call([:zpopmin, key, count].compact, &FloatifyPairs)
|
1824
|
+
count.to_i > 1 ? members : members.first
|
1825
|
+
end
|
1826
|
+
end
|
1827
|
+
|
1828
|
+
# Removes and returns up to count members with the highest scores in the sorted set stored at keys,
|
1829
|
+
# or block until one is available.
|
1830
|
+
#
|
1831
|
+
# @example Popping a member from a sorted set
|
1832
|
+
# redis.bzpopmax('zset', 1)
|
1833
|
+
# #=> ['zset', 'b', 2.0]
|
1834
|
+
# @example Popping a member from multiple sorted sets
|
1835
|
+
# redis.bzpopmax('zset1', 'zset2', 1)
|
1836
|
+
# #=> ['zset1', 'b', 2.0]
|
1837
|
+
#
|
1838
|
+
# @params keys [Array<String>] one or multiple keys of the sorted sets
|
1839
|
+
# @params timeout [Integer] the maximum number of seconds to block
|
1840
|
+
#
|
1841
|
+
# @return [Array<String, String, Float>] a touple of key, member and score
|
1842
|
+
# @return [nil] when no element could be popped and the timeout expired
|
1843
|
+
def bzpopmax(*args)
|
1844
|
+
_bpop(:bzpopmax, args) do |reply|
|
1845
|
+
reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
|
1846
|
+
end
|
1847
|
+
end
|
1848
|
+
|
1849
|
+
# Removes and returns up to count members with the lowest scores in the sorted set stored at keys,
|
1850
|
+
# or block until one is available.
|
1851
|
+
#
|
1852
|
+
# @example Popping a member from a sorted set
|
1853
|
+
# redis.bzpopmin('zset', 1)
|
1854
|
+
# #=> ['zset', 'a', 1.0]
|
1855
|
+
# @example Popping a member from multiple sorted sets
|
1856
|
+
# redis.bzpopmin('zset1', 'zset2', 1)
|
1857
|
+
# #=> ['zset1', 'a', 1.0]
|
1858
|
+
#
|
1859
|
+
# @params keys [Array<String>] one or multiple keys of the sorted sets
|
1860
|
+
# @params timeout [Integer] the maximum number of seconds to block
|
1861
|
+
#
|
1862
|
+
# @return [Array<String, String, Float>] a touple of key, member and score
|
1863
|
+
# @return [nil] when no element could be popped and the timeout expired
|
1864
|
+
def bzpopmin(*args)
|
1865
|
+
_bpop(:bzpopmin, args) do |reply|
|
1866
|
+
reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
|
1867
|
+
end
|
1868
|
+
end
|
1869
|
+
|
1588
1870
|
# Get the score associated with the given member in a sorted set.
|
1589
1871
|
#
|
1590
1872
|
# @example Get the score for member "a"
|
@@ -1600,6 +1882,63 @@ class Redis
|
|
1600
1882
|
end
|
1601
1883
|
end
|
1602
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
|
+
|
1603
1942
|
# Return a range of members in a sorted set, by index.
|
1604
1943
|
#
|
1605
1944
|
# @example Retrieve all members from a sorted set
|
@@ -1610,18 +1949,16 @@ class Redis
|
|
1610
1949
|
# # => [["a", 32.0], ["b", 64.0]]
|
1611
1950
|
#
|
1612
1951
|
# @param [String] key
|
1613
|
-
# @param [
|
1614
|
-
# @param [
|
1952
|
+
# @param [Integer] start start index
|
1953
|
+
# @param [Integer] stop stop index
|
1615
1954
|
# @param [Hash] options
|
1616
1955
|
# - `:with_scores => true`: include scores in output
|
1617
1956
|
#
|
1618
1957
|
# @return [Array<String>, Array<[String, Float]>]
|
1619
1958
|
# - when `:with_scores` is not specified, an array of members
|
1620
1959
|
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
1621
|
-
def zrange(key, start, stop,
|
1622
|
-
args = []
|
1623
|
-
|
1624
|
-
with_scores = options[:with_scores] || options[:withscores]
|
1960
|
+
def zrange(key, start, stop, withscores: false, with_scores: withscores)
|
1961
|
+
args = [:zrange, key, start, stop]
|
1625
1962
|
|
1626
1963
|
if with_scores
|
1627
1964
|
args << "WITHSCORES"
|
@@ -1629,7 +1966,7 @@ class Redis
|
|
1629
1966
|
end
|
1630
1967
|
|
1631
1968
|
synchronize do |client|
|
1632
|
-
client.call(
|
1969
|
+
client.call(args, &block)
|
1633
1970
|
end
|
1634
1971
|
end
|
1635
1972
|
|
@@ -1644,10 +1981,8 @@ class Redis
|
|
1644
1981
|
# # => [["b", 64.0], ["a", 32.0]]
|
1645
1982
|
#
|
1646
1983
|
# @see #zrange
|
1647
|
-
def zrevrange(key, start, stop,
|
1648
|
-
args = []
|
1649
|
-
|
1650
|
-
with_scores = options[:with_scores] || options[:withscores]
|
1984
|
+
def zrevrange(key, start, stop, withscores: false, with_scores: withscores)
|
1985
|
+
args = [:zrevrange, key, start, stop]
|
1651
1986
|
|
1652
1987
|
if with_scores
|
1653
1988
|
args << "WITHSCORES"
|
@@ -1655,7 +1990,7 @@ class Redis
|
|
1655
1990
|
end
|
1656
1991
|
|
1657
1992
|
synchronize do |client|
|
1658
|
-
client.call(
|
1993
|
+
client.call(args, &block)
|
1659
1994
|
end
|
1660
1995
|
end
|
1661
1996
|
|
@@ -1663,7 +1998,7 @@ class Redis
|
|
1663
1998
|
#
|
1664
1999
|
# @param [String] key
|
1665
2000
|
# @param [String] member
|
1666
|
-
# @return [
|
2001
|
+
# @return [Integer]
|
1667
2002
|
def zrank(key, member)
|
1668
2003
|
synchronize do |client|
|
1669
2004
|
client.call([:zrank, key, member])
|
@@ -1675,7 +2010,7 @@ class Redis
|
|
1675
2010
|
#
|
1676
2011
|
# @param [String] key
|
1677
2012
|
# @param [String] member
|
1678
|
-
# @return [
|
2013
|
+
# @return [Integer]
|
1679
2014
|
def zrevrank(key, member)
|
1680
2015
|
synchronize do |client|
|
1681
2016
|
client.call([:zrevrank, key, member])
|
@@ -1692,15 +2027,39 @@ class Redis
|
|
1692
2027
|
# # => 5
|
1693
2028
|
#
|
1694
2029
|
# @param [String] key
|
1695
|
-
# @param [
|
1696
|
-
# @param [
|
1697
|
-
# @return [
|
2030
|
+
# @param [Integer] start start index
|
2031
|
+
# @param [Integer] stop stop index
|
2032
|
+
# @return [Integer] number of members that were removed
|
1698
2033
|
def zremrangebyrank(key, start, stop)
|
1699
2034
|
synchronize do |client|
|
1700
2035
|
client.call([:zremrangebyrank, key, start, stop])
|
1701
2036
|
end
|
1702
2037
|
end
|
1703
2038
|
|
2039
|
+
# Count the members, with the same score in a sorted set, within the given lexicographical range.
|
2040
|
+
#
|
2041
|
+
# @example Count members matching a
|
2042
|
+
# redis.zlexcount("zset", "[a", "[a\xff")
|
2043
|
+
# # => 1
|
2044
|
+
# @example Count members matching a-z
|
2045
|
+
# redis.zlexcount("zset", "[a", "[z\xff")
|
2046
|
+
# # => 26
|
2047
|
+
#
|
2048
|
+
# @param [String] key
|
2049
|
+
# @param [String] min
|
2050
|
+
# - inclusive minimum is specified by prefixing `(`
|
2051
|
+
# - exclusive minimum is specified by prefixing `[`
|
2052
|
+
# @param [String] max
|
2053
|
+
# - inclusive maximum is specified by prefixing `(`
|
2054
|
+
# - exclusive maximum is specified by prefixing `[`
|
2055
|
+
#
|
2056
|
+
# @return [Integer] number of members within the specified lexicographical range
|
2057
|
+
def zlexcount(key, min, max)
|
2058
|
+
synchronize do |client|
|
2059
|
+
client.call([:zlexcount, key, min, max])
|
2060
|
+
end
|
2061
|
+
end
|
2062
|
+
|
1704
2063
|
# Return a range of members with the same score in a sorted set, by lexicographical ordering
|
1705
2064
|
#
|
1706
2065
|
# @example Retrieve members matching a
|
@@ -1722,14 +2081,16 @@ class Redis
|
|
1722
2081
|
# `count` members
|
1723
2082
|
#
|
1724
2083
|
# @return [Array<String>, Array<[String, Float]>]
|
1725
|
-
def zrangebylex(key, min, max,
|
1726
|
-
args = []
|
2084
|
+
def zrangebylex(key, min, max, limit: nil)
|
2085
|
+
args = [:zrangebylex, key, min, max]
|
1727
2086
|
|
1728
|
-
|
1729
|
-
|
2087
|
+
if limit
|
2088
|
+
args << "LIMIT"
|
2089
|
+
args.concat(limit)
|
2090
|
+
end
|
1730
2091
|
|
1731
2092
|
synchronize do |client|
|
1732
|
-
client.call(
|
2093
|
+
client.call(args)
|
1733
2094
|
end
|
1734
2095
|
end
|
1735
2096
|
|
@@ -1744,14 +2105,16 @@ class Redis
|
|
1744
2105
|
# # => ["abbygail", "abby"]
|
1745
2106
|
#
|
1746
2107
|
# @see #zrangebylex
|
1747
|
-
def zrevrangebylex(key, max, min,
|
1748
|
-
args = []
|
2108
|
+
def zrevrangebylex(key, max, min, limit: nil)
|
2109
|
+
args = [:zrevrangebylex, key, max, min]
|
1749
2110
|
|
1750
|
-
|
1751
|
-
|
2111
|
+
if limit
|
2112
|
+
args << "LIMIT"
|
2113
|
+
args.concat(limit)
|
2114
|
+
end
|
1752
2115
|
|
1753
2116
|
synchronize do |client|
|
1754
|
-
client.call(
|
2117
|
+
client.call(args)
|
1755
2118
|
end
|
1756
2119
|
end
|
1757
2120
|
|
@@ -1782,21 +2145,21 @@ class Redis
|
|
1782
2145
|
# @return [Array<String>, Array<[String, Float]>]
|
1783
2146
|
# - when `:with_scores` is not specified, an array of members
|
1784
2147
|
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
1785
|
-
def zrangebyscore(key, min, max,
|
1786
|
-
args = []
|
1787
|
-
|
1788
|
-
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]
|
1789
2150
|
|
1790
2151
|
if with_scores
|
1791
2152
|
args << "WITHSCORES"
|
1792
2153
|
block = FloatifyPairs
|
1793
2154
|
end
|
1794
2155
|
|
1795
|
-
|
1796
|
-
|
2156
|
+
if limit
|
2157
|
+
args << "LIMIT"
|
2158
|
+
args.concat(limit)
|
2159
|
+
end
|
1797
2160
|
|
1798
2161
|
synchronize do |client|
|
1799
|
-
client.call(
|
2162
|
+
client.call(args, &block)
|
1800
2163
|
end
|
1801
2164
|
end
|
1802
2165
|
|
@@ -1814,21 +2177,21 @@ class Redis
|
|
1814
2177
|
# # => [["b", 64.0], ["a", 32.0]]
|
1815
2178
|
#
|
1816
2179
|
# @see #zrangebyscore
|
1817
|
-
def zrevrangebyscore(key, max, min,
|
1818
|
-
args = []
|
1819
|
-
|
1820
|
-
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]
|
1821
2182
|
|
1822
2183
|
if with_scores
|
1823
|
-
args <<
|
2184
|
+
args << "WITHSCORES"
|
1824
2185
|
block = FloatifyPairs
|
1825
2186
|
end
|
1826
2187
|
|
1827
|
-
|
1828
|
-
|
2188
|
+
if limit
|
2189
|
+
args << "LIMIT"
|
2190
|
+
args.concat(limit)
|
2191
|
+
end
|
1829
2192
|
|
1830
2193
|
synchronize do |client|
|
1831
|
-
client.call(
|
2194
|
+
client.call(args, &block)
|
1832
2195
|
end
|
1833
2196
|
end
|
1834
2197
|
|
@@ -1848,7 +2211,7 @@ class Redis
|
|
1848
2211
|
# @param [String] max
|
1849
2212
|
# - inclusive maximum score is specified verbatim
|
1850
2213
|
# - exclusive maximum score is specified by prefixing `(`
|
1851
|
-
# @return [
|
2214
|
+
# @return [Integer] number of members that were removed
|
1852
2215
|
def zremrangebyscore(key, min, max)
|
1853
2216
|
synchronize do |client|
|
1854
2217
|
client.call([:zremrangebyscore, key, min, max])
|
@@ -1871,13 +2234,52 @@ class Redis
|
|
1871
2234
|
# @param [String] max
|
1872
2235
|
# - inclusive maximum score is specified verbatim
|
1873
2236
|
# - exclusive maximum score is specified by prefixing `(`
|
1874
|
-
# @return [
|
2237
|
+
# @return [Integer] number of members in within the specified range
|
1875
2238
|
def zcount(key, min, max)
|
1876
2239
|
synchronize do |client|
|
1877
2240
|
client.call([:zcount, key, min, max])
|
1878
2241
|
end
|
1879
2242
|
end
|
1880
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
|
+
|
1881
2283
|
# Intersect multiple sorted sets and store the resulting sorted set in a new
|
1882
2284
|
# key.
|
1883
2285
|
#
|
@@ -1891,18 +2293,19 @@ class Redis
|
|
1891
2293
|
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
1892
2294
|
# sorted sets
|
1893
2295
|
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
1894
|
-
# @return [
|
1895
|
-
def zinterstore(destination, keys,
|
1896
|
-
args = []
|
2296
|
+
# @return [Integer] number of elements in the resulting sorted set
|
2297
|
+
def zinterstore(destination, keys, weights: nil, aggregate: nil)
|
2298
|
+
args = [:zinterstore, destination, keys.size, *keys]
|
1897
2299
|
|
1898
|
-
|
1899
|
-
|
2300
|
+
if weights
|
2301
|
+
args << "WEIGHTS"
|
2302
|
+
args.concat(weights)
|
2303
|
+
end
|
1900
2304
|
|
1901
|
-
aggregate
|
1902
|
-
args.concat(["AGGREGATE", aggregate]) if aggregate
|
2305
|
+
args << "AGGREGATE" << aggregate if aggregate
|
1903
2306
|
|
1904
2307
|
synchronize do |client|
|
1905
|
-
client.call(
|
2308
|
+
client.call(args)
|
1906
2309
|
end
|
1907
2310
|
end
|
1908
2311
|
|
@@ -1918,40 +2321,46 @@ class Redis
|
|
1918
2321
|
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
1919
2322
|
# sorted sets
|
1920
2323
|
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
1921
|
-
# @return [
|
1922
|
-
def zunionstore(destination, keys,
|
1923
|
-
args = []
|
2324
|
+
# @return [Integer] number of elements in the resulting sorted set
|
2325
|
+
def zunionstore(destination, keys, weights: nil, aggregate: nil)
|
2326
|
+
args = [:zunionstore, destination, keys.size, *keys]
|
1924
2327
|
|
1925
|
-
|
1926
|
-
|
2328
|
+
if weights
|
2329
|
+
args << "WEIGHTS"
|
2330
|
+
args.concat(weights)
|
2331
|
+
end
|
1927
2332
|
|
1928
|
-
aggregate
|
1929
|
-
args.concat(["AGGREGATE", aggregate]) if aggregate
|
2333
|
+
args << "AGGREGATE" << aggregate if aggregate
|
1930
2334
|
|
1931
2335
|
synchronize do |client|
|
1932
|
-
client.call(
|
2336
|
+
client.call(args)
|
1933
2337
|
end
|
1934
2338
|
end
|
1935
2339
|
|
1936
2340
|
# Get the number of fields in a hash.
|
1937
2341
|
#
|
1938
2342
|
# @param [String] key
|
1939
|
-
# @return [
|
2343
|
+
# @return [Integer] number of fields in the hash
|
1940
2344
|
def hlen(key)
|
1941
2345
|
synchronize do |client|
|
1942
2346
|
client.call([:hlen, key])
|
1943
2347
|
end
|
1944
2348
|
end
|
1945
2349
|
|
1946
|
-
# 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
|
1947
2355
|
#
|
1948
2356
|
# @param [String] key
|
1949
|
-
# @param [String]
|
1950
|
-
# @
|
1951
|
-
|
1952
|
-
|
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
|
+
|
1953
2362
|
synchronize do |client|
|
1954
|
-
client.call([:hset, key,
|
2363
|
+
client.call([:hset, key, *attrs])
|
1955
2364
|
end
|
1956
2365
|
end
|
1957
2366
|
|
@@ -2040,7 +2449,7 @@ class Redis
|
|
2040
2449
|
# @see #hmget
|
2041
2450
|
def mapped_hmget(key, *fields)
|
2042
2451
|
hmget(key, *fields) do |reply|
|
2043
|
-
if reply.
|
2452
|
+
if reply.is_a?(Array)
|
2044
2453
|
Hash[fields.zip(reply)]
|
2045
2454
|
else
|
2046
2455
|
reply
|
@@ -2052,10 +2461,10 @@ class Redis
|
|
2052
2461
|
#
|
2053
2462
|
# @param [String] key
|
2054
2463
|
# @param [String, Array<String>] field
|
2055
|
-
# @return [
|
2056
|
-
def hdel(key,
|
2464
|
+
# @return [Integer] the number of fields that were removed from the hash
|
2465
|
+
def hdel(key, *fields)
|
2057
2466
|
synchronize do |client|
|
2058
|
-
client.call([:hdel, key,
|
2467
|
+
client.call([:hdel, key, *fields])
|
2059
2468
|
end
|
2060
2469
|
end
|
2061
2470
|
|
@@ -2074,8 +2483,8 @@ class Redis
|
|
2074
2483
|
#
|
2075
2484
|
# @param [String] key
|
2076
2485
|
# @param [String] field
|
2077
|
-
# @param [
|
2078
|
-
# @return [
|
2486
|
+
# @param [Integer] increment
|
2487
|
+
# @return [Integer] value of the field after incrementing it
|
2079
2488
|
def hincrby(key, field, increment)
|
2080
2489
|
synchronize do |client|
|
2081
2490
|
client.call([:hincrby, key, field, increment])
|
@@ -2133,20 +2542,21 @@ class Redis
|
|
2133
2542
|
|
2134
2543
|
def subscribed?
|
2135
2544
|
synchronize do |client|
|
2136
|
-
client.
|
2545
|
+
client.is_a? SubscribedClient
|
2137
2546
|
end
|
2138
2547
|
end
|
2139
2548
|
|
2140
2549
|
# Listen for messages published to the given channels.
|
2141
2550
|
def subscribe(*channels, &block)
|
2142
|
-
synchronize do |
|
2551
|
+
synchronize do |_client|
|
2143
2552
|
_subscription(:subscribe, 0, channels, block)
|
2144
2553
|
end
|
2145
2554
|
end
|
2146
2555
|
|
2147
|
-
# 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.
|
2148
2558
|
def subscribe_with_timeout(timeout, *channels, &block)
|
2149
|
-
synchronize do |
|
2559
|
+
synchronize do |_client|
|
2150
2560
|
_subscription(:subscribe_with_timeout, timeout, channels, block)
|
2151
2561
|
end
|
2152
2562
|
end
|
@@ -2154,21 +2564,23 @@ class Redis
|
|
2154
2564
|
# Stop listening for messages posted to the given channels.
|
2155
2565
|
def unsubscribe(*channels)
|
2156
2566
|
synchronize do |client|
|
2157
|
-
raise
|
2567
|
+
raise "Can't unsubscribe if not subscribed." unless subscribed?
|
2568
|
+
|
2158
2569
|
client.unsubscribe(*channels)
|
2159
2570
|
end
|
2160
2571
|
end
|
2161
2572
|
|
2162
2573
|
# Listen for messages published to channels matching the given patterns.
|
2163
2574
|
def psubscribe(*channels, &block)
|
2164
|
-
synchronize do |
|
2575
|
+
synchronize do |_client|
|
2165
2576
|
_subscription(:psubscribe, 0, channels, block)
|
2166
2577
|
end
|
2167
2578
|
end
|
2168
2579
|
|
2169
|
-
# 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.
|
2170
2582
|
def psubscribe_with_timeout(timeout, *channels, &block)
|
2171
|
-
synchronize do |
|
2583
|
+
synchronize do |_client|
|
2172
2584
|
_subscription(:psubscribe_with_timeout, timeout, channels, block)
|
2173
2585
|
end
|
2174
2586
|
end
|
@@ -2176,7 +2588,8 @@ class Redis
|
|
2176
2588
|
# Stop listening for messages posted to channels matching the given patterns.
|
2177
2589
|
def punsubscribe(*channels)
|
2178
2590
|
synchronize do |client|
|
2179
|
-
raise
|
2591
|
+
raise "Can't unsubscribe if not subscribed." unless subscribed?
|
2592
|
+
|
2180
2593
|
client.punsubscribe(*channels)
|
2181
2594
|
end
|
2182
2595
|
end
|
@@ -2221,7 +2634,7 @@ class Redis
|
|
2221
2634
|
# @see #multi
|
2222
2635
|
def watch(*keys)
|
2223
2636
|
synchronize do |client|
|
2224
|
-
res = client.call([:watch
|
2637
|
+
res = client.call([:watch, *keys])
|
2225
2638
|
|
2226
2639
|
if block_given?
|
2227
2640
|
begin
|
@@ -2251,13 +2664,13 @@ class Redis
|
|
2251
2664
|
end
|
2252
2665
|
|
2253
2666
|
def pipelined
|
2254
|
-
synchronize do |
|
2667
|
+
synchronize do |prior_client|
|
2255
2668
|
begin
|
2256
|
-
|
2669
|
+
@client = Pipeline.new(prior_client)
|
2257
2670
|
yield(self)
|
2258
|
-
|
2671
|
+
prior_client.call_pipeline(@client)
|
2259
2672
|
ensure
|
2260
|
-
@client =
|
2673
|
+
@client = prior_client
|
2261
2674
|
end
|
2262
2675
|
end
|
2263
2676
|
end
|
@@ -2293,17 +2706,16 @@ class Redis
|
|
2293
2706
|
# @see #watch
|
2294
2707
|
# @see #unwatch
|
2295
2708
|
def multi
|
2296
|
-
synchronize do |
|
2709
|
+
synchronize do |prior_client|
|
2297
2710
|
if !block_given?
|
2298
|
-
|
2711
|
+
prior_client.call([:multi])
|
2299
2712
|
else
|
2300
2713
|
begin
|
2301
|
-
|
2302
|
-
original, @client = @client, pipeline
|
2714
|
+
@client = Pipeline::Multi.new(prior_client)
|
2303
2715
|
yield(self)
|
2304
|
-
|
2716
|
+
prior_client.call_pipeline(@client)
|
2305
2717
|
ensure
|
2306
|
-
@client =
|
2718
|
+
@client = prior_client
|
2307
2719
|
end
|
2308
2720
|
end
|
2309
2721
|
end
|
@@ -2450,18 +2862,13 @@ class Redis
|
|
2450
2862
|
_eval(:evalsha, args)
|
2451
2863
|
end
|
2452
2864
|
|
2453
|
-
def _scan(command, cursor, args,
|
2865
|
+
def _scan(command, cursor, args, match: nil, count: nil, type: nil, &block)
|
2454
2866
|
# SSCAN/ZSCAN/HSCAN already prepend the key to +args+.
|
2455
2867
|
|
2456
2868
|
args << cursor
|
2457
|
-
|
2458
|
-
|
2459
|
-
|
2460
|
-
end
|
2461
|
-
|
2462
|
-
if count = options[:count]
|
2463
|
-
args.concat(["COUNT", count])
|
2464
|
-
end
|
2869
|
+
args << "MATCH" << match if match
|
2870
|
+
args << "COUNT" << count if count
|
2871
|
+
args << "TYPE" << type if type
|
2465
2872
|
|
2466
2873
|
synchronize do |client|
|
2467
2874
|
client.call([command] + args, &block)
|
@@ -2476,15 +2883,19 @@ class Redis
|
|
2476
2883
|
# @example Retrieve a batch of keys matching a pattern
|
2477
2884
|
# redis.scan(4, :match => "key:1?")
|
2478
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"]]
|
2479
2889
|
#
|
2480
2890
|
# @param [String, Integer] cursor the cursor of the iteration
|
2481
2891
|
# @param [Hash] options
|
2482
2892
|
# - `:match => String`: only return keys matching the pattern
|
2483
2893
|
# - `:count => Integer`: return count keys at most per iteration
|
2894
|
+
# - `:type => String`: return keys only of the given type
|
2484
2895
|
#
|
2485
2896
|
# @return [String, Array<String>] the next cursor and all found keys
|
2486
|
-
def scan(cursor, options
|
2487
|
-
_scan(:scan, cursor, [], options)
|
2897
|
+
def scan(cursor, **options)
|
2898
|
+
_scan(:scan, cursor, [], **options)
|
2488
2899
|
end
|
2489
2900
|
|
2490
2901
|
# Scan the keyspace
|
@@ -2496,17 +2907,23 @@ class Redis
|
|
2496
2907
|
# redis.scan_each(:match => "key:1?") {|key| puts key}
|
2497
2908
|
# # => key:13
|
2498
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"
|
2499
2914
|
#
|
2500
2915
|
# @param [Hash] options
|
2501
2916
|
# - `:match => String`: only return keys matching the pattern
|
2502
2917
|
# - `:count => Integer`: return count keys at most per iteration
|
2918
|
+
# - `:type => String`: return keys only of the given type
|
2503
2919
|
#
|
2504
2920
|
# @return [Enumerator] an enumerator for all found keys
|
2505
|
-
def scan_each(options
|
2506
|
-
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
|
+
|
2507
2924
|
cursor = 0
|
2508
2925
|
loop do
|
2509
|
-
cursor, keys = scan(cursor, options)
|
2926
|
+
cursor, keys = scan(cursor, **options)
|
2510
2927
|
keys.each(&block)
|
2511
2928
|
break if cursor == "0"
|
2512
2929
|
end
|
@@ -2523,8 +2940,8 @@ class Redis
|
|
2523
2940
|
# - `:count => Integer`: return count keys at most per iteration
|
2524
2941
|
#
|
2525
2942
|
# @return [String, Array<[String, String]>] the next cursor and all found keys
|
2526
|
-
def hscan(key, cursor, options
|
2527
|
-
_scan(:hscan, cursor, [key], options) do |reply|
|
2943
|
+
def hscan(key, cursor, **options)
|
2944
|
+
_scan(:hscan, cursor, [key], **options) do |reply|
|
2528
2945
|
[reply[0], reply[1].each_slice(2).to_a]
|
2529
2946
|
end
|
2530
2947
|
end
|
@@ -2540,11 +2957,12 @@ class Redis
|
|
2540
2957
|
# - `:count => Integer`: return count keys at most per iteration
|
2541
2958
|
#
|
2542
2959
|
# @return [Enumerator] an enumerator for all found keys
|
2543
|
-
def hscan_each(key, options
|
2544
|
-
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
|
+
|
2545
2963
|
cursor = 0
|
2546
2964
|
loop do
|
2547
|
-
cursor, values = hscan(key, cursor, options)
|
2965
|
+
cursor, values = hscan(key, cursor, **options)
|
2548
2966
|
values.each(&block)
|
2549
2967
|
break if cursor == "0"
|
2550
2968
|
end
|
@@ -2562,8 +2980,8 @@ class Redis
|
|
2562
2980
|
#
|
2563
2981
|
# @return [String, Array<[String, Float]>] the next cursor and all found
|
2564
2982
|
# members and scores
|
2565
|
-
def zscan(key, cursor, options
|
2566
|
-
_scan(:zscan, cursor, [key], options) do |reply|
|
2983
|
+
def zscan(key, cursor, **options)
|
2984
|
+
_scan(:zscan, cursor, [key], **options) do |reply|
|
2567
2985
|
[reply[0], FloatifyPairs.call(reply[1])]
|
2568
2986
|
end
|
2569
2987
|
end
|
@@ -2579,11 +2997,12 @@ class Redis
|
|
2579
2997
|
# - `:count => Integer`: return count keys at most per iteration
|
2580
2998
|
#
|
2581
2999
|
# @return [Enumerator] an enumerator for all found scores and members
|
2582
|
-
def zscan_each(key, options
|
2583
|
-
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
|
+
|
2584
3003
|
cursor = 0
|
2585
3004
|
loop do
|
2586
|
-
cursor, values = zscan(key, cursor, options)
|
3005
|
+
cursor, values = zscan(key, cursor, **options)
|
2587
3006
|
values.each(&block)
|
2588
3007
|
break if cursor == "0"
|
2589
3008
|
end
|
@@ -2600,8 +3019,8 @@ class Redis
|
|
2600
3019
|
# - `:count => Integer`: return count keys at most per iteration
|
2601
3020
|
#
|
2602
3021
|
# @return [String, Array<String>] the next cursor and all found members
|
2603
|
-
def sscan(key, cursor, options
|
2604
|
-
_scan(:sscan, cursor, [key], options)
|
3022
|
+
def sscan(key, cursor, **options)
|
3023
|
+
_scan(:sscan, cursor, [key], **options)
|
2605
3024
|
end
|
2606
3025
|
|
2607
3026
|
# Scan a set
|
@@ -2615,11 +3034,12 @@ class Redis
|
|
2615
3034
|
# - `:count => Integer`: return count keys at most per iteration
|
2616
3035
|
#
|
2617
3036
|
# @return [Enumerator] an enumerator for all keys in the set
|
2618
|
-
def sscan_each(key, options
|
2619
|
-
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
|
+
|
2620
3040
|
cursor = 0
|
2621
3041
|
loop do
|
2622
|
-
cursor, keys = sscan(key, cursor, options)
|
3042
|
+
cursor, keys = sscan(key, cursor, **options)
|
2623
3043
|
keys.each(&block)
|
2624
3044
|
break if cursor == "0"
|
2625
3045
|
end
|
@@ -2642,7 +3062,7 @@ class Redis
|
|
2642
3062
|
# union of the HyperLogLogs contained in the keys.
|
2643
3063
|
#
|
2644
3064
|
# @param [String, Array<String>] keys
|
2645
|
-
# @return [
|
3065
|
+
# @return [Integer]
|
2646
3066
|
def pfcount(*keys)
|
2647
3067
|
synchronize do |client|
|
2648
3068
|
client.call([:pfcount] + keys)
|
@@ -2661,6 +3081,445 @@ class Redis
|
|
2661
3081
|
end
|
2662
3082
|
end
|
2663
3083
|
|
3084
|
+
# Adds the specified geospatial items (latitude, longitude, name) to the specified key
|
3085
|
+
#
|
3086
|
+
# @param [String] key
|
3087
|
+
# @param [Array] member arguemnts for member or members: longitude, latitude, name
|
3088
|
+
# @return [Integer] number of elements added to the sorted set
|
3089
|
+
def geoadd(key, *member)
|
3090
|
+
synchronize do |client|
|
3091
|
+
client.call([:geoadd, key, *member])
|
3092
|
+
end
|
3093
|
+
end
|
3094
|
+
|
3095
|
+
# Returns geohash string representing position for specified members of the specified key.
|
3096
|
+
#
|
3097
|
+
# @param [String] key
|
3098
|
+
# @param [String, Array<String>] member one member or array of members
|
3099
|
+
# @return [Array<String, nil>] returns array containg geohash string if member is present, nil otherwise
|
3100
|
+
def geohash(key, member)
|
3101
|
+
synchronize do |client|
|
3102
|
+
client.call([:geohash, key, member])
|
3103
|
+
end
|
3104
|
+
end
|
3105
|
+
|
3106
|
+
# Query a sorted set representing a geospatial index to fetch members matching a
|
3107
|
+
# given maximum distance from a point
|
3108
|
+
#
|
3109
|
+
# @param [Array] args key, longitude, latitude, radius, unit(m|km|ft|mi)
|
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
|
3112
|
+
# @param [Integer] count limit the results to the first N matching items
|
3113
|
+
# @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
|
3114
|
+
# @return [Array<String>] may be changed with `options`
|
3115
|
+
|
3116
|
+
def georadius(*args, **geoptions)
|
3117
|
+
geoarguments = _geoarguments(*args, **geoptions)
|
3118
|
+
|
3119
|
+
synchronize do |client|
|
3120
|
+
client.call([:georadius, *geoarguments])
|
3121
|
+
end
|
3122
|
+
end
|
3123
|
+
|
3124
|
+
# Query a sorted set representing a geospatial index to fetch members matching a
|
3125
|
+
# given maximum distance from an already existing member
|
3126
|
+
#
|
3127
|
+
# @param [Array] args key, member, radius, unit(m|km|ft|mi)
|
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
|
3130
|
+
# @param [Integer] count limit the results to the first N matching items
|
3131
|
+
# @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
|
3132
|
+
# @return [Array<String>] may be changed with `options`
|
3133
|
+
|
3134
|
+
def georadiusbymember(*args, **geoptions)
|
3135
|
+
geoarguments = _geoarguments(*args, **geoptions)
|
3136
|
+
|
3137
|
+
synchronize do |client|
|
3138
|
+
client.call([:georadiusbymember, *geoarguments])
|
3139
|
+
end
|
3140
|
+
end
|
3141
|
+
|
3142
|
+
# Returns longitude and latitude of members of a geospatial index
|
3143
|
+
#
|
3144
|
+
# @param [String] key
|
3145
|
+
# @param [String, Array<String>] member one member or array of members
|
3146
|
+
# @return [Array<Array<String>, nil>] returns array of elements, where each
|
3147
|
+
# element is either array of longitude and latitude or nil
|
3148
|
+
def geopos(key, member)
|
3149
|
+
synchronize do |client|
|
3150
|
+
client.call([:geopos, key, member])
|
3151
|
+
end
|
3152
|
+
end
|
3153
|
+
|
3154
|
+
# Returns the distance between two members of a geospatial index
|
3155
|
+
#
|
3156
|
+
# @param [String ]key
|
3157
|
+
# @param [Array<String>] members
|
3158
|
+
# @param ['m', 'km', 'mi', 'ft'] unit
|
3159
|
+
# @return [String, nil] returns distance in spefied unit if both members present, nil otherwise.
|
3160
|
+
def geodist(key, member1, member2, unit = 'm')
|
3161
|
+
synchronize do |client|
|
3162
|
+
client.call([:geodist, key, member1, member2, unit])
|
3163
|
+
end
|
3164
|
+
end
|
3165
|
+
|
3166
|
+
# Returns the stream information each subcommand.
|
3167
|
+
#
|
3168
|
+
# @example stream
|
3169
|
+
# redis.xinfo(:stream, 'mystream')
|
3170
|
+
# @example groups
|
3171
|
+
# redis.xinfo(:groups, 'mystream')
|
3172
|
+
# @example consumers
|
3173
|
+
# redis.xinfo(:consumers, 'mystream', 'mygroup')
|
3174
|
+
#
|
3175
|
+
# @param subcommand [String] e.g. `stream` `groups` `consumers`
|
3176
|
+
# @param key [String] the stream key
|
3177
|
+
# @param group [String] the consumer group name, required if subcommand is `consumers`
|
3178
|
+
#
|
3179
|
+
# @return [Hash] information of the stream if subcommand is `stream`
|
3180
|
+
# @return [Array<Hash>] information of the consumer groups if subcommand is `groups`
|
3181
|
+
# @return [Array<Hash>] information of the consumers if subcommand is `consumers`
|
3182
|
+
def xinfo(subcommand, key, group = nil)
|
3183
|
+
args = [:xinfo, subcommand, key, group].compact
|
3184
|
+
synchronize do |client|
|
3185
|
+
client.call(args) do |reply|
|
3186
|
+
case subcommand.to_s.downcase
|
3187
|
+
when 'stream' then Hashify.call(reply)
|
3188
|
+
when 'groups', 'consumers' then reply.map { |arr| Hashify.call(arr) }
|
3189
|
+
else reply
|
3190
|
+
end
|
3191
|
+
end
|
3192
|
+
end
|
3193
|
+
end
|
3194
|
+
|
3195
|
+
# Add new entry to the stream.
|
3196
|
+
#
|
3197
|
+
# @example Without options
|
3198
|
+
# redis.xadd('mystream', f1: 'v1', f2: 'v2')
|
3199
|
+
# @example With options
|
3200
|
+
# redis.xadd('mystream', { f1: 'v1', f2: 'v2' }, id: '0-0', maxlen: 1000, approximate: true)
|
3201
|
+
#
|
3202
|
+
# @param key [String] the stream key
|
3203
|
+
# @param entry [Hash] one or multiple field-value pairs
|
3204
|
+
# @param opts [Hash] several options for `XADD` command
|
3205
|
+
#
|
3206
|
+
# @option opts [String] :id the entry id, default value is `*`, it means auto generation
|
3207
|
+
# @option opts [Integer] :maxlen max length of entries
|
3208
|
+
# @option opts [Boolean] :approximate whether to add `~` modifier of maxlen or not
|
3209
|
+
#
|
3210
|
+
# @return [String] the entry id
|
3211
|
+
def xadd(key, entry, approximate: nil, maxlen: nil, id: '*')
|
3212
|
+
args = [:xadd, key]
|
3213
|
+
if maxlen
|
3214
|
+
args << "MAXLEN"
|
3215
|
+
args << "~" if approximate
|
3216
|
+
args << maxlen
|
3217
|
+
end
|
3218
|
+
args << id
|
3219
|
+
args.concat(entry.to_a.flatten)
|
3220
|
+
synchronize { |client| client.call(args) }
|
3221
|
+
end
|
3222
|
+
|
3223
|
+
# Trims older entries of the stream if needed.
|
3224
|
+
#
|
3225
|
+
# @example Without options
|
3226
|
+
# redis.xtrim('mystream', 1000)
|
3227
|
+
# @example With options
|
3228
|
+
# redis.xtrim('mystream', 1000, approximate: true)
|
3229
|
+
#
|
3230
|
+
# @param key [String] the stream key
|
3231
|
+
# @param mexlen [Integer] max length of entries
|
3232
|
+
# @param approximate [Boolean] whether to add `~` modifier of maxlen or not
|
3233
|
+
#
|
3234
|
+
# @return [Integer] the number of entries actually deleted
|
3235
|
+
def xtrim(key, maxlen, approximate: false)
|
3236
|
+
args = [:xtrim, key, 'MAXLEN', (approximate ? '~' : nil), maxlen].compact
|
3237
|
+
synchronize { |client| client.call(args) }
|
3238
|
+
end
|
3239
|
+
|
3240
|
+
# Delete entries by entry ids.
|
3241
|
+
#
|
3242
|
+
# @example With splatted entry ids
|
3243
|
+
# redis.xdel('mystream', '0-1', '0-2')
|
3244
|
+
# @example With arrayed entry ids
|
3245
|
+
# redis.xdel('mystream', ['0-1', '0-2'])
|
3246
|
+
#
|
3247
|
+
# @param key [String] the stream key
|
3248
|
+
# @param ids [Array<String>] one or multiple entry ids
|
3249
|
+
#
|
3250
|
+
# @return [Integer] the number of entries actually deleted
|
3251
|
+
def xdel(key, *ids)
|
3252
|
+
args = [:xdel, key].concat(ids.flatten)
|
3253
|
+
synchronize { |client| client.call(args) }
|
3254
|
+
end
|
3255
|
+
|
3256
|
+
# Fetches entries of the stream in ascending order.
|
3257
|
+
#
|
3258
|
+
# @example Without options
|
3259
|
+
# redis.xrange('mystream')
|
3260
|
+
# @example With a specific start
|
3261
|
+
# redis.xrange('mystream', '0-1')
|
3262
|
+
# @example With a specific start and end
|
3263
|
+
# redis.xrange('mystream', '0-1', '0-3')
|
3264
|
+
# @example With count options
|
3265
|
+
# redis.xrange('mystream', count: 10)
|
3266
|
+
#
|
3267
|
+
# @param key [String] the stream key
|
3268
|
+
# @param start [String] first entry id of range, default value is `-`
|
3269
|
+
# @param end [String] last entry id of range, default value is `+`
|
3270
|
+
# @param count [Integer] the number of entries as limit
|
3271
|
+
#
|
3272
|
+
# @return [Array<Array<String, Hash>>] the ids and entries pairs
|
3273
|
+
def xrange(key, start = '-', range_end = '+', count: nil)
|
3274
|
+
args = [:xrange, key, start, range_end]
|
3275
|
+
args.concat(['COUNT', count]) if count
|
3276
|
+
synchronize { |client| client.call(args, &HashifyStreamEntries) }
|
3277
|
+
end
|
3278
|
+
|
3279
|
+
# Fetches entries of the stream in descending order.
|
3280
|
+
#
|
3281
|
+
# @example Without options
|
3282
|
+
# redis.xrevrange('mystream')
|
3283
|
+
# @example With a specific end
|
3284
|
+
# redis.xrevrange('mystream', '0-3')
|
3285
|
+
# @example With a specific end and start
|
3286
|
+
# redis.xrevrange('mystream', '0-3', '0-1')
|
3287
|
+
# @example With count options
|
3288
|
+
# redis.xrevrange('mystream', count: 10)
|
3289
|
+
#
|
3290
|
+
# @param key [String] the stream key
|
3291
|
+
# @param end [String] first entry id of range, default value is `+`
|
3292
|
+
# @param start [String] last entry id of range, default value is `-`
|
3293
|
+
# @params count [Integer] the number of entries as limit
|
3294
|
+
#
|
3295
|
+
# @return [Array<Array<String, Hash>>] the ids and entries pairs
|
3296
|
+
def xrevrange(key, range_end = '+', start = '-', count: nil)
|
3297
|
+
args = [:xrevrange, key, range_end, start]
|
3298
|
+
args.concat(['COUNT', count]) if count
|
3299
|
+
synchronize { |client| client.call(args, &HashifyStreamEntries) }
|
3300
|
+
end
|
3301
|
+
|
3302
|
+
# Returns the number of entries inside a stream.
|
3303
|
+
#
|
3304
|
+
# @example With key
|
3305
|
+
# redis.xlen('mystream')
|
3306
|
+
#
|
3307
|
+
# @param key [String] the stream key
|
3308
|
+
#
|
3309
|
+
# @return [Integer] the number of entries
|
3310
|
+
def xlen(key)
|
3311
|
+
synchronize { |client| client.call([:xlen, key]) }
|
3312
|
+
end
|
3313
|
+
|
3314
|
+
# Fetches entries from one or multiple streams. Optionally blocking.
|
3315
|
+
#
|
3316
|
+
# @example With a key
|
3317
|
+
# redis.xread('mystream', '0-0')
|
3318
|
+
# @example With multiple keys
|
3319
|
+
# redis.xread(%w[mystream1 mystream2], %w[0-0 0-0])
|
3320
|
+
# @example With count option
|
3321
|
+
# redis.xread('mystream', '0-0', count: 2)
|
3322
|
+
# @example With block option
|
3323
|
+
# redis.xread('mystream', '$', block: 1000)
|
3324
|
+
#
|
3325
|
+
# @param keys [Array<String>] one or multiple stream keys
|
3326
|
+
# @param ids [Array<String>] one or multiple entry ids
|
3327
|
+
# @param count [Integer] the number of entries as limit per stream
|
3328
|
+
# @param block [Integer] the number of milliseconds as blocking timeout
|
3329
|
+
#
|
3330
|
+
# @return [Hash{String => Hash{String => Hash}}] the entries
|
3331
|
+
def xread(keys, ids, count: nil, block: nil)
|
3332
|
+
args = [:xread]
|
3333
|
+
args << 'COUNT' << count if count
|
3334
|
+
args << 'BLOCK' << block.to_i if block
|
3335
|
+
_xread(args, keys, ids, block)
|
3336
|
+
end
|
3337
|
+
|
3338
|
+
# Manages the consumer group of the stream.
|
3339
|
+
#
|
3340
|
+
# @example With `create` subcommand
|
3341
|
+
# redis.xgroup(:create, 'mystream', 'mygroup', '$')
|
3342
|
+
# @example With `setid` subcommand
|
3343
|
+
# redis.xgroup(:setid, 'mystream', 'mygroup', '$')
|
3344
|
+
# @example With `destroy` subcommand
|
3345
|
+
# redis.xgroup(:destroy, 'mystream', 'mygroup')
|
3346
|
+
# @example With `delconsumer` subcommand
|
3347
|
+
# redis.xgroup(:delconsumer, 'mystream', 'mygroup', 'consumer1')
|
3348
|
+
#
|
3349
|
+
# @param subcommand [String] `create` `setid` `destroy` `delconsumer`
|
3350
|
+
# @param key [String] the stream key
|
3351
|
+
# @param group [String] the consumer group name
|
3352
|
+
# @param id_or_consumer [String]
|
3353
|
+
# * the entry id or `$`, required if subcommand is `create` or `setid`
|
3354
|
+
# * the consumer name, required if subcommand is `delconsumer`
|
3355
|
+
# @param mkstream [Boolean] whether to create an empty stream automatically or not
|
3356
|
+
#
|
3357
|
+
# @return [String] `OK` if subcommand is `create` or `setid`
|
3358
|
+
# @return [Integer] effected count if subcommand is `destroy` or `delconsumer`
|
3359
|
+
def xgroup(subcommand, key, group, id_or_consumer = nil, mkstream: false)
|
3360
|
+
args = [:xgroup, subcommand, key, group, id_or_consumer, (mkstream ? 'MKSTREAM' : nil)].compact
|
3361
|
+
synchronize { |client| client.call(args) }
|
3362
|
+
end
|
3363
|
+
|
3364
|
+
# Fetches a subset of the entries from one or multiple streams related with the consumer group.
|
3365
|
+
# Optionally blocking.
|
3366
|
+
#
|
3367
|
+
# @example With a key
|
3368
|
+
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>')
|
3369
|
+
# @example With multiple keys
|
3370
|
+
# redis.xreadgroup('mygroup', 'consumer1', %w[mystream1 mystream2], %w[> >])
|
3371
|
+
# @example With count option
|
3372
|
+
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', count: 2)
|
3373
|
+
# @example With block option
|
3374
|
+
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', block: 1000)
|
3375
|
+
# @example With noack option
|
3376
|
+
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', noack: true)
|
3377
|
+
#
|
3378
|
+
# @param group [String] the consumer group name
|
3379
|
+
# @param consumer [String] the consumer name
|
3380
|
+
# @param keys [Array<String>] one or multiple stream keys
|
3381
|
+
# @param ids [Array<String>] one or multiple entry ids
|
3382
|
+
# @param opts [Hash] several options for `XREADGROUP` command
|
3383
|
+
#
|
3384
|
+
# @option opts [Integer] :count the number of entries as limit
|
3385
|
+
# @option opts [Integer] :block the number of milliseconds as blocking timeout
|
3386
|
+
# @option opts [Boolean] :noack whether message loss is acceptable or not
|
3387
|
+
#
|
3388
|
+
# @return [Hash{String => Hash{String => Hash}}] the entries
|
3389
|
+
def xreadgroup(group, consumer, keys, ids, count: nil, block: nil, noack: nil)
|
3390
|
+
args = [:xreadgroup, 'GROUP', group, consumer]
|
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)
|
3395
|
+
end
|
3396
|
+
|
3397
|
+
# Removes one or multiple entries from the pending entries list of a stream consumer group.
|
3398
|
+
#
|
3399
|
+
# @example With a entry id
|
3400
|
+
# redis.xack('mystream', 'mygroup', '1526569495631-0')
|
3401
|
+
# @example With splatted entry ids
|
3402
|
+
# redis.xack('mystream', 'mygroup', '0-1', '0-2')
|
3403
|
+
# @example With arrayed entry ids
|
3404
|
+
# redis.xack('mystream', 'mygroup', %w[0-1 0-2])
|
3405
|
+
#
|
3406
|
+
# @param key [String] the stream key
|
3407
|
+
# @param group [String] the consumer group name
|
3408
|
+
# @param ids [Array<String>] one or multiple entry ids
|
3409
|
+
#
|
3410
|
+
# @return [Integer] the number of entries successfully acknowledged
|
3411
|
+
def xack(key, group, *ids)
|
3412
|
+
args = [:xack, key, group].concat(ids.flatten)
|
3413
|
+
synchronize { |client| client.call(args) }
|
3414
|
+
end
|
3415
|
+
|
3416
|
+
# Changes the ownership of a pending entry
|
3417
|
+
#
|
3418
|
+
# @example With splatted entry ids
|
3419
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-1', '0-2')
|
3420
|
+
# @example With arrayed entry ids
|
3421
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2])
|
3422
|
+
# @example With idle option
|
3423
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], idle: 1000)
|
3424
|
+
# @example With time option
|
3425
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], time: 1542866959000)
|
3426
|
+
# @example With retrycount option
|
3427
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], retrycount: 10)
|
3428
|
+
# @example With force option
|
3429
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], force: true)
|
3430
|
+
# @example With justid option
|
3431
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], justid: true)
|
3432
|
+
#
|
3433
|
+
# @param key [String] the stream key
|
3434
|
+
# @param group [String] the consumer group name
|
3435
|
+
# @param consumer [String] the consumer name
|
3436
|
+
# @param min_idle_time [Integer] the number of milliseconds
|
3437
|
+
# @param ids [Array<String>] one or multiple entry ids
|
3438
|
+
# @param opts [Hash] several options for `XCLAIM` command
|
3439
|
+
#
|
3440
|
+
# @option opts [Integer] :idle the number of milliseconds as last time it was delivered of the entry
|
3441
|
+
# @option opts [Integer] :time the number of milliseconds as a specific Unix Epoch time
|
3442
|
+
# @option opts [Integer] :retrycount the number of retry counter
|
3443
|
+
# @option opts [Boolean] :force whether to create the pending entry to the pending entries list or not
|
3444
|
+
# @option opts [Boolean] :justid whether to fetch just an array of entry ids or not
|
3445
|
+
#
|
3446
|
+
# @return [Hash{String => Hash}] the entries successfully claimed
|
3447
|
+
# @return [Array<String>] the entry ids successfully claimed if justid option is `true`
|
3448
|
+
def xclaim(key, group, consumer, min_idle_time, *ids, **opts)
|
3449
|
+
args = [:xclaim, key, group, consumer, min_idle_time].concat(ids.flatten)
|
3450
|
+
args.concat(['IDLE', opts[:idle].to_i]) if opts[:idle]
|
3451
|
+
args.concat(['TIME', opts[:time].to_i]) if opts[:time]
|
3452
|
+
args.concat(['RETRYCOUNT', opts[:retrycount]]) if opts[:retrycount]
|
3453
|
+
args << 'FORCE' if opts[:force]
|
3454
|
+
args << 'JUSTID' if opts[:justid]
|
3455
|
+
blk = opts[:justid] ? Noop : HashifyStreamEntries
|
3456
|
+
synchronize { |client| client.call(args, &blk) }
|
3457
|
+
end
|
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
|
+
|
3491
|
+
# Fetches not acknowledging pending entries
|
3492
|
+
#
|
3493
|
+
# @example With key and group
|
3494
|
+
# redis.xpending('mystream', 'mygroup')
|
3495
|
+
# @example With range options
|
3496
|
+
# redis.xpending('mystream', 'mygroup', '-', '+', 10)
|
3497
|
+
# @example With range and consumer options
|
3498
|
+
# redis.xpending('mystream', 'mygroup', '-', '+', 10, 'consumer1')
|
3499
|
+
#
|
3500
|
+
# @param key [String] the stream key
|
3501
|
+
# @param group [String] the consumer group name
|
3502
|
+
# @param start [String] start first entry id of range
|
3503
|
+
# @param end [String] end last entry id of range
|
3504
|
+
# @param count [Integer] count the number of entries as limit
|
3505
|
+
# @param consumer [String] the consumer name
|
3506
|
+
#
|
3507
|
+
# @return [Hash] the summary of pending entries
|
3508
|
+
# @return [Array<Hash>] the pending entries details if options were specified
|
3509
|
+
def xpending(key, group, *args)
|
3510
|
+
command_args = [:xpending, key, group]
|
3511
|
+
case args.size
|
3512
|
+
when 0, 3, 4
|
3513
|
+
command_args.concat(args)
|
3514
|
+
else
|
3515
|
+
raise ArgumentError, "wrong number of arguments (given #{args.size + 2}, expected 2, 5 or 6)"
|
3516
|
+
end
|
3517
|
+
|
3518
|
+
summary_needed = args.empty?
|
3519
|
+
blk = summary_needed ? HashifyStreamPendings : HashifyStreamPendingDetails
|
3520
|
+
synchronize { |client| client.call(command_args, &blk) }
|
3521
|
+
end
|
3522
|
+
|
2664
3523
|
# Interact with the sentinel command (masters, master, slaves, failover)
|
2665
3524
|
#
|
2666
3525
|
# @param [String] subcommand e.g. `masters`, `master`, `slaves`
|
@@ -2674,8 +3533,8 @@ class Redis
|
|
2674
3533
|
when "get-master-addr-by-name"
|
2675
3534
|
reply
|
2676
3535
|
else
|
2677
|
-
if reply.
|
2678
|
-
if reply[0].
|
3536
|
+
if reply.is_a?(Array)
|
3537
|
+
if reply[0].is_a?(Array)
|
2679
3538
|
reply.map(&Hashify)
|
2680
3539
|
else
|
2681
3540
|
Hashify.call(reply)
|
@@ -2688,6 +3547,46 @@ class Redis
|
|
2688
3547
|
end
|
2689
3548
|
end
|
2690
3549
|
|
3550
|
+
# Sends `CLUSTER *` command to random node and returns its reply.
|
3551
|
+
#
|
3552
|
+
# @see https://redis.io/commands#cluster Reference of cluster command
|
3553
|
+
#
|
3554
|
+
# @param subcommand [String, Symbol] the subcommand of cluster command
|
3555
|
+
# e.g. `:slots`, `:nodes`, `:slaves`, `:info`
|
3556
|
+
#
|
3557
|
+
# @return [Object] depends on the subcommand
|
3558
|
+
def cluster(subcommand, *args)
|
3559
|
+
subcommand = subcommand.to_s.downcase
|
3560
|
+
block = case subcommand
|
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
|
3572
|
+
|
3573
|
+
# @see https://github.com/antirez/redis/blob/unstable/src/redis-trib.rb#L127 raw reply expected
|
3574
|
+
block = Noop unless @cluster_mode
|
3575
|
+
|
3576
|
+
synchronize do |client|
|
3577
|
+
client.call([:cluster, subcommand] + args, &block)
|
3578
|
+
end
|
3579
|
+
end
|
3580
|
+
|
3581
|
+
# Sends `ASKING` command to random node and returns its reply.
|
3582
|
+
#
|
3583
|
+
# @see https://redis.io/topics/cluster-spec#ask-redirection ASK redirection
|
3584
|
+
#
|
3585
|
+
# @return [String] `'OK'`
|
3586
|
+
def asking
|
3587
|
+
synchronize { |client| client.call(%i[asking]) }
|
3588
|
+
end
|
3589
|
+
|
2691
3590
|
def id
|
2692
3591
|
@original_client.id
|
2693
3592
|
end
|
@@ -2701,68 +3600,183 @@ class Redis
|
|
2701
3600
|
end
|
2702
3601
|
|
2703
3602
|
def connection
|
3603
|
+
return @original_client.connection_info if @cluster_mode
|
3604
|
+
|
2704
3605
|
{
|
2705
|
-
:
|
2706
|
-
:
|
2707
|
-
:
|
2708
|
-
:
|
2709
|
-
:
|
3606
|
+
host: @original_client.host,
|
3607
|
+
port: @original_client.port,
|
3608
|
+
db: @original_client.db,
|
3609
|
+
id: @original_client.id,
|
3610
|
+
location: @original_client.location
|
2710
3611
|
}
|
2711
3612
|
end
|
2712
3613
|
|
2713
|
-
def method_missing(command, *args)
|
3614
|
+
def method_missing(command, *args) # rubocop:disable Style/MissingRespondToMissing
|
2714
3615
|
synchronize do |client|
|
2715
3616
|
client.call([command] + args)
|
2716
3617
|
end
|
2717
3618
|
end
|
2718
3619
|
|
2719
|
-
private
|
3620
|
+
private
|
2720
3621
|
|
2721
3622
|
# Commands returning 1 for true and 0 for false may be executed in a pipeline
|
2722
3623
|
# where the method call will return nil. Propagate the nil instead of falsely
|
2723
3624
|
# returning false.
|
2724
|
-
Boolify =
|
2725
|
-
|
2726
|
-
|
2727
|
-
|
3625
|
+
Boolify = lambda { |value|
|
3626
|
+
case value
|
3627
|
+
when 1
|
3628
|
+
true
|
3629
|
+
when 0
|
3630
|
+
false
|
3631
|
+
else
|
3632
|
+
value
|
3633
|
+
end
|
3634
|
+
}
|
2728
3635
|
|
2729
|
-
BoolifySet =
|
2730
|
-
|
2731
|
-
|
2732
|
-
|
2733
|
-
|
2734
|
-
|
2735
|
-
|
3636
|
+
BoolifySet = lambda { |value|
|
3637
|
+
case value
|
3638
|
+
when "OK"
|
3639
|
+
true
|
3640
|
+
when nil
|
3641
|
+
false
|
3642
|
+
else
|
3643
|
+
value
|
3644
|
+
end
|
3645
|
+
}
|
3646
|
+
|
3647
|
+
Hashify = lambda { |value|
|
3648
|
+
if value.respond_to?(:each_slice)
|
3649
|
+
value.each_slice(2).to_h
|
3650
|
+
else
|
3651
|
+
value
|
3652
|
+
end
|
3653
|
+
}
|
3654
|
+
|
3655
|
+
Floatify = lambda { |value|
|
3656
|
+
case value
|
3657
|
+
when "inf"
|
3658
|
+
Float::INFINITY
|
3659
|
+
when "-inf"
|
3660
|
+
-Float::INFINITY
|
3661
|
+
when String
|
3662
|
+
Float(value)
|
3663
|
+
else
|
3664
|
+
value
|
3665
|
+
end
|
3666
|
+
}
|
3667
|
+
|
3668
|
+
FloatifyPairs = lambda { |value|
|
3669
|
+
return value unless value.respond_to?(:each_slice)
|
3670
|
+
|
3671
|
+
value.each_slice(2).map do |member, score|
|
3672
|
+
[member, Floatify.call(score)]
|
3673
|
+
end
|
3674
|
+
}
|
3675
|
+
|
3676
|
+
HashifyInfo = lambda { |reply|
|
3677
|
+
lines = reply.split("\r\n").grep_v(/^(#|$)/)
|
3678
|
+
lines.map! { |line| line.split(':', 2) }
|
3679
|
+
lines.compact!
|
3680
|
+
lines.to_h
|
3681
|
+
}
|
3682
|
+
|
3683
|
+
HashifyStreams = lambda { |reply|
|
3684
|
+
case reply
|
3685
|
+
when nil
|
3686
|
+
{}
|
3687
|
+
else
|
3688
|
+
reply.map { |key, entries| [key, HashifyStreamEntries.call(entries)] }.to_h
|
3689
|
+
end
|
3690
|
+
}
|
3691
|
+
|
3692
|
+
EMPTY_STREAM_RESPONSE = [nil].freeze
|
3693
|
+
private_constant :EMPTY_STREAM_RESPONSE
|
3694
|
+
|
3695
|
+
HashifyStreamEntries = lambda { |reply|
|
3696
|
+
reply.compact.map do |entry_id, values|
|
3697
|
+
[entry_id, values&.each_slice(2)&.to_h]
|
3698
|
+
end
|
3699
|
+
}
|
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] }
|
2736
3705
|
}
|
3706
|
+
}
|
2737
3707
|
|
2738
|
-
|
2739
|
-
|
2740
|
-
|
2741
|
-
|
2742
|
-
hash[field] = value
|
2743
|
-
end
|
2744
|
-
hash
|
3708
|
+
HashifyStreamAutoclaimJustId = lambda { |reply|
|
3709
|
+
{
|
3710
|
+
'next' => reply[0],
|
3711
|
+
'entries' => reply[1]
|
2745
3712
|
}
|
3713
|
+
}
|
2746
3714
|
|
2747
|
-
|
2748
|
-
|
2749
|
-
|
2750
|
-
|
2751
|
-
|
2752
|
-
|
2753
|
-
Float(str)
|
2754
|
-
end
|
2755
|
-
end
|
3715
|
+
HashifyStreamPendings = lambda { |reply|
|
3716
|
+
{
|
3717
|
+
'size' => reply[0],
|
3718
|
+
'min_entry_id' => reply[1],
|
3719
|
+
'max_entry_id' => reply[2],
|
3720
|
+
'consumers' => reply[3].nil? ? {} : reply[3].to_h
|
2756
3721
|
}
|
3722
|
+
}
|
2757
3723
|
|
2758
|
-
|
2759
|
-
|
2760
|
-
|
2761
|
-
|
2762
|
-
|
2763
|
-
|
2764
|
-
|
3724
|
+
HashifyStreamPendingDetails = lambda { |reply|
|
3725
|
+
reply.map do |arr|
|
3726
|
+
{
|
3727
|
+
'entry_id' => arr[0],
|
3728
|
+
'consumer' => arr[1],
|
3729
|
+
'elapsed' => arr[2],
|
3730
|
+
'count' => arr[3]
|
3731
|
+
}
|
3732
|
+
end
|
3733
|
+
}
|
3734
|
+
|
3735
|
+
HashifyClusterNodeInfo = lambda { |str|
|
3736
|
+
arr = str.split(' ')
|
3737
|
+
{
|
3738
|
+
'node_id' => arr[0],
|
3739
|
+
'ip_port' => arr[1],
|
3740
|
+
'flags' => arr[2].split(','),
|
3741
|
+
'master_node_id' => arr[3],
|
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('-'))
|
2765
3747
|
}
|
3748
|
+
}
|
3749
|
+
|
3750
|
+
HashifyClusterSlots = lambda { |reply|
|
3751
|
+
reply.map do |arr|
|
3752
|
+
first_slot, last_slot = arr[0..1]
|
3753
|
+
master = { 'ip' => arr[2][0], 'port' => arr[2][1], 'node_id' => arr[2][2] }
|
3754
|
+
replicas = arr[3..-1].map { |r| { 'ip' => r[0], 'port' => r[1], 'node_id' => r[2] } }
|
3755
|
+
{
|
3756
|
+
'start_slot' => first_slot,
|
3757
|
+
'end_slot' => last_slot,
|
3758
|
+
'master' => master,
|
3759
|
+
'replicas' => replicas
|
3760
|
+
}
|
3761
|
+
end
|
3762
|
+
}
|
3763
|
+
|
3764
|
+
HashifyClusterNodes = lambda { |reply|
|
3765
|
+
reply.split(/[\r\n]+/).map { |str| HashifyClusterNodeInfo.call(str) }
|
3766
|
+
}
|
3767
|
+
|
3768
|
+
HashifyClusterSlaves = lambda { |reply|
|
3769
|
+
reply.map { |str| HashifyClusterNodeInfo.call(str) }
|
3770
|
+
}
|
3771
|
+
|
3772
|
+
Noop = ->(reply) { reply }
|
3773
|
+
|
3774
|
+
def _geoarguments(*args, options: nil, sort: nil, count: nil)
|
3775
|
+
args.push sort if sort
|
3776
|
+
args.push 'count', count if count
|
3777
|
+
args.push options if options
|
3778
|
+
args
|
3779
|
+
end
|
2766
3780
|
|
2767
3781
|
def _subscription(method, timeout, channels, block)
|
2768
3782
|
return @client.call([method] + channels) if subscribed?
|
@@ -2779,10 +3793,44 @@ private
|
|
2779
3793
|
end
|
2780
3794
|
end
|
2781
3795
|
|
3796
|
+
def _xread(args, keys, ids, blocking_timeout_msec)
|
3797
|
+
keys = keys.is_a?(Array) ? keys : [keys]
|
3798
|
+
ids = ids.is_a?(Array) ? ids : [ids]
|
3799
|
+
args << 'STREAMS'
|
3800
|
+
args.concat(keys)
|
3801
|
+
args.concat(ids)
|
3802
|
+
|
3803
|
+
synchronize do |client|
|
3804
|
+
if blocking_timeout_msec.nil?
|
3805
|
+
client.call(args, &HashifyStreams)
|
3806
|
+
elsif blocking_timeout_msec.to_f.zero?
|
3807
|
+
client.call_without_timeout(args, &HashifyStreams)
|
3808
|
+
else
|
3809
|
+
timeout = client.timeout.to_f + blocking_timeout_msec.to_f / 1000.0
|
3810
|
+
client.call_with_timeout(args, timeout, &HashifyStreams)
|
3811
|
+
end
|
3812
|
+
end
|
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
|
2782
3829
|
end
|
2783
3830
|
|
2784
|
-
|
2785
|
-
|
2786
|
-
|
2787
|
-
|
2788
|
-
|
3831
|
+
require_relative "redis/version"
|
3832
|
+
require_relative "redis/connection"
|
3833
|
+
require_relative "redis/client"
|
3834
|
+
require_relative "redis/cluster"
|
3835
|
+
require_relative "redis/pipeline"
|
3836
|
+
require_relative "redis/subscribe"
|