redis 3.3.5 → 4.3.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 +132 -2
- data/README.md +144 -79
- data/lib/redis.rb +1174 -405
- data/lib/redis/client.rb +150 -90
- data/lib/redis/cluster.rb +295 -0
- data/lib/redis/cluster/command.rb +81 -0
- data/lib/redis/cluster/command_loader.rb +34 -0
- data/lib/redis/cluster/key_slot_converter.rb +72 -0
- data/lib/redis/cluster/node.rb +107 -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/connection.rb +4 -2
- data/lib/redis/connection/command_helper.rb +5 -10
- data/lib/redis/connection/hiredis.rb +6 -5
- data/lib/redis/connection/registry.rb +2 -1
- data/lib/redis/connection/ruby.rb +126 -128
- data/lib/redis/connection/synchrony.rb +21 -8
- data/lib/redis/distributed.rb +147 -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
- metadata +49 -202
- data/.gitignore +0 -16
- data/.travis.yml +0 -89
- data/.travis/Gemfile +0 -11
- 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.rb +0 -41
- data/examples/sentinel/sentinel.conf +0 -9
- data/examples/sentinel/start +0 -49
- 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,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "monitor"
|
2
|
-
|
4
|
+
require_relative "redis/errors"
|
3
5
|
|
4
6
|
class Redis
|
7
|
+
class << self
|
8
|
+
attr_reader :exists_returns_integer
|
5
9
|
|
6
|
-
|
7
|
-
|
8
|
-
|
10
|
+
def exists_returns_integer=(value)
|
11
|
+
unless value
|
12
|
+
message = "`Redis#exists(key)` will return an Integer by default in redis-rb 4.3. The option to explicitly " \
|
13
|
+
"disable this behaviour via `Redis.exists_returns_integer` will be removed in 5.0. You should use " \
|
14
|
+
"`exists?` instead."
|
9
15
|
|
10
|
-
|
16
|
+
::Kernel.warn(message)
|
17
|
+
end
|
18
|
+
|
19
|
+
@exists_returns_integer = value
|
20
|
+
end
|
11
21
|
|
12
|
-
|
13
|
-
# This method does not actually establish a connection to Redis,
|
14
|
-
# in contrary to what you might expect.
|
15
|
-
def self.connect(options = {})
|
16
|
-
new(options)
|
22
|
+
attr_writer :current
|
17
23
|
end
|
18
24
|
|
19
25
|
def self.current
|
20
26
|
@current ||= Redis.new
|
21
27
|
end
|
22
28
|
|
23
|
-
def self.current=(redis)
|
24
|
-
@current = redis
|
25
|
-
end
|
26
|
-
|
27
29
|
include MonitorMixin
|
28
30
|
|
29
31
|
# Create a new client instance
|
30
32
|
#
|
31
33
|
# @param [Hash] options
|
32
|
-
# @option options [String] :url (value of the environment variable REDIS_URL) a Redis URL, for a TCP connection:
|
34
|
+
# @option options [String] :url (value of the environment variable REDIS_URL) a Redis URL, for a TCP connection:
|
35
|
+
# `redis://:[password]@[hostname]:[port]/[db]` (password, port and database are optional), for a unix socket
|
36
|
+
# connection: `unix://[path to Redis socket]`. This overrides all other options.
|
33
37
|
# @option options [String] :host ("127.0.0.1") server hostname
|
34
|
-
# @option options [
|
38
|
+
# @option options [Integer] :port (6379) server port
|
35
39
|
# @option options [String] :path path to server socket (overrides host and port)
|
36
40
|
# @option options [Float] :timeout (5.0) timeout in seconds
|
37
41
|
# @option options [Float] :connect_timeout (same as timeout) timeout for initial connect in seconds
|
42
|
+
# @option options [String] :username Username to authenticate against server
|
38
43
|
# @option options [String] :password Password to authenticate against server
|
39
|
-
# @option options [
|
44
|
+
# @option options [Integer] :db (0) Database to select after initial connect
|
40
45
|
# @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 [
|
46
|
+
# @option options [String] :id ID for the client connection, assigns name to current connection by sending
|
47
|
+
# `CLIENT SETNAME`
|
48
|
+
# @option options [Hash, Integer] :tcp_keepalive Keepalive values, if Integer `intvl` and `probe` are calculated
|
49
|
+
# based on the value, if Hash `time`, `intvl` and `probes` can be specified as a Integer
|
50
|
+
# @option options [Integer] :reconnect_attempts Number of attempts trying to connect
|
44
51
|
# @option options [Boolean] :inherit_socket (false) Whether to use socket in forked process or not
|
45
52
|
# @option options [Array] :sentinels List of sentinels to contact
|
46
53
|
# @option options [Symbol] :role (:master) Role to fetch via Sentinel, either `:master` or `:slave`
|
54
|
+
# @option options [Array<String, Hash{Symbol => String, Integer}>] :cluster List of cluster nodes to contact
|
55
|
+
# @option options [Boolean] :replica Whether to use readonly replica nodes in Redis Cluster or not
|
56
|
+
# @option options [Class] :connector Class of custom connector
|
47
57
|
#
|
48
58
|
# @return [Redis] a new client instance
|
49
59
|
def initialize(options = {})
|
50
60
|
@options = options.dup
|
51
|
-
@
|
61
|
+
@cluster_mode = options.key?(:cluster)
|
62
|
+
client = @cluster_mode ? Cluster : Client
|
63
|
+
@original_client = @client = client.new(options)
|
52
64
|
@queue = Hash.new { |h, k| h[k] = [] }
|
53
65
|
|
54
66
|
super() # Monitor#initialize
|
@@ -59,7 +71,7 @@ class Redis
|
|
59
71
|
end
|
60
72
|
|
61
73
|
# Run code with the client reconnecting
|
62
|
-
def with_reconnect(val=true, &blk)
|
74
|
+
def with_reconnect(val = true, &blk)
|
63
75
|
synchronize do |client|
|
64
76
|
client.with_reconnect(val, &blk)
|
65
77
|
end
|
@@ -102,7 +114,9 @@ class Redis
|
|
102
114
|
# See http://redis.io/topics/pipelining for more details.
|
103
115
|
#
|
104
116
|
def queue(*command)
|
105
|
-
|
117
|
+
synchronize do
|
118
|
+
@queue[Thread.current.object_id] << command
|
119
|
+
end
|
106
120
|
end
|
107
121
|
|
108
122
|
# Sends all commands in the queue.
|
@@ -112,27 +126,37 @@ class Redis
|
|
112
126
|
def commit
|
113
127
|
synchronize do |client|
|
114
128
|
begin
|
115
|
-
|
129
|
+
pipeline = Pipeline.new(client)
|
130
|
+
@queue[Thread.current.object_id].each do |command|
|
131
|
+
pipeline.call(command)
|
132
|
+
end
|
133
|
+
|
134
|
+
client.call_pipelined(pipeline)
|
116
135
|
ensure
|
117
136
|
@queue.delete(Thread.current.object_id)
|
118
137
|
end
|
119
138
|
end
|
120
139
|
end
|
121
140
|
|
141
|
+
def _client
|
142
|
+
@client
|
143
|
+
end
|
144
|
+
|
122
145
|
# Authenticate to the server.
|
123
146
|
#
|
124
|
-
# @param [String]
|
125
|
-
#
|
147
|
+
# @param [Array<String>] args includes both username and password
|
148
|
+
# or only password
|
126
149
|
# @return [String] `OK`
|
127
|
-
|
150
|
+
# @see https://redis.io/commands/auth AUTH command
|
151
|
+
def auth(*args)
|
128
152
|
synchronize do |client|
|
129
|
-
client.call([:auth,
|
153
|
+
client.call([:auth, *args])
|
130
154
|
end
|
131
155
|
end
|
132
156
|
|
133
157
|
# Change the selected database for the current connection.
|
134
158
|
#
|
135
|
-
# @param [
|
159
|
+
# @param [Integer] db zero-based index of the DB to use (0 to 15)
|
136
160
|
# @return [String] `OK`
|
137
161
|
def select(db)
|
138
162
|
synchronize do |client|
|
@@ -143,10 +167,11 @@ class Redis
|
|
143
167
|
|
144
168
|
# Ping the server.
|
145
169
|
#
|
170
|
+
# @param [optional, String] message
|
146
171
|
# @return [String] `PONG`
|
147
|
-
def ping
|
172
|
+
def ping(message = nil)
|
148
173
|
synchronize do |client|
|
149
|
-
client.call([:ping])
|
174
|
+
client.call([:ping, message].compact)
|
150
175
|
end
|
151
176
|
end
|
152
177
|
|
@@ -200,7 +225,7 @@ class Redis
|
|
200
225
|
def config(action, *args)
|
201
226
|
synchronize do |client|
|
202
227
|
client.call([:config, action] + args) do |reply|
|
203
|
-
if reply.
|
228
|
+
if reply.is_a?(Array) && action == :get
|
204
229
|
Hashify.call(reply)
|
205
230
|
else
|
206
231
|
reply
|
@@ -209,9 +234,28 @@ class Redis
|
|
209
234
|
end
|
210
235
|
end
|
211
236
|
|
237
|
+
# Manage client connections.
|
238
|
+
#
|
239
|
+
# @param [String, Symbol] subcommand e.g. `kill`, `list`, `getname`, `setname`
|
240
|
+
# @return [String, Hash] depends on subcommand
|
241
|
+
def client(subcommand = nil, *args)
|
242
|
+
synchronize do |client|
|
243
|
+
client.call([:client, subcommand] + args) do |reply|
|
244
|
+
if subcommand.to_s == "list"
|
245
|
+
reply.lines.map do |line|
|
246
|
+
entries = line.chomp.split(/[ =]/)
|
247
|
+
Hash[entries.each_slice(2).to_a]
|
248
|
+
end
|
249
|
+
else
|
250
|
+
reply
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
212
256
|
# Return the number of keys in the selected database.
|
213
257
|
#
|
214
|
-
# @return [
|
258
|
+
# @return [Integer]
|
215
259
|
def dbsize
|
216
260
|
synchronize do |client|
|
217
261
|
client.call([:dbsize])
|
@@ -226,19 +270,31 @@ class Redis
|
|
226
270
|
|
227
271
|
# Remove all keys from all databases.
|
228
272
|
#
|
273
|
+
# @param [Hash] options
|
274
|
+
# - `:async => Boolean`: async flush (default: false)
|
229
275
|
# @return [String] `OK`
|
230
|
-
def flushall
|
276
|
+
def flushall(options = nil)
|
231
277
|
synchronize do |client|
|
232
|
-
|
278
|
+
if options && options[:async]
|
279
|
+
client.call(%i[flushall async])
|
280
|
+
else
|
281
|
+
client.call([:flushall])
|
282
|
+
end
|
233
283
|
end
|
234
284
|
end
|
235
285
|
|
236
286
|
# Remove all keys from the current database.
|
237
287
|
#
|
288
|
+
# @param [Hash] options
|
289
|
+
# - `:async => Boolean`: async flush (default: false)
|
238
290
|
# @return [String] `OK`
|
239
|
-
def flushdb
|
291
|
+
def flushdb(options = nil)
|
240
292
|
synchronize do |client|
|
241
|
-
|
293
|
+
if options && options[:async]
|
294
|
+
client.call(%i[flushdb async])
|
295
|
+
else
|
296
|
+
client.call([:flushdb])
|
297
|
+
end
|
242
298
|
end
|
243
299
|
end
|
244
300
|
|
@@ -249,10 +305,8 @@ class Redis
|
|
249
305
|
def info(cmd = nil)
|
250
306
|
synchronize do |client|
|
251
307
|
client.call([:info, cmd].compact) do |reply|
|
252
|
-
if reply.
|
253
|
-
reply =
|
254
|
-
line.split(":", 2) unless line =~ /^(#|$)/
|
255
|
-
end.compact]
|
308
|
+
if reply.is_a?(String)
|
309
|
+
reply = HashifyInfo.call(reply)
|
256
310
|
|
257
311
|
if cmd && cmd.to_s == "commandstats"
|
258
312
|
# Extract nested hashes for INFO COMMANDSTATS
|
@@ -270,7 +324,7 @@ class Redis
|
|
270
324
|
|
271
325
|
# Get the UNIX time stamp of the last successful save to disk.
|
272
326
|
#
|
273
|
-
# @return [
|
327
|
+
# @return [Integer]
|
274
328
|
def lastsave
|
275
329
|
synchronize do |client|
|
276
330
|
client.call([:lastsave])
|
@@ -322,9 +376,9 @@ class Redis
|
|
322
376
|
# Interact with the slowlog (get, len, reset)
|
323
377
|
#
|
324
378
|
# @param [String] subcommand e.g. `get`, `len`, `reset`
|
325
|
-
# @param [
|
326
|
-
# @return [Array<String>,
|
327
|
-
def slowlog(subcommand, length=nil)
|
379
|
+
# @param [Integer] length maximum number of entries to return
|
380
|
+
# @return [Array<String>, Integer, String] depends on subcommand
|
381
|
+
def slowlog(subcommand, length = nil)
|
328
382
|
synchronize do |client|
|
329
383
|
args = [:slowlog, subcommand]
|
330
384
|
args << length if length
|
@@ -344,12 +398,12 @@ class Redis
|
|
344
398
|
# @example
|
345
399
|
# r.time # => [ 1333093196, 606806 ]
|
346
400
|
#
|
347
|
-
# @return [Array<
|
401
|
+
# @return [Array<Integer>] tuple of seconds since UNIX epoch and
|
348
402
|
# microseconds in the current second
|
349
403
|
def time
|
350
404
|
synchronize do |client|
|
351
405
|
client.call([:time]) do |reply|
|
352
|
-
reply
|
406
|
+
reply&.map(&:to_i)
|
353
407
|
end
|
354
408
|
end
|
355
409
|
end
|
@@ -367,7 +421,7 @@ class Redis
|
|
367
421
|
# Set a key's time to live in seconds.
|
368
422
|
#
|
369
423
|
# @param [String] key
|
370
|
-
# @param [
|
424
|
+
# @param [Integer] seconds time to live
|
371
425
|
# @return [Boolean] whether the timeout was set or not
|
372
426
|
def expire(key, seconds)
|
373
427
|
synchronize do |client|
|
@@ -378,7 +432,7 @@ class Redis
|
|
378
432
|
# Set the expiration for a key as a UNIX timestamp.
|
379
433
|
#
|
380
434
|
# @param [String] key
|
381
|
-
# @param [
|
435
|
+
# @param [Integer] unix_time expiry time specified as a UNIX timestamp
|
382
436
|
# @return [Boolean] whether the timeout was set or not
|
383
437
|
def expireat(key, unix_time)
|
384
438
|
synchronize do |client|
|
@@ -389,7 +443,7 @@ class Redis
|
|
389
443
|
# Get the time to live (in seconds) for a key.
|
390
444
|
#
|
391
445
|
# @param [String] key
|
392
|
-
# @return [
|
446
|
+
# @return [Integer] remaining time to live in seconds.
|
393
447
|
#
|
394
448
|
# In Redis 2.6 or older the command returns -1 if the key does not exist or if
|
395
449
|
# the key exist but has no associated expire.
|
@@ -407,7 +461,7 @@ class Redis
|
|
407
461
|
# Set a key's time to live in milliseconds.
|
408
462
|
#
|
409
463
|
# @param [String] key
|
410
|
-
# @param [
|
464
|
+
# @param [Integer] milliseconds time to live
|
411
465
|
# @return [Boolean] whether the timeout was set or not
|
412
466
|
def pexpire(key, milliseconds)
|
413
467
|
synchronize do |client|
|
@@ -418,7 +472,7 @@ class Redis
|
|
418
472
|
# Set the expiration for a key as number of milliseconds from UNIX Epoch.
|
419
473
|
#
|
420
474
|
# @param [String] key
|
421
|
-
# @param [
|
475
|
+
# @param [Integer] ms_unix_time expiry time specified as number of milliseconds from UNIX Epoch.
|
422
476
|
# @return [Boolean] whether the timeout was set or not
|
423
477
|
def pexpireat(key, ms_unix_time)
|
424
478
|
synchronize do |client|
|
@@ -429,7 +483,7 @@ class Redis
|
|
429
483
|
# Get the time to live (in milliseconds) for a key.
|
430
484
|
#
|
431
485
|
# @param [String] key
|
432
|
-
# @return [
|
486
|
+
# @return [Integer] remaining time to live in milliseconds
|
433
487
|
# In Redis 2.6 or older the command returns -1 if the key does not exist or if
|
434
488
|
# the key exist but has no associated expire.
|
435
489
|
#
|
@@ -458,50 +512,101 @@ class Redis
|
|
458
512
|
# @param [String] key
|
459
513
|
# @param [String] ttl
|
460
514
|
# @param [String] serialized_value
|
515
|
+
# @param [Hash] options
|
516
|
+
# - `:replace => Boolean`: if false, raises an error if key already exists
|
517
|
+
# @raise [Redis::CommandError]
|
461
518
|
# @return [String] `"OK"`
|
462
|
-
def restore(key, ttl, serialized_value)
|
519
|
+
def restore(key, ttl, serialized_value, replace: nil)
|
520
|
+
args = [:restore, key, ttl, serialized_value]
|
521
|
+
args << 'REPLACE' if replace
|
522
|
+
|
463
523
|
synchronize do |client|
|
464
|
-
client.call(
|
524
|
+
client.call(args)
|
465
525
|
end
|
466
526
|
end
|
467
527
|
|
468
528
|
# Transfer a key from the connected instance to another instance.
|
469
529
|
#
|
470
|
-
# @param [String] key
|
530
|
+
# @param [String, Array<String>] key
|
471
531
|
# @param [Hash] options
|
472
532
|
# - `:host => String`: host of instance to migrate to
|
473
533
|
# - `:port => Integer`: port of instance to migrate to
|
474
534
|
# - `:db => Integer`: database to migrate to (default: same as source)
|
475
535
|
# - `:timeout => Integer`: timeout (default: same as connection timeout)
|
536
|
+
# - `:copy => Boolean`: Do not remove the key from the local instance.
|
537
|
+
# - `:replace => Boolean`: Replace existing key on the remote instance.
|
476
538
|
# @return [String] `"OK"`
|
477
539
|
def migrate(key, options)
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
540
|
+
args = [:migrate]
|
541
|
+
args << (options[:host] || raise(':host not specified'))
|
542
|
+
args << (options[:port] || raise(':port not specified'))
|
543
|
+
args << (key.is_a?(String) ? key : '')
|
544
|
+
args << (options[:db] || @client.db).to_i
|
545
|
+
args << (options[:timeout] || @client.timeout).to_i
|
546
|
+
args << 'COPY' if options[:copy]
|
547
|
+
args << 'REPLACE' if options[:replace]
|
548
|
+
args += ['KEYS', *key] if key.is_a?(Array)
|
482
549
|
|
483
|
-
synchronize
|
484
|
-
client.call([:migrate, host, port, key, db, timeout])
|
485
|
-
end
|
550
|
+
synchronize { |client| client.call(args) }
|
486
551
|
end
|
487
552
|
|
488
553
|
# Delete one or more keys.
|
489
554
|
#
|
490
555
|
# @param [String, Array<String>] keys
|
491
|
-
# @return [
|
556
|
+
# @return [Integer] number of keys that were deleted
|
492
557
|
def del(*keys)
|
493
558
|
synchronize do |client|
|
494
559
|
client.call([:del] + keys)
|
495
560
|
end
|
496
561
|
end
|
497
562
|
|
498
|
-
#
|
563
|
+
# Unlink one or more keys.
|
499
564
|
#
|
500
|
-
# @param [String]
|
565
|
+
# @param [String, Array<String>] keys
|
566
|
+
# @return [Integer] number of keys that were unlinked
|
567
|
+
def unlink(*keys)
|
568
|
+
synchronize do |client|
|
569
|
+
client.call([:unlink] + keys)
|
570
|
+
end
|
571
|
+
end
|
572
|
+
|
573
|
+
# Determine how many of the keys exists.
|
574
|
+
#
|
575
|
+
# @param [String, Array<String>] keys
|
576
|
+
# @return [Integer]
|
577
|
+
def exists(*keys)
|
578
|
+
if !Redis.exists_returns_integer && keys.size == 1
|
579
|
+
if Redis.exists_returns_integer.nil?
|
580
|
+
message = "`Redis#exists(key)` will return an Integer in redis-rb 4.3. `exists?` returns a boolean, you " \
|
581
|
+
"should use it instead. To opt-in to the new behavior now you can set Redis.exists_returns_integer = " \
|
582
|
+
"true. To disable this message and keep the current (boolean) behaviour of 'exists' you can set " \
|
583
|
+
"`Redis.exists_returns_integer = false`, but this option will be removed in 5.0. " \
|
584
|
+
"(#{::Kernel.caller(1, 1).first})\n"
|
585
|
+
|
586
|
+
::Kernel.warn(message)
|
587
|
+
end
|
588
|
+
|
589
|
+
exists?(*keys)
|
590
|
+
else
|
591
|
+
_exists(*keys)
|
592
|
+
end
|
593
|
+
end
|
594
|
+
|
595
|
+
def _exists(*keys)
|
596
|
+
synchronize do |client|
|
597
|
+
client.call([:exists, *keys])
|
598
|
+
end
|
599
|
+
end
|
600
|
+
|
601
|
+
# Determine if any of the keys exists.
|
602
|
+
#
|
603
|
+
# @param [String, Array<String>] keys
|
501
604
|
# @return [Boolean]
|
502
|
-
def exists(
|
605
|
+
def exists?(*keys)
|
503
606
|
synchronize do |client|
|
504
|
-
client.call([:exists,
|
607
|
+
client.call([:exists, *keys]) do |value|
|
608
|
+
value > 0
|
609
|
+
end
|
505
610
|
end
|
506
611
|
end
|
507
612
|
|
@@ -512,7 +617,7 @@ class Redis
|
|
512
617
|
def keys(pattern = "*")
|
513
618
|
synchronize do |client|
|
514
619
|
client.call([:keys, pattern]) do |reply|
|
515
|
-
if reply.
|
620
|
+
if reply.is_a?(String)
|
516
621
|
reply.split(" ")
|
517
622
|
else
|
518
623
|
reply
|
@@ -538,7 +643,7 @@ class Redis
|
|
538
643
|
# # => "bar"
|
539
644
|
#
|
540
645
|
# @param [String] key
|
541
|
-
# @param [
|
646
|
+
# @param [Integer] db
|
542
647
|
# @return [Boolean] whether the key was moved or not
|
543
648
|
def move(key, db)
|
544
649
|
synchronize do |client|
|
@@ -602,36 +707,33 @@ class Redis
|
|
602
707
|
# - `:order => String`: combination of `ASC`, `DESC` and optionally `ALPHA`
|
603
708
|
# - `:store => String`: key to store the result at
|
604
709
|
#
|
605
|
-
# @return [Array<String>, Array<Array<String>>,
|
710
|
+
# @return [Array<String>, Array<Array<String>>, Integer]
|
606
711
|
# - when `:get` is not specified, or holds a single element, an array of elements
|
607
712
|
# - when `:get` is specified, and holds more than one element, an array of
|
608
713
|
# elements where every element is an array with the result for every
|
609
714
|
# element specified in `:get`
|
610
715
|
# - when `:store` is specified, the number of elements in the stored result
|
611
|
-
def sort(key,
|
612
|
-
args = []
|
613
|
-
|
614
|
-
by = options[:by]
|
615
|
-
args.concat(["BY", by]) if by
|
716
|
+
def sort(key, by: nil, limit: nil, get: nil, order: nil, store: nil)
|
717
|
+
args = [:sort, key]
|
718
|
+
args << "BY" << by if by
|
616
719
|
|
617
|
-
|
618
|
-
|
720
|
+
if limit
|
721
|
+
args << "LIMIT"
|
722
|
+
args.concat(limit)
|
723
|
+
end
|
619
724
|
|
620
|
-
get = Array(
|
621
|
-
|
725
|
+
get = Array(get)
|
726
|
+
get.each do |item|
|
727
|
+
args << "GET" << item
|
728
|
+
end
|
622
729
|
|
623
|
-
order = options[:order]
|
624
730
|
args.concat(order.split(" ")) if order
|
625
|
-
|
626
|
-
store = options[:store]
|
627
|
-
args.concat(["STORE", store]) if store
|
731
|
+
args << "STORE" << store if store
|
628
732
|
|
629
733
|
synchronize do |client|
|
630
|
-
client.call(
|
734
|
+
client.call(args) do |reply|
|
631
735
|
if get.size > 1 && !store
|
632
|
-
if reply
|
633
|
-
reply.each_slice(get.size).to_a
|
634
|
-
end
|
736
|
+
reply.each_slice(get.size).to_a if reply
|
635
737
|
else
|
636
738
|
reply
|
637
739
|
end
|
@@ -656,7 +758,7 @@ class Redis
|
|
656
758
|
# # => 4
|
657
759
|
#
|
658
760
|
# @param [String] key
|
659
|
-
# @return [
|
761
|
+
# @return [Integer] value after decrementing it
|
660
762
|
def decr(key)
|
661
763
|
synchronize do |client|
|
662
764
|
client.call([:decr, key])
|
@@ -670,8 +772,8 @@ class Redis
|
|
670
772
|
# # => 0
|
671
773
|
#
|
672
774
|
# @param [String] key
|
673
|
-
# @param [
|
674
|
-
# @return [
|
775
|
+
# @param [Integer] decrement
|
776
|
+
# @return [Integer] value after decrementing it
|
675
777
|
def decrby(key, decrement)
|
676
778
|
synchronize do |client|
|
677
779
|
client.call([:decrby, key, decrement])
|
@@ -685,7 +787,7 @@ class Redis
|
|
685
787
|
# # => 6
|
686
788
|
#
|
687
789
|
# @param [String] key
|
688
|
-
# @return [
|
790
|
+
# @return [Integer] value after incrementing it
|
689
791
|
def incr(key)
|
690
792
|
synchronize do |client|
|
691
793
|
client.call([:incr, key])
|
@@ -699,8 +801,8 @@ class Redis
|
|
699
801
|
# # => 10
|
700
802
|
#
|
701
803
|
# @param [String] key
|
702
|
-
# @param [
|
703
|
-
# @return [
|
804
|
+
# @param [Integer] increment
|
805
|
+
# @return [Integer] value after incrementing it
|
704
806
|
def incrby(key, increment)
|
705
807
|
synchronize do |client|
|
706
808
|
client.call([:incrby, key, increment])
|
@@ -727,41 +829,33 @@ class Redis
|
|
727
829
|
# @param [String] key
|
728
830
|
# @param [String] value
|
729
831
|
# @param [Hash] options
|
730
|
-
# - `:ex =>
|
731
|
-
# - `:px =>
|
832
|
+
# - `:ex => Integer`: Set the specified expire time, in seconds.
|
833
|
+
# - `:px => Integer`: Set the specified expire time, in milliseconds.
|
732
834
|
# - `:nx => true`: Only set the key if it does not already exist.
|
733
835
|
# - `:xx => true`: Only set the key if it already exist.
|
836
|
+
# - `:keepttl => true`: Retain the time to live associated with the key.
|
734
837
|
# @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.concat(["PX", px]) if px
|
743
|
-
|
744
|
-
nx = options[:nx]
|
745
|
-
args.concat(["NX"]) if nx
|
746
|
-
|
747
|
-
xx = options[:xx]
|
748
|
-
args.concat(["XX"]) if xx
|
838
|
+
def set(key, value, ex: nil, px: nil, nx: nil, xx: nil, keepttl: nil)
|
839
|
+
args = [:set, key, value.to_s]
|
840
|
+
args << "EX" << ex if ex
|
841
|
+
args << "PX" << px if px
|
842
|
+
args << "NX" if nx
|
843
|
+
args << "XX" if xx
|
844
|
+
args << "KEEPTTL" if keepttl
|
749
845
|
|
750
846
|
synchronize do |client|
|
751
847
|
if nx || xx
|
752
|
-
client.call(
|
848
|
+
client.call(args, &BoolifySet)
|
753
849
|
else
|
754
|
-
client.call(
|
850
|
+
client.call(args)
|
755
851
|
end
|
756
852
|
end
|
757
853
|
end
|
758
854
|
|
759
|
-
alias :[]= :set
|
760
|
-
|
761
855
|
# Set the time to live in seconds of a key.
|
762
856
|
#
|
763
857
|
# @param [String] key
|
764
|
-
# @param [
|
858
|
+
# @param [Integer] ttl
|
765
859
|
# @param [String] value
|
766
860
|
# @return [String] `"OK"`
|
767
861
|
def setex(key, ttl, value)
|
@@ -773,7 +867,7 @@ class Redis
|
|
773
867
|
# Set the time to live in milliseconds of a key.
|
774
868
|
#
|
775
869
|
# @param [String] key
|
776
|
-
# @param [
|
870
|
+
# @param [Integer] ttl
|
777
871
|
# @param [String] value
|
778
872
|
# @return [String] `"OK"`
|
779
873
|
def psetex(key, ttl, value)
|
@@ -835,7 +929,7 @@ class Redis
|
|
835
929
|
# @see #mapped_msetnx
|
836
930
|
def msetnx(*args)
|
837
931
|
synchronize do |client|
|
838
|
-
client.call([:msetnx
|
932
|
+
client.call([:msetnx, *args], &Boolify)
|
839
933
|
end
|
840
934
|
end
|
841
935
|
|
@@ -863,12 +957,10 @@ class Redis
|
|
863
957
|
end
|
864
958
|
end
|
865
959
|
|
866
|
-
alias :[] :get
|
867
|
-
|
868
960
|
# Get the values of all the given keys.
|
869
961
|
#
|
870
962
|
# @example
|
871
|
-
# redis.mget("key1", "
|
963
|
+
# redis.mget("key1", "key2")
|
872
964
|
# # => ["v1", "v2"]
|
873
965
|
#
|
874
966
|
# @param [Array<String>] keys
|
@@ -877,7 +969,7 @@ class Redis
|
|
877
969
|
# @see #mapped_mget
|
878
970
|
def mget(*keys, &blk)
|
879
971
|
synchronize do |client|
|
880
|
-
client.call([:mget
|
972
|
+
client.call([:mget, *keys], &blk)
|
881
973
|
end
|
882
974
|
end
|
883
975
|
|
@@ -893,7 +985,7 @@ class Redis
|
|
893
985
|
# @see #mget
|
894
986
|
def mapped_mget(*keys)
|
895
987
|
mget(*keys) do |reply|
|
896
|
-
if reply.
|
988
|
+
if reply.is_a?(Array)
|
897
989
|
Hash[keys.zip(reply)]
|
898
990
|
else
|
899
991
|
reply
|
@@ -904,9 +996,9 @@ class Redis
|
|
904
996
|
# Overwrite part of a string at key starting at the specified offset.
|
905
997
|
#
|
906
998
|
# @param [String] key
|
907
|
-
# @param [
|
999
|
+
# @param [Integer] offset byte offset
|
908
1000
|
# @param [String] value
|
909
|
-
# @return [
|
1001
|
+
# @return [Integer] length of the string after it was modified
|
910
1002
|
def setrange(key, offset, value)
|
911
1003
|
synchronize do |client|
|
912
1004
|
client.call([:setrange, key, offset, value.to_s])
|
@@ -916,10 +1008,10 @@ class Redis
|
|
916
1008
|
# Get a substring of the string stored at a key.
|
917
1009
|
#
|
918
1010
|
# @param [String] key
|
919
|
-
# @param [
|
920
|
-
# @param [
|
1011
|
+
# @param [Integer] start zero-based start offset
|
1012
|
+
# @param [Integer] stop zero-based end offset. Use -1 for representing
|
921
1013
|
# the end of the string
|
922
|
-
# @return [
|
1014
|
+
# @return [Integer] `0` or `1`
|
923
1015
|
def getrange(key, start, stop)
|
924
1016
|
synchronize do |client|
|
925
1017
|
client.call([:getrange, key, start, stop])
|
@@ -929,9 +1021,9 @@ class Redis
|
|
929
1021
|
# Sets or clears the bit at offset in the string value stored at key.
|
930
1022
|
#
|
931
1023
|
# @param [String] key
|
932
|
-
# @param [
|
933
|
-
# @param [
|
934
|
-
# @return [
|
1024
|
+
# @param [Integer] offset bit offset
|
1025
|
+
# @param [Integer] value bit value `0` or `1`
|
1026
|
+
# @return [Integer] the original bit value stored at `offset`
|
935
1027
|
def setbit(key, offset, value)
|
936
1028
|
synchronize do |client|
|
937
1029
|
client.call([:setbit, key, offset, value])
|
@@ -941,8 +1033,8 @@ class Redis
|
|
941
1033
|
# Returns the bit value at offset in the string value stored at key.
|
942
1034
|
#
|
943
1035
|
# @param [String] key
|
944
|
-
# @param [
|
945
|
-
# @return [
|
1036
|
+
# @param [Integer] offset bit offset
|
1037
|
+
# @return [Integer] `0` or `1`
|
946
1038
|
def getbit(key, offset)
|
947
1039
|
synchronize do |client|
|
948
1040
|
client.call([:getbit, key, offset])
|
@@ -953,7 +1045,7 @@ class Redis
|
|
953
1045
|
#
|
954
1046
|
# @param [String] key
|
955
1047
|
# @param [String] value value to append
|
956
|
-
# @return [
|
1048
|
+
# @return [Integer] length of the string after appending
|
957
1049
|
def append(key, value)
|
958
1050
|
synchronize do |client|
|
959
1051
|
client.call([:append, key, value])
|
@@ -963,9 +1055,9 @@ class Redis
|
|
963
1055
|
# Count the number of set bits in a range of the string value stored at key.
|
964
1056
|
#
|
965
1057
|
# @param [String] key
|
966
|
-
# @param [
|
967
|
-
# @param [
|
968
|
-
# @return [
|
1058
|
+
# @param [Integer] start start index
|
1059
|
+
# @param [Integer] stop stop index
|
1060
|
+
# @return [Integer] the number of bits set to 1
|
969
1061
|
def bitcount(key, start = 0, stop = -1)
|
970
1062
|
synchronize do |client|
|
971
1063
|
client.call([:bitcount, key, start, stop])
|
@@ -977,25 +1069,23 @@ class Redis
|
|
977
1069
|
# @param [String] operation e.g. `and`, `or`, `xor`, `not`
|
978
1070
|
# @param [String] destkey destination key
|
979
1071
|
# @param [String, Array<String>] keys one or more source keys to perform `operation`
|
980
|
-
# @return [
|
1072
|
+
# @return [Integer] the length of the string stored in `destkey`
|
981
1073
|
def bitop(operation, destkey, *keys)
|
982
1074
|
synchronize do |client|
|
983
|
-
client.call([:bitop, operation, destkey
|
1075
|
+
client.call([:bitop, operation, destkey, *keys])
|
984
1076
|
end
|
985
1077
|
end
|
986
1078
|
|
987
1079
|
# Return the position of the first bit set to 1 or 0 in a string.
|
988
1080
|
#
|
989
1081
|
# @param [String] key
|
990
|
-
# @param [
|
991
|
-
# @param [
|
992
|
-
# @param [
|
993
|
-
# @return [
|
1082
|
+
# @param [Integer] bit whether to look for the first 1 or 0 bit
|
1083
|
+
# @param [Integer] start start index
|
1084
|
+
# @param [Integer] stop stop index
|
1085
|
+
# @return [Integer] the position of the first 1/0 bit.
|
994
1086
|
# -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
|
1087
|
+
def bitpos(key, bit, start = nil, stop = nil)
|
1088
|
+
raise(ArgumentError, 'stop parameter specified without start parameter') if stop && !start
|
999
1089
|
|
1000
1090
|
synchronize do |client|
|
1001
1091
|
command = [:bitpos, key, bit]
|
@@ -1020,7 +1110,7 @@ class Redis
|
|
1020
1110
|
# Get the length of the value stored in a key.
|
1021
1111
|
#
|
1022
1112
|
# @param [String] key
|
1023
|
-
# @return [
|
1113
|
+
# @return [Integer] the length of the value stored in the key, or 0
|
1024
1114
|
# if the key does not exist
|
1025
1115
|
def strlen(key)
|
1026
1116
|
synchronize do |client|
|
@@ -1031,7 +1121,7 @@ class Redis
|
|
1031
1121
|
# Get the length of a list.
|
1032
1122
|
#
|
1033
1123
|
# @param [String] key
|
1034
|
-
# @return [
|
1124
|
+
# @return [Integer]
|
1035
1125
|
def llen(key)
|
1036
1126
|
synchronize do |client|
|
1037
1127
|
client.call([:llen, key])
|
@@ -1041,8 +1131,8 @@ class Redis
|
|
1041
1131
|
# Prepend one or more values to a list, creating the list if it doesn't exist
|
1042
1132
|
#
|
1043
1133
|
# @param [String] key
|
1044
|
-
# @param [String, Array] value string value, or array of string values to push
|
1045
|
-
# @return [
|
1134
|
+
# @param [String, Array<String>] value string value, or array of string values to push
|
1135
|
+
# @return [Integer] the length of the list after the push operation
|
1046
1136
|
def lpush(key, value)
|
1047
1137
|
synchronize do |client|
|
1048
1138
|
client.call([:lpush, key, value])
|
@@ -1053,7 +1143,7 @@ class Redis
|
|
1053
1143
|
#
|
1054
1144
|
# @param [String] key
|
1055
1145
|
# @param [String] value
|
1056
|
-
# @return [
|
1146
|
+
# @return [Integer] the length of the list after the push operation
|
1057
1147
|
def lpushx(key, value)
|
1058
1148
|
synchronize do |client|
|
1059
1149
|
client.call([:lpushx, key, value])
|
@@ -1063,8 +1153,8 @@ class Redis
|
|
1063
1153
|
# Append one or more values to a list, creating the list if it doesn't exist
|
1064
1154
|
#
|
1065
1155
|
# @param [String] key
|
1066
|
-
# @param [String] value
|
1067
|
-
# @return [
|
1156
|
+
# @param [String, Array<String>] value string value, or array of string values to push
|
1157
|
+
# @return [Integer] the length of the list after the push operation
|
1068
1158
|
def rpush(key, value)
|
1069
1159
|
synchronize do |client|
|
1070
1160
|
client.call([:rpush, key, value])
|
@@ -1075,30 +1165,36 @@ class Redis
|
|
1075
1165
|
#
|
1076
1166
|
# @param [String] key
|
1077
1167
|
# @param [String] value
|
1078
|
-
# @return [
|
1168
|
+
# @return [Integer] the length of the list after the push operation
|
1079
1169
|
def rpushx(key, value)
|
1080
1170
|
synchronize do |client|
|
1081
1171
|
client.call([:rpushx, key, value])
|
1082
1172
|
end
|
1083
1173
|
end
|
1084
1174
|
|
1085
|
-
# Remove and get the first
|
1175
|
+
# Remove and get the first elements in a list.
|
1086
1176
|
#
|
1087
1177
|
# @param [String] key
|
1088
|
-
# @
|
1089
|
-
|
1178
|
+
# @param [Integer] count number of elements to remove
|
1179
|
+
# @return [String, Array<String>] the values of the first elements
|
1180
|
+
def lpop(key, count = nil)
|
1090
1181
|
synchronize do |client|
|
1091
|
-
|
1182
|
+
command = [:lpop, key]
|
1183
|
+
command << count if count
|
1184
|
+
client.call(command)
|
1092
1185
|
end
|
1093
1186
|
end
|
1094
1187
|
|
1095
|
-
# Remove and get the last
|
1188
|
+
# Remove and get the last elements in a list.
|
1096
1189
|
#
|
1097
1190
|
# @param [String] key
|
1098
|
-
# @
|
1099
|
-
|
1191
|
+
# @param [Integer] count number of elements to remove
|
1192
|
+
# @return [String, Array<String>] the values of the last elements
|
1193
|
+
def rpop(key, count = nil)
|
1100
1194
|
synchronize do |client|
|
1101
|
-
|
1195
|
+
command = [:rpop, key]
|
1196
|
+
command << count if count
|
1197
|
+
client.call(command)
|
1102
1198
|
end
|
1103
1199
|
end
|
1104
1200
|
|
@@ -1113,28 +1209,27 @@ class Redis
|
|
1113
1209
|
end
|
1114
1210
|
end
|
1115
1211
|
|
1116
|
-
def _bpop(cmd, args)
|
1117
|
-
|
1118
|
-
|
1119
|
-
case args.last
|
1120
|
-
when Hash
|
1212
|
+
def _bpop(cmd, args, &blk)
|
1213
|
+
timeout = if args.last.is_a?(Hash)
|
1121
1214
|
options = args.pop
|
1122
|
-
|
1215
|
+
options[:timeout]
|
1216
|
+
elsif args.last.respond_to?(:to_int)
|
1123
1217
|
# Issue deprecation notice in obnoxious mode...
|
1124
|
-
|
1218
|
+
args.pop.to_int
|
1125
1219
|
end
|
1126
1220
|
|
1221
|
+
timeout ||= 0
|
1222
|
+
|
1127
1223
|
if args.size > 1
|
1128
1224
|
# Issue deprecation notice in obnoxious mode...
|
1129
1225
|
end
|
1130
1226
|
|
1131
1227
|
keys = args.flatten
|
1132
|
-
timeout = options[:timeout] || 0
|
1133
1228
|
|
1134
1229
|
synchronize do |client|
|
1135
1230
|
command = [cmd, keys, timeout]
|
1136
1231
|
timeout += client.timeout if timeout > 0
|
1137
|
-
client.call_with_timeout(command, timeout)
|
1232
|
+
client.call_with_timeout(command, timeout, &blk)
|
1138
1233
|
end
|
1139
1234
|
end
|
1140
1235
|
|
@@ -1154,7 +1249,7 @@ class Redis
|
|
1154
1249
|
# @param [String, Array<String>] keys one or more keys to perform the
|
1155
1250
|
# blocking pop on
|
1156
1251
|
# @param [Hash] options
|
1157
|
-
# - `:timeout =>
|
1252
|
+
# - `:timeout => Integer`: timeout in seconds, defaults to no timeout
|
1158
1253
|
#
|
1159
1254
|
# @return [nil, [String, String]]
|
1160
1255
|
# - `nil` when the operation timed out
|
@@ -1168,7 +1263,7 @@ class Redis
|
|
1168
1263
|
# @param [String, Array<String>] keys one or more keys to perform the
|
1169
1264
|
# blocking pop on
|
1170
1265
|
# @param [Hash] options
|
1171
|
-
# - `:timeout =>
|
1266
|
+
# - `:timeout => Integer`: timeout in seconds, defaults to no timeout
|
1172
1267
|
#
|
1173
1268
|
# @return [nil, [String, String]]
|
1174
1269
|
# - `nil` when the operation timed out
|
@@ -1185,20 +1280,12 @@ class Redis
|
|
1185
1280
|
# @param [String] source source key
|
1186
1281
|
# @param [String] destination destination key
|
1187
1282
|
# @param [Hash] options
|
1188
|
-
# - `:timeout =>
|
1283
|
+
# - `:timeout => Integer`: timeout in seconds, defaults to no timeout
|
1189
1284
|
#
|
1190
1285
|
# @return [nil, String]
|
1191
1286
|
# - `nil` when the operation timed out
|
1192
1287
|
# - 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
|
-
|
1288
|
+
def brpoplpush(source, destination, deprecated_timeout = 0, timeout: deprecated_timeout)
|
1202
1289
|
synchronize do |client|
|
1203
1290
|
command = [:brpoplpush, source, destination, timeout]
|
1204
1291
|
timeout += client.timeout if timeout > 0
|
@@ -1209,7 +1296,7 @@ class Redis
|
|
1209
1296
|
# Get an element from a list by its index.
|
1210
1297
|
#
|
1211
1298
|
# @param [String] key
|
1212
|
-
# @param [
|
1299
|
+
# @param [Integer] index
|
1213
1300
|
# @return [String]
|
1214
1301
|
def lindex(key, index)
|
1215
1302
|
synchronize do |client|
|
@@ -1223,7 +1310,7 @@ class Redis
|
|
1223
1310
|
# @param [String, Symbol] where `BEFORE` or `AFTER`
|
1224
1311
|
# @param [String] pivot reference element
|
1225
1312
|
# @param [String] value
|
1226
|
-
# @return [
|
1313
|
+
# @return [Integer] length of the list after the insert operation, or `-1`
|
1227
1314
|
# when the element `pivot` was not found
|
1228
1315
|
def linsert(key, where, pivot, value)
|
1229
1316
|
synchronize do |client|
|
@@ -1234,8 +1321,8 @@ class Redis
|
|
1234
1321
|
# Get a range of elements from a list.
|
1235
1322
|
#
|
1236
1323
|
# @param [String] key
|
1237
|
-
# @param [
|
1238
|
-
# @param [
|
1324
|
+
# @param [Integer] start start index
|
1325
|
+
# @param [Integer] stop stop index
|
1239
1326
|
# @return [Array<String>]
|
1240
1327
|
def lrange(key, start, stop)
|
1241
1328
|
synchronize do |client|
|
@@ -1246,12 +1333,12 @@ class Redis
|
|
1246
1333
|
# Remove elements from a list.
|
1247
1334
|
#
|
1248
1335
|
# @param [String] key
|
1249
|
-
# @param [
|
1336
|
+
# @param [Integer] count number of elements to remove. Use a positive
|
1250
1337
|
# value to remove the first `count` occurrences of `value`. A negative
|
1251
1338
|
# value to remove the last `count` occurrences of `value`. Or zero, to
|
1252
1339
|
# remove all occurrences of `value` from the list.
|
1253
1340
|
# @param [String] value
|
1254
|
-
# @return [
|
1341
|
+
# @return [Integer] the number of removed elements
|
1255
1342
|
def lrem(key, count, value)
|
1256
1343
|
synchronize do |client|
|
1257
1344
|
client.call([:lrem, key, count, value])
|
@@ -1261,7 +1348,7 @@ class Redis
|
|
1261
1348
|
# Set the value of an element in a list by its index.
|
1262
1349
|
#
|
1263
1350
|
# @param [String] key
|
1264
|
-
# @param [
|
1351
|
+
# @param [Integer] index
|
1265
1352
|
# @param [String] value
|
1266
1353
|
# @return [String] `OK`
|
1267
1354
|
def lset(key, index, value)
|
@@ -1273,8 +1360,8 @@ class Redis
|
|
1273
1360
|
# Trim a list to the specified range.
|
1274
1361
|
#
|
1275
1362
|
# @param [String] key
|
1276
|
-
# @param [
|
1277
|
-
# @param [
|
1363
|
+
# @param [Integer] start start index
|
1364
|
+
# @param [Integer] stop stop index
|
1278
1365
|
# @return [String] `OK`
|
1279
1366
|
def ltrim(key, start, stop)
|
1280
1367
|
synchronize do |client|
|
@@ -1285,7 +1372,7 @@ class Redis
|
|
1285
1372
|
# Get the number of members in a set.
|
1286
1373
|
#
|
1287
1374
|
# @param [String] key
|
1288
|
-
# @return [
|
1375
|
+
# @return [Integer]
|
1289
1376
|
def scard(key)
|
1290
1377
|
synchronize do |client|
|
1291
1378
|
client.call([:scard, key])
|
@@ -1296,8 +1383,8 @@ class Redis
|
|
1296
1383
|
#
|
1297
1384
|
# @param [String] key
|
1298
1385
|
# @param [String, Array<String>] member one member, or array of members
|
1299
|
-
# @return [Boolean,
|
1300
|
-
# holding whether or not adding the member succeeded, or `
|
1386
|
+
# @return [Boolean, Integer] `Boolean` when a single member is specified,
|
1387
|
+
# holding whether or not adding the member succeeded, or `Integer` when an
|
1301
1388
|
# array of members is specified, holding the number of members that were
|
1302
1389
|
# successfully added
|
1303
1390
|
def sadd(key, member)
|
@@ -1318,8 +1405,8 @@ class Redis
|
|
1318
1405
|
#
|
1319
1406
|
# @param [String] key
|
1320
1407
|
# @param [String, Array<String>] member one member, or array of members
|
1321
|
-
# @return [Boolean,
|
1322
|
-
# holding whether or not removing the member succeeded, or `
|
1408
|
+
# @return [Boolean, Integer] `Boolean` when a single member is specified,
|
1409
|
+
# holding whether or not removing the member succeeded, or `Integer` when an
|
1323
1410
|
# array of members is specified, holding the number of members that were
|
1324
1411
|
# successfully removed
|
1325
1412
|
def srem(key, member)
|
@@ -1340,7 +1427,7 @@ class Redis
|
|
1340
1427
|
#
|
1341
1428
|
# @param [String] key
|
1342
1429
|
# @return [String]
|
1343
|
-
# @param [
|
1430
|
+
# @param [Integer] count
|
1344
1431
|
def spop(key, count = nil)
|
1345
1432
|
synchronize do |client|
|
1346
1433
|
if count.nil?
|
@@ -1354,7 +1441,7 @@ class Redis
|
|
1354
1441
|
# Get one or more random members from a set.
|
1355
1442
|
#
|
1356
1443
|
# @param [String] key
|
1357
|
-
# @param [
|
1444
|
+
# @param [Integer] count
|
1358
1445
|
# @return [String]
|
1359
1446
|
def srandmember(key, count = nil)
|
1360
1447
|
synchronize do |client|
|
@@ -1405,7 +1492,7 @@ class Redis
|
|
1405
1492
|
# @return [Array<String>] members in the difference
|
1406
1493
|
def sdiff(*keys)
|
1407
1494
|
synchronize do |client|
|
1408
|
-
client.call([:sdiff
|
1495
|
+
client.call([:sdiff, *keys])
|
1409
1496
|
end
|
1410
1497
|
end
|
1411
1498
|
|
@@ -1413,10 +1500,10 @@ class Redis
|
|
1413
1500
|
#
|
1414
1501
|
# @param [String] destination destination key
|
1415
1502
|
# @param [String, Array<String>] keys keys pointing to sets to subtract
|
1416
|
-
# @return [
|
1503
|
+
# @return [Integer] number of elements in the resulting set
|
1417
1504
|
def sdiffstore(destination, *keys)
|
1418
1505
|
synchronize do |client|
|
1419
|
-
client.call([:sdiffstore, destination
|
1506
|
+
client.call([:sdiffstore, destination, *keys])
|
1420
1507
|
end
|
1421
1508
|
end
|
1422
1509
|
|
@@ -1426,7 +1513,7 @@ class Redis
|
|
1426
1513
|
# @return [Array<String>] members in the intersection
|
1427
1514
|
def sinter(*keys)
|
1428
1515
|
synchronize do |client|
|
1429
|
-
client.call([:sinter
|
1516
|
+
client.call([:sinter, *keys])
|
1430
1517
|
end
|
1431
1518
|
end
|
1432
1519
|
|
@@ -1434,10 +1521,10 @@ class Redis
|
|
1434
1521
|
#
|
1435
1522
|
# @param [String] destination destination key
|
1436
1523
|
# @param [String, Array<String>] keys keys pointing to sets to intersect
|
1437
|
-
# @return [
|
1524
|
+
# @return [Integer] number of elements in the resulting set
|
1438
1525
|
def sinterstore(destination, *keys)
|
1439
1526
|
synchronize do |client|
|
1440
|
-
client.call([:sinterstore, destination
|
1527
|
+
client.call([:sinterstore, destination, *keys])
|
1441
1528
|
end
|
1442
1529
|
end
|
1443
1530
|
|
@@ -1447,7 +1534,7 @@ class Redis
|
|
1447
1534
|
# @return [Array<String>] members in the union
|
1448
1535
|
def sunion(*keys)
|
1449
1536
|
synchronize do |client|
|
1450
|
-
client.call([:sunion
|
1537
|
+
client.call([:sunion, *keys])
|
1451
1538
|
end
|
1452
1539
|
end
|
1453
1540
|
|
@@ -1455,10 +1542,10 @@ class Redis
|
|
1455
1542
|
#
|
1456
1543
|
# @param [String] destination destination key
|
1457
1544
|
# @param [String, Array<String>] keys keys pointing to sets to unify
|
1458
|
-
# @return [
|
1545
|
+
# @return [Integer] number of elements in the resulting set
|
1459
1546
|
def sunionstore(destination, *keys)
|
1460
1547
|
synchronize do |client|
|
1461
|
-
client.call([:sunionstore, destination
|
1548
|
+
client.call([:sunionstore, destination, *keys])
|
1462
1549
|
end
|
1463
1550
|
end
|
1464
1551
|
|
@@ -1469,7 +1556,7 @@ class Redis
|
|
1469
1556
|
# # => 4
|
1470
1557
|
#
|
1471
1558
|
# @param [String] key
|
1472
|
-
# @return [
|
1559
|
+
# @return [Integer]
|
1473
1560
|
def zcard(key)
|
1474
1561
|
synchronize do |client|
|
1475
1562
|
client.call([:zcard, key])
|
@@ -1500,38 +1587,27 @@ class Redis
|
|
1500
1587
|
# - `:incr => true`: When this option is specified ZADD acts like
|
1501
1588
|
# ZINCRBY; only one score-element pair can be specified in this mode
|
1502
1589
|
#
|
1503
|
-
# @return [Boolean,
|
1590
|
+
# @return [Boolean, Integer, Float]
|
1504
1591
|
# - `Boolean` when a single pair is specified, holding whether or not it was
|
1505
1592
|
# **added** to the sorted set.
|
1506
|
-
# - `
|
1593
|
+
# - `Integer` when an array of pairs is specified, holding the number of
|
1507
1594
|
# pairs that were **added** to the sorted set.
|
1508
1595
|
# - `Float` when option :incr is specified, holding the score of the member
|
1509
1596
|
# after incrementing it.
|
1510
|
-
def zadd(key, *args
|
1511
|
-
|
1512
|
-
if
|
1513
|
-
|
1514
|
-
|
1515
|
-
|
1516
|
-
zadd_options << "NX" if nx
|
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
|
1597
|
+
def zadd(key, *args, nx: nil, xx: nil, ch: nil, incr: nil)
|
1598
|
+
command = [:zadd, key]
|
1599
|
+
command << "NX" if nx
|
1600
|
+
command << "XX" if xx
|
1601
|
+
command << "CH" if ch
|
1602
|
+
command << "INCR" if incr
|
1527
1603
|
|
1528
1604
|
synchronize do |client|
|
1529
1605
|
if args.size == 1 && args[0].is_a?(Array)
|
1530
1606
|
# Variadic: return float if INCR, integer if !INCR
|
1531
|
-
client.call(
|
1607
|
+
client.call(command + args[0], &(incr ? Floatify : nil))
|
1532
1608
|
elsif args.size == 2
|
1533
1609
|
# Single pair: return float if INCR, boolean if !INCR
|
1534
|
-
client.call(
|
1610
|
+
client.call(command + args, &(incr ? Floatify : Boolify))
|
1535
1611
|
else
|
1536
1612
|
raise ArgumentError, "wrong number of arguments"
|
1537
1613
|
end
|
@@ -1566,10 +1642,10 @@ class Redis
|
|
1566
1642
|
# - a single member
|
1567
1643
|
# - an array of members
|
1568
1644
|
#
|
1569
|
-
# @return [Boolean,
|
1645
|
+
# @return [Boolean, Integer]
|
1570
1646
|
# - `Boolean` when a single member is specified, holding whether or not it
|
1571
1647
|
# was removed from the sorted set
|
1572
|
-
# - `
|
1648
|
+
# - `Integer` when an array of pairs is specified, holding the number of
|
1573
1649
|
# members that were removed to the sorted set
|
1574
1650
|
def zrem(key, member)
|
1575
1651
|
synchronize do |client|
|
@@ -1585,6 +1661,90 @@ class Redis
|
|
1585
1661
|
end
|
1586
1662
|
end
|
1587
1663
|
|
1664
|
+
# Removes and returns up to count members with the highest scores in the sorted set stored at key.
|
1665
|
+
#
|
1666
|
+
# @example Popping a member
|
1667
|
+
# redis.zpopmax('zset')
|
1668
|
+
# #=> ['b', 2.0]
|
1669
|
+
# @example With count option
|
1670
|
+
# redis.zpopmax('zset', 2)
|
1671
|
+
# #=> [['b', 2.0], ['a', 1.0]]
|
1672
|
+
#
|
1673
|
+
# @params key [String] a key of the sorted set
|
1674
|
+
# @params count [Integer] a number of members
|
1675
|
+
#
|
1676
|
+
# @return [Array<String, Float>] element and score pair if count is not specified
|
1677
|
+
# @return [Array<Array<String, Float>>] list of popped elements and scores
|
1678
|
+
def zpopmax(key, count = nil)
|
1679
|
+
synchronize do |client|
|
1680
|
+
members = client.call([:zpopmax, key, count].compact, &FloatifyPairs)
|
1681
|
+
count.to_i > 1 ? members : members.first
|
1682
|
+
end
|
1683
|
+
end
|
1684
|
+
|
1685
|
+
# Removes and returns up to count members with the lowest scores in the sorted set stored at key.
|
1686
|
+
#
|
1687
|
+
# @example Popping a member
|
1688
|
+
# redis.zpopmin('zset')
|
1689
|
+
# #=> ['a', 1.0]
|
1690
|
+
# @example With count option
|
1691
|
+
# redis.zpopmin('zset', 2)
|
1692
|
+
# #=> [['a', 1.0], ['b', 2.0]]
|
1693
|
+
#
|
1694
|
+
# @params key [String] a key of the sorted set
|
1695
|
+
# @params count [Integer] a number of members
|
1696
|
+
#
|
1697
|
+
# @return [Array<String, Float>] element and score pair if count is not specified
|
1698
|
+
# @return [Array<Array<String, Float>>] list of popped elements and scores
|
1699
|
+
def zpopmin(key, count = nil)
|
1700
|
+
synchronize do |client|
|
1701
|
+
members = client.call([:zpopmin, key, count].compact, &FloatifyPairs)
|
1702
|
+
count.to_i > 1 ? members : members.first
|
1703
|
+
end
|
1704
|
+
end
|
1705
|
+
|
1706
|
+
# Removes and returns up to count members with the highest scores in the sorted set stored at keys,
|
1707
|
+
# or block until one is available.
|
1708
|
+
#
|
1709
|
+
# @example Popping a member from a sorted set
|
1710
|
+
# redis.bzpopmax('zset', 1)
|
1711
|
+
# #=> ['zset', 'b', 2.0]
|
1712
|
+
# @example Popping a member from multiple sorted sets
|
1713
|
+
# redis.bzpopmax('zset1', 'zset2', 1)
|
1714
|
+
# #=> ['zset1', 'b', 2.0]
|
1715
|
+
#
|
1716
|
+
# @params keys [Array<String>] one or multiple keys of the sorted sets
|
1717
|
+
# @params timeout [Integer] the maximum number of seconds to block
|
1718
|
+
#
|
1719
|
+
# @return [Array<String, String, Float>] a touple of key, member and score
|
1720
|
+
# @return [nil] when no element could be popped and the timeout expired
|
1721
|
+
def bzpopmax(*args)
|
1722
|
+
_bpop(:bzpopmax, args) do |reply|
|
1723
|
+
reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
|
1724
|
+
end
|
1725
|
+
end
|
1726
|
+
|
1727
|
+
# Removes and returns up to count members with the lowest scores in the sorted set stored at keys,
|
1728
|
+
# or block until one is available.
|
1729
|
+
#
|
1730
|
+
# @example Popping a member from a sorted set
|
1731
|
+
# redis.bzpopmin('zset', 1)
|
1732
|
+
# #=> ['zset', 'a', 1.0]
|
1733
|
+
# @example Popping a member from multiple sorted sets
|
1734
|
+
# redis.bzpopmin('zset1', 'zset2', 1)
|
1735
|
+
# #=> ['zset1', 'a', 1.0]
|
1736
|
+
#
|
1737
|
+
# @params keys [Array<String>] one or multiple keys of the sorted sets
|
1738
|
+
# @params timeout [Integer] the maximum number of seconds to block
|
1739
|
+
#
|
1740
|
+
# @return [Array<String, String, Float>] a touple of key, member and score
|
1741
|
+
# @return [nil] when no element could be popped and the timeout expired
|
1742
|
+
def bzpopmin(*args)
|
1743
|
+
_bpop(:bzpopmin, args) do |reply|
|
1744
|
+
reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
|
1745
|
+
end
|
1746
|
+
end
|
1747
|
+
|
1588
1748
|
# Get the score associated with the given member in a sorted set.
|
1589
1749
|
#
|
1590
1750
|
# @example Get the score for member "a"
|
@@ -1610,18 +1770,16 @@ class Redis
|
|
1610
1770
|
# # => [["a", 32.0], ["b", 64.0]]
|
1611
1771
|
#
|
1612
1772
|
# @param [String] key
|
1613
|
-
# @param [
|
1614
|
-
# @param [
|
1773
|
+
# @param [Integer] start start index
|
1774
|
+
# @param [Integer] stop stop index
|
1615
1775
|
# @param [Hash] options
|
1616
1776
|
# - `:with_scores => true`: include scores in output
|
1617
1777
|
#
|
1618
1778
|
# @return [Array<String>, Array<[String, Float]>]
|
1619
1779
|
# - when `:with_scores` is not specified, an array of members
|
1620
1780
|
# - 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]
|
1781
|
+
def zrange(key, start, stop, withscores: false, with_scores: withscores)
|
1782
|
+
args = [:zrange, key, start, stop]
|
1625
1783
|
|
1626
1784
|
if with_scores
|
1627
1785
|
args << "WITHSCORES"
|
@@ -1629,7 +1787,7 @@ class Redis
|
|
1629
1787
|
end
|
1630
1788
|
|
1631
1789
|
synchronize do |client|
|
1632
|
-
client.call(
|
1790
|
+
client.call(args, &block)
|
1633
1791
|
end
|
1634
1792
|
end
|
1635
1793
|
|
@@ -1644,10 +1802,8 @@ class Redis
|
|
1644
1802
|
# # => [["b", 64.0], ["a", 32.0]]
|
1645
1803
|
#
|
1646
1804
|
# @see #zrange
|
1647
|
-
def zrevrange(key, start, stop,
|
1648
|
-
args = []
|
1649
|
-
|
1650
|
-
with_scores = options[:with_scores] || options[:withscores]
|
1805
|
+
def zrevrange(key, start, stop, withscores: false, with_scores: withscores)
|
1806
|
+
args = [:zrevrange, key, start, stop]
|
1651
1807
|
|
1652
1808
|
if with_scores
|
1653
1809
|
args << "WITHSCORES"
|
@@ -1655,7 +1811,7 @@ class Redis
|
|
1655
1811
|
end
|
1656
1812
|
|
1657
1813
|
synchronize do |client|
|
1658
|
-
client.call(
|
1814
|
+
client.call(args, &block)
|
1659
1815
|
end
|
1660
1816
|
end
|
1661
1817
|
|
@@ -1663,7 +1819,7 @@ class Redis
|
|
1663
1819
|
#
|
1664
1820
|
# @param [String] key
|
1665
1821
|
# @param [String] member
|
1666
|
-
# @return [
|
1822
|
+
# @return [Integer]
|
1667
1823
|
def zrank(key, member)
|
1668
1824
|
synchronize do |client|
|
1669
1825
|
client.call([:zrank, key, member])
|
@@ -1675,7 +1831,7 @@ class Redis
|
|
1675
1831
|
#
|
1676
1832
|
# @param [String] key
|
1677
1833
|
# @param [String] member
|
1678
|
-
# @return [
|
1834
|
+
# @return [Integer]
|
1679
1835
|
def zrevrank(key, member)
|
1680
1836
|
synchronize do |client|
|
1681
1837
|
client.call([:zrevrank, key, member])
|
@@ -1692,15 +1848,39 @@ class Redis
|
|
1692
1848
|
# # => 5
|
1693
1849
|
#
|
1694
1850
|
# @param [String] key
|
1695
|
-
# @param [
|
1696
|
-
# @param [
|
1697
|
-
# @return [
|
1851
|
+
# @param [Integer] start start index
|
1852
|
+
# @param [Integer] stop stop index
|
1853
|
+
# @return [Integer] number of members that were removed
|
1698
1854
|
def zremrangebyrank(key, start, stop)
|
1699
1855
|
synchronize do |client|
|
1700
1856
|
client.call([:zremrangebyrank, key, start, stop])
|
1701
1857
|
end
|
1702
1858
|
end
|
1703
1859
|
|
1860
|
+
# Count the members, with the same score in a sorted set, within the given lexicographical range.
|
1861
|
+
#
|
1862
|
+
# @example Count members matching a
|
1863
|
+
# redis.zlexcount("zset", "[a", "[a\xff")
|
1864
|
+
# # => 1
|
1865
|
+
# @example Count members matching a-z
|
1866
|
+
# redis.zlexcount("zset", "[a", "[z\xff")
|
1867
|
+
# # => 26
|
1868
|
+
#
|
1869
|
+
# @param [String] key
|
1870
|
+
# @param [String] min
|
1871
|
+
# - inclusive minimum is specified by prefixing `(`
|
1872
|
+
# - exclusive minimum is specified by prefixing `[`
|
1873
|
+
# @param [String] max
|
1874
|
+
# - inclusive maximum is specified by prefixing `(`
|
1875
|
+
# - exclusive maximum is specified by prefixing `[`
|
1876
|
+
#
|
1877
|
+
# @return [Integer] number of members within the specified lexicographical range
|
1878
|
+
def zlexcount(key, min, max)
|
1879
|
+
synchronize do |client|
|
1880
|
+
client.call([:zlexcount, key, min, max])
|
1881
|
+
end
|
1882
|
+
end
|
1883
|
+
|
1704
1884
|
# Return a range of members with the same score in a sorted set, by lexicographical ordering
|
1705
1885
|
#
|
1706
1886
|
# @example Retrieve members matching a
|
@@ -1722,14 +1902,16 @@ class Redis
|
|
1722
1902
|
# `count` members
|
1723
1903
|
#
|
1724
1904
|
# @return [Array<String>, Array<[String, Float]>]
|
1725
|
-
def zrangebylex(key, min, max,
|
1726
|
-
args = []
|
1905
|
+
def zrangebylex(key, min, max, limit: nil)
|
1906
|
+
args = [:zrangebylex, key, min, max]
|
1727
1907
|
|
1728
|
-
|
1729
|
-
|
1908
|
+
if limit
|
1909
|
+
args << "LIMIT"
|
1910
|
+
args.concat(limit)
|
1911
|
+
end
|
1730
1912
|
|
1731
1913
|
synchronize do |client|
|
1732
|
-
client.call(
|
1914
|
+
client.call(args)
|
1733
1915
|
end
|
1734
1916
|
end
|
1735
1917
|
|
@@ -1744,14 +1926,16 @@ class Redis
|
|
1744
1926
|
# # => ["abbygail", "abby"]
|
1745
1927
|
#
|
1746
1928
|
# @see #zrangebylex
|
1747
|
-
def zrevrangebylex(key, max, min,
|
1748
|
-
args = []
|
1929
|
+
def zrevrangebylex(key, max, min, limit: nil)
|
1930
|
+
args = [:zrevrangebylex, key, max, min]
|
1749
1931
|
|
1750
|
-
|
1751
|
-
|
1932
|
+
if limit
|
1933
|
+
args << "LIMIT"
|
1934
|
+
args.concat(limit)
|
1935
|
+
end
|
1752
1936
|
|
1753
1937
|
synchronize do |client|
|
1754
|
-
client.call(
|
1938
|
+
client.call(args)
|
1755
1939
|
end
|
1756
1940
|
end
|
1757
1941
|
|
@@ -1782,21 +1966,21 @@ class Redis
|
|
1782
1966
|
# @return [Array<String>, Array<[String, Float]>]
|
1783
1967
|
# - when `:with_scores` is not specified, an array of members
|
1784
1968
|
# - 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]
|
1969
|
+
def zrangebyscore(key, min, max, withscores: false, with_scores: withscores, limit: nil)
|
1970
|
+
args = [:zrangebyscore, key, min, max]
|
1789
1971
|
|
1790
1972
|
if with_scores
|
1791
1973
|
args << "WITHSCORES"
|
1792
1974
|
block = FloatifyPairs
|
1793
1975
|
end
|
1794
1976
|
|
1795
|
-
|
1796
|
-
|
1977
|
+
if limit
|
1978
|
+
args << "LIMIT"
|
1979
|
+
args.concat(limit)
|
1980
|
+
end
|
1797
1981
|
|
1798
1982
|
synchronize do |client|
|
1799
|
-
client.call(
|
1983
|
+
client.call(args, &block)
|
1800
1984
|
end
|
1801
1985
|
end
|
1802
1986
|
|
@@ -1814,21 +1998,21 @@ class Redis
|
|
1814
1998
|
# # => [["b", 64.0], ["a", 32.0]]
|
1815
1999
|
#
|
1816
2000
|
# @see #zrangebyscore
|
1817
|
-
def zrevrangebyscore(key, max, min,
|
1818
|
-
args = []
|
1819
|
-
|
1820
|
-
with_scores = options[:with_scores] || options[:withscores]
|
2001
|
+
def zrevrangebyscore(key, max, min, withscores: false, with_scores: withscores, limit: nil)
|
2002
|
+
args = [:zrevrangebyscore, key, max, min]
|
1821
2003
|
|
1822
2004
|
if with_scores
|
1823
|
-
args <<
|
2005
|
+
args << "WITHSCORES"
|
1824
2006
|
block = FloatifyPairs
|
1825
2007
|
end
|
1826
2008
|
|
1827
|
-
|
1828
|
-
|
2009
|
+
if limit
|
2010
|
+
args << "LIMIT"
|
2011
|
+
args.concat(limit)
|
2012
|
+
end
|
1829
2013
|
|
1830
2014
|
synchronize do |client|
|
1831
|
-
client.call(
|
2015
|
+
client.call(args, &block)
|
1832
2016
|
end
|
1833
2017
|
end
|
1834
2018
|
|
@@ -1848,7 +2032,7 @@ class Redis
|
|
1848
2032
|
# @param [String] max
|
1849
2033
|
# - inclusive maximum score is specified verbatim
|
1850
2034
|
# - exclusive maximum score is specified by prefixing `(`
|
1851
|
-
# @return [
|
2035
|
+
# @return [Integer] number of members that were removed
|
1852
2036
|
def zremrangebyscore(key, min, max)
|
1853
2037
|
synchronize do |client|
|
1854
2038
|
client.call([:zremrangebyscore, key, min, max])
|
@@ -1871,7 +2055,7 @@ class Redis
|
|
1871
2055
|
# @param [String] max
|
1872
2056
|
# - inclusive maximum score is specified verbatim
|
1873
2057
|
# - exclusive maximum score is specified by prefixing `(`
|
1874
|
-
# @return [
|
2058
|
+
# @return [Integer] number of members in within the specified range
|
1875
2059
|
def zcount(key, min, max)
|
1876
2060
|
synchronize do |client|
|
1877
2061
|
client.call([:zcount, key, min, max])
|
@@ -1891,18 +2075,19 @@ class Redis
|
|
1891
2075
|
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
1892
2076
|
# sorted sets
|
1893
2077
|
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
1894
|
-
# @return [
|
1895
|
-
def zinterstore(destination, keys,
|
1896
|
-
args = []
|
2078
|
+
# @return [Integer] number of elements in the resulting sorted set
|
2079
|
+
def zinterstore(destination, keys, weights: nil, aggregate: nil)
|
2080
|
+
args = [:zinterstore, destination, keys.size, *keys]
|
1897
2081
|
|
1898
|
-
|
1899
|
-
|
2082
|
+
if weights
|
2083
|
+
args << "WEIGHTS"
|
2084
|
+
args.concat(weights)
|
2085
|
+
end
|
1900
2086
|
|
1901
|
-
aggregate
|
1902
|
-
args.concat(["AGGREGATE", aggregate]) if aggregate
|
2087
|
+
args << "AGGREGATE" << aggregate if aggregate
|
1903
2088
|
|
1904
2089
|
synchronize do |client|
|
1905
|
-
client.call(
|
2090
|
+
client.call(args)
|
1906
2091
|
end
|
1907
2092
|
end
|
1908
2093
|
|
@@ -1918,40 +2103,46 @@ class Redis
|
|
1918
2103
|
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
1919
2104
|
# sorted sets
|
1920
2105
|
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
1921
|
-
# @return [
|
1922
|
-
def zunionstore(destination, keys,
|
1923
|
-
args = []
|
2106
|
+
# @return [Integer] number of elements in the resulting sorted set
|
2107
|
+
def zunionstore(destination, keys, weights: nil, aggregate: nil)
|
2108
|
+
args = [:zunionstore, destination, keys.size, *keys]
|
1924
2109
|
|
1925
|
-
|
1926
|
-
|
2110
|
+
if weights
|
2111
|
+
args << "WEIGHTS"
|
2112
|
+
args.concat(weights)
|
2113
|
+
end
|
1927
2114
|
|
1928
|
-
aggregate
|
1929
|
-
args.concat(["AGGREGATE", aggregate]) if aggregate
|
2115
|
+
args << "AGGREGATE" << aggregate if aggregate
|
1930
2116
|
|
1931
2117
|
synchronize do |client|
|
1932
|
-
client.call(
|
2118
|
+
client.call(args)
|
1933
2119
|
end
|
1934
2120
|
end
|
1935
2121
|
|
1936
2122
|
# Get the number of fields in a hash.
|
1937
2123
|
#
|
1938
2124
|
# @param [String] key
|
1939
|
-
# @return [
|
2125
|
+
# @return [Integer] number of fields in the hash
|
1940
2126
|
def hlen(key)
|
1941
2127
|
synchronize do |client|
|
1942
2128
|
client.call([:hlen, key])
|
1943
2129
|
end
|
1944
2130
|
end
|
1945
2131
|
|
1946
|
-
# Set
|
2132
|
+
# Set one or more hash values.
|
2133
|
+
#
|
2134
|
+
# @example
|
2135
|
+
# redis.hset("hash", "f1", "v1", "f2", "v2") # => 2
|
2136
|
+
# redis.hset("hash", { "f1" => "v1", "f2" => "v2" }) # => 2
|
1947
2137
|
#
|
1948
2138
|
# @param [String] key
|
1949
|
-
# @param [String]
|
1950
|
-
# @
|
1951
|
-
|
1952
|
-
|
2139
|
+
# @param [Array<String> | Hash<String, String>] attrs array or hash of fields and values
|
2140
|
+
# @return [Integer] The number of fields that were added to the hash
|
2141
|
+
def hset(key, *attrs)
|
2142
|
+
attrs = attrs.first.flatten if attrs.size == 1 && attrs.first.is_a?(Hash)
|
2143
|
+
|
1953
2144
|
synchronize do |client|
|
1954
|
-
client.call([:hset, key,
|
2145
|
+
client.call([:hset, key, *attrs])
|
1955
2146
|
end
|
1956
2147
|
end
|
1957
2148
|
|
@@ -2040,7 +2231,7 @@ class Redis
|
|
2040
2231
|
# @see #hmget
|
2041
2232
|
def mapped_hmget(key, *fields)
|
2042
2233
|
hmget(key, *fields) do |reply|
|
2043
|
-
if reply.
|
2234
|
+
if reply.is_a?(Array)
|
2044
2235
|
Hash[fields.zip(reply)]
|
2045
2236
|
else
|
2046
2237
|
reply
|
@@ -2052,10 +2243,10 @@ class Redis
|
|
2052
2243
|
#
|
2053
2244
|
# @param [String] key
|
2054
2245
|
# @param [String, Array<String>] field
|
2055
|
-
# @return [
|
2056
|
-
def hdel(key,
|
2246
|
+
# @return [Integer] the number of fields that were removed from the hash
|
2247
|
+
def hdel(key, *fields)
|
2057
2248
|
synchronize do |client|
|
2058
|
-
client.call([:hdel, key,
|
2249
|
+
client.call([:hdel, key, *fields])
|
2059
2250
|
end
|
2060
2251
|
end
|
2061
2252
|
|
@@ -2074,8 +2265,8 @@ class Redis
|
|
2074
2265
|
#
|
2075
2266
|
# @param [String] key
|
2076
2267
|
# @param [String] field
|
2077
|
-
# @param [
|
2078
|
-
# @return [
|
2268
|
+
# @param [Integer] increment
|
2269
|
+
# @return [Integer] value of the field after incrementing it
|
2079
2270
|
def hincrby(key, field, increment)
|
2080
2271
|
synchronize do |client|
|
2081
2272
|
client.call([:hincrby, key, field, increment])
|
@@ -2133,20 +2324,21 @@ class Redis
|
|
2133
2324
|
|
2134
2325
|
def subscribed?
|
2135
2326
|
synchronize do |client|
|
2136
|
-
client.
|
2327
|
+
client.is_a? SubscribedClient
|
2137
2328
|
end
|
2138
2329
|
end
|
2139
2330
|
|
2140
2331
|
# Listen for messages published to the given channels.
|
2141
2332
|
def subscribe(*channels, &block)
|
2142
|
-
synchronize do |
|
2333
|
+
synchronize do |_client|
|
2143
2334
|
_subscription(:subscribe, 0, channels, block)
|
2144
2335
|
end
|
2145
2336
|
end
|
2146
2337
|
|
2147
|
-
# Listen for messages published to the given channels. Throw a timeout error
|
2338
|
+
# Listen for messages published to the given channels. Throw a timeout error
|
2339
|
+
# if there is no messages for a timeout period.
|
2148
2340
|
def subscribe_with_timeout(timeout, *channels, &block)
|
2149
|
-
synchronize do |
|
2341
|
+
synchronize do |_client|
|
2150
2342
|
_subscription(:subscribe_with_timeout, timeout, channels, block)
|
2151
2343
|
end
|
2152
2344
|
end
|
@@ -2154,21 +2346,23 @@ class Redis
|
|
2154
2346
|
# Stop listening for messages posted to the given channels.
|
2155
2347
|
def unsubscribe(*channels)
|
2156
2348
|
synchronize do |client|
|
2157
|
-
raise
|
2349
|
+
raise "Can't unsubscribe if not subscribed." unless subscribed?
|
2350
|
+
|
2158
2351
|
client.unsubscribe(*channels)
|
2159
2352
|
end
|
2160
2353
|
end
|
2161
2354
|
|
2162
2355
|
# Listen for messages published to channels matching the given patterns.
|
2163
2356
|
def psubscribe(*channels, &block)
|
2164
|
-
synchronize do |
|
2357
|
+
synchronize do |_client|
|
2165
2358
|
_subscription(:psubscribe, 0, channels, block)
|
2166
2359
|
end
|
2167
2360
|
end
|
2168
2361
|
|
2169
|
-
# Listen for messages published to channels matching the given patterns.
|
2362
|
+
# Listen for messages published to channels matching the given patterns.
|
2363
|
+
# Throw a timeout error if there is no messages for a timeout period.
|
2170
2364
|
def psubscribe_with_timeout(timeout, *channels, &block)
|
2171
|
-
synchronize do |
|
2365
|
+
synchronize do |_client|
|
2172
2366
|
_subscription(:psubscribe_with_timeout, timeout, channels, block)
|
2173
2367
|
end
|
2174
2368
|
end
|
@@ -2176,7 +2370,8 @@ class Redis
|
|
2176
2370
|
# Stop listening for messages posted to channels matching the given patterns.
|
2177
2371
|
def punsubscribe(*channels)
|
2178
2372
|
synchronize do |client|
|
2179
|
-
raise
|
2373
|
+
raise "Can't unsubscribe if not subscribed." unless subscribed?
|
2374
|
+
|
2180
2375
|
client.punsubscribe(*channels)
|
2181
2376
|
end
|
2182
2377
|
end
|
@@ -2221,7 +2416,7 @@ class Redis
|
|
2221
2416
|
# @see #multi
|
2222
2417
|
def watch(*keys)
|
2223
2418
|
synchronize do |client|
|
2224
|
-
res = client.call([:watch
|
2419
|
+
res = client.call([:watch, *keys])
|
2225
2420
|
|
2226
2421
|
if block_given?
|
2227
2422
|
begin
|
@@ -2251,13 +2446,13 @@ class Redis
|
|
2251
2446
|
end
|
2252
2447
|
|
2253
2448
|
def pipelined
|
2254
|
-
synchronize do |
|
2449
|
+
synchronize do |prior_client|
|
2255
2450
|
begin
|
2256
|
-
|
2451
|
+
@client = Pipeline.new(prior_client)
|
2257
2452
|
yield(self)
|
2258
|
-
|
2453
|
+
prior_client.call_pipeline(@client)
|
2259
2454
|
ensure
|
2260
|
-
@client =
|
2455
|
+
@client = prior_client
|
2261
2456
|
end
|
2262
2457
|
end
|
2263
2458
|
end
|
@@ -2293,17 +2488,16 @@ class Redis
|
|
2293
2488
|
# @see #watch
|
2294
2489
|
# @see #unwatch
|
2295
2490
|
def multi
|
2296
|
-
synchronize do |
|
2491
|
+
synchronize do |prior_client|
|
2297
2492
|
if !block_given?
|
2298
|
-
|
2493
|
+
prior_client.call([:multi])
|
2299
2494
|
else
|
2300
2495
|
begin
|
2301
|
-
|
2302
|
-
original, @client = @client, pipeline
|
2496
|
+
@client = Pipeline::Multi.new(prior_client)
|
2303
2497
|
yield(self)
|
2304
|
-
|
2498
|
+
prior_client.call_pipeline(@client)
|
2305
2499
|
ensure
|
2306
|
-
@client =
|
2500
|
+
@client = prior_client
|
2307
2501
|
end
|
2308
2502
|
end
|
2309
2503
|
end
|
@@ -2450,18 +2644,13 @@ class Redis
|
|
2450
2644
|
_eval(:evalsha, args)
|
2451
2645
|
end
|
2452
2646
|
|
2453
|
-
def _scan(command, cursor, args,
|
2647
|
+
def _scan(command, cursor, args, match: nil, count: nil, type: nil, &block)
|
2454
2648
|
# SSCAN/ZSCAN/HSCAN already prepend the key to +args+.
|
2455
2649
|
|
2456
2650
|
args << cursor
|
2457
|
-
|
2458
|
-
|
2459
|
-
|
2460
|
-
end
|
2461
|
-
|
2462
|
-
if count = options[:count]
|
2463
|
-
args.concat(["COUNT", count])
|
2464
|
-
end
|
2651
|
+
args << "MATCH" << match if match
|
2652
|
+
args << "COUNT" << count if count
|
2653
|
+
args << "TYPE" << type if type
|
2465
2654
|
|
2466
2655
|
synchronize do |client|
|
2467
2656
|
client.call([command] + args, &block)
|
@@ -2476,15 +2665,19 @@ class Redis
|
|
2476
2665
|
# @example Retrieve a batch of keys matching a pattern
|
2477
2666
|
# redis.scan(4, :match => "key:1?")
|
2478
2667
|
# # => ["92", ["key:13", "key:18"]]
|
2668
|
+
# @example Retrieve a batch of keys of a certain type
|
2669
|
+
# redis.scan(92, :type => "zset")
|
2670
|
+
# # => ["173", ["sortedset:14", "sortedset:78"]]
|
2479
2671
|
#
|
2480
2672
|
# @param [String, Integer] cursor the cursor of the iteration
|
2481
2673
|
# @param [Hash] options
|
2482
2674
|
# - `:match => String`: only return keys matching the pattern
|
2483
2675
|
# - `:count => Integer`: return count keys at most per iteration
|
2676
|
+
# - `:type => String`: return keys only of the given type
|
2484
2677
|
#
|
2485
2678
|
# @return [String, Array<String>] the next cursor and all found keys
|
2486
|
-
def scan(cursor, options
|
2487
|
-
_scan(:scan, cursor, [], options)
|
2679
|
+
def scan(cursor, **options)
|
2680
|
+
_scan(:scan, cursor, [], **options)
|
2488
2681
|
end
|
2489
2682
|
|
2490
2683
|
# Scan the keyspace
|
@@ -2496,17 +2689,23 @@ class Redis
|
|
2496
2689
|
# redis.scan_each(:match => "key:1?") {|key| puts key}
|
2497
2690
|
# # => key:13
|
2498
2691
|
# # => key:18
|
2692
|
+
# @example Execute block for each key of a type
|
2693
|
+
# redis.scan_each(:type => "hash") {|key| puts redis.type(key)}
|
2694
|
+
# # => "hash"
|
2695
|
+
# # => "hash"
|
2499
2696
|
#
|
2500
2697
|
# @param [Hash] options
|
2501
2698
|
# - `:match => String`: only return keys matching the pattern
|
2502
2699
|
# - `:count => Integer`: return count keys at most per iteration
|
2700
|
+
# - `:type => String`: return keys only of the given type
|
2503
2701
|
#
|
2504
2702
|
# @return [Enumerator] an enumerator for all found keys
|
2505
|
-
def scan_each(options
|
2506
|
-
return to_enum(:scan_each, options) unless block_given?
|
2703
|
+
def scan_each(**options, &block)
|
2704
|
+
return to_enum(:scan_each, **options) unless block_given?
|
2705
|
+
|
2507
2706
|
cursor = 0
|
2508
2707
|
loop do
|
2509
|
-
cursor, keys = scan(cursor, options)
|
2708
|
+
cursor, keys = scan(cursor, **options)
|
2510
2709
|
keys.each(&block)
|
2511
2710
|
break if cursor == "0"
|
2512
2711
|
end
|
@@ -2523,8 +2722,8 @@ class Redis
|
|
2523
2722
|
# - `:count => Integer`: return count keys at most per iteration
|
2524
2723
|
#
|
2525
2724
|
# @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|
|
2725
|
+
def hscan(key, cursor, **options)
|
2726
|
+
_scan(:hscan, cursor, [key], **options) do |reply|
|
2528
2727
|
[reply[0], reply[1].each_slice(2).to_a]
|
2529
2728
|
end
|
2530
2729
|
end
|
@@ -2540,11 +2739,12 @@ class Redis
|
|
2540
2739
|
# - `:count => Integer`: return count keys at most per iteration
|
2541
2740
|
#
|
2542
2741
|
# @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?
|
2742
|
+
def hscan_each(key, **options, &block)
|
2743
|
+
return to_enum(:hscan_each, key, **options) unless block_given?
|
2744
|
+
|
2545
2745
|
cursor = 0
|
2546
2746
|
loop do
|
2547
|
-
cursor, values = hscan(key, cursor, options)
|
2747
|
+
cursor, values = hscan(key, cursor, **options)
|
2548
2748
|
values.each(&block)
|
2549
2749
|
break if cursor == "0"
|
2550
2750
|
end
|
@@ -2562,8 +2762,8 @@ class Redis
|
|
2562
2762
|
#
|
2563
2763
|
# @return [String, Array<[String, Float]>] the next cursor and all found
|
2564
2764
|
# members and scores
|
2565
|
-
def zscan(key, cursor, options
|
2566
|
-
_scan(:zscan, cursor, [key], options) do |reply|
|
2765
|
+
def zscan(key, cursor, **options)
|
2766
|
+
_scan(:zscan, cursor, [key], **options) do |reply|
|
2567
2767
|
[reply[0], FloatifyPairs.call(reply[1])]
|
2568
2768
|
end
|
2569
2769
|
end
|
@@ -2579,11 +2779,12 @@ class Redis
|
|
2579
2779
|
# - `:count => Integer`: return count keys at most per iteration
|
2580
2780
|
#
|
2581
2781
|
# @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?
|
2782
|
+
def zscan_each(key, **options, &block)
|
2783
|
+
return to_enum(:zscan_each, key, **options) unless block_given?
|
2784
|
+
|
2584
2785
|
cursor = 0
|
2585
2786
|
loop do
|
2586
|
-
cursor, values = zscan(key, cursor, options)
|
2787
|
+
cursor, values = zscan(key, cursor, **options)
|
2587
2788
|
values.each(&block)
|
2588
2789
|
break if cursor == "0"
|
2589
2790
|
end
|
@@ -2600,8 +2801,8 @@ class Redis
|
|
2600
2801
|
# - `:count => Integer`: return count keys at most per iteration
|
2601
2802
|
#
|
2602
2803
|
# @return [String, Array<String>] the next cursor and all found members
|
2603
|
-
def sscan(key, cursor, options
|
2604
|
-
_scan(:sscan, cursor, [key], options)
|
2804
|
+
def sscan(key, cursor, **options)
|
2805
|
+
_scan(:sscan, cursor, [key], **options)
|
2605
2806
|
end
|
2606
2807
|
|
2607
2808
|
# Scan a set
|
@@ -2615,11 +2816,12 @@ class Redis
|
|
2615
2816
|
# - `:count => Integer`: return count keys at most per iteration
|
2616
2817
|
#
|
2617
2818
|
# @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?
|
2819
|
+
def sscan_each(key, **options, &block)
|
2820
|
+
return to_enum(:sscan_each, key, **options) unless block_given?
|
2821
|
+
|
2620
2822
|
cursor = 0
|
2621
2823
|
loop do
|
2622
|
-
cursor, keys = sscan(key, cursor, options)
|
2824
|
+
cursor, keys = sscan(key, cursor, **options)
|
2623
2825
|
keys.each(&block)
|
2624
2826
|
break if cursor == "0"
|
2625
2827
|
end
|
@@ -2642,7 +2844,7 @@ class Redis
|
|
2642
2844
|
# union of the HyperLogLogs contained in the keys.
|
2643
2845
|
#
|
2644
2846
|
# @param [String, Array<String>] keys
|
2645
|
-
# @return [
|
2847
|
+
# @return [Integer]
|
2646
2848
|
def pfcount(*keys)
|
2647
2849
|
synchronize do |client|
|
2648
2850
|
client.call([:pfcount] + keys)
|
@@ -2661,6 +2863,413 @@ class Redis
|
|
2661
2863
|
end
|
2662
2864
|
end
|
2663
2865
|
|
2866
|
+
# Adds the specified geospatial items (latitude, longitude, name) to the specified key
|
2867
|
+
#
|
2868
|
+
# @param [String] key
|
2869
|
+
# @param [Array] member arguemnts for member or members: longitude, latitude, name
|
2870
|
+
# @return [Integer] number of elements added to the sorted set
|
2871
|
+
def geoadd(key, *member)
|
2872
|
+
synchronize do |client|
|
2873
|
+
client.call([:geoadd, key, *member])
|
2874
|
+
end
|
2875
|
+
end
|
2876
|
+
|
2877
|
+
# Returns geohash string representing position for specified members of the specified key.
|
2878
|
+
#
|
2879
|
+
# @param [String] key
|
2880
|
+
# @param [String, Array<String>] member one member or array of members
|
2881
|
+
# @return [Array<String, nil>] returns array containg geohash string if member is present, nil otherwise
|
2882
|
+
def geohash(key, member)
|
2883
|
+
synchronize do |client|
|
2884
|
+
client.call([:geohash, key, member])
|
2885
|
+
end
|
2886
|
+
end
|
2887
|
+
|
2888
|
+
# Query a sorted set representing a geospatial index to fetch members matching a
|
2889
|
+
# given maximum distance from a point
|
2890
|
+
#
|
2891
|
+
# @param [Array] args key, longitude, latitude, radius, unit(m|km|ft|mi)
|
2892
|
+
# @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest
|
2893
|
+
# or the farthest to the nearest relative to the center
|
2894
|
+
# @param [Integer] count limit the results to the first N matching items
|
2895
|
+
# @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
|
2896
|
+
# @return [Array<String>] may be changed with `options`
|
2897
|
+
|
2898
|
+
def georadius(*args, **geoptions)
|
2899
|
+
geoarguments = _geoarguments(*args, **geoptions)
|
2900
|
+
|
2901
|
+
synchronize do |client|
|
2902
|
+
client.call([:georadius, *geoarguments])
|
2903
|
+
end
|
2904
|
+
end
|
2905
|
+
|
2906
|
+
# Query a sorted set representing a geospatial index to fetch members matching a
|
2907
|
+
# given maximum distance from an already existing member
|
2908
|
+
#
|
2909
|
+
# @param [Array] args key, member, radius, unit(m|km|ft|mi)
|
2910
|
+
# @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest or the farthest
|
2911
|
+
# to the nearest relative to the center
|
2912
|
+
# @param [Integer] count limit the results to the first N matching items
|
2913
|
+
# @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
|
2914
|
+
# @return [Array<String>] may be changed with `options`
|
2915
|
+
|
2916
|
+
def georadiusbymember(*args, **geoptions)
|
2917
|
+
geoarguments = _geoarguments(*args, **geoptions)
|
2918
|
+
|
2919
|
+
synchronize do |client|
|
2920
|
+
client.call([:georadiusbymember, *geoarguments])
|
2921
|
+
end
|
2922
|
+
end
|
2923
|
+
|
2924
|
+
# Returns longitude and latitude of members of a geospatial index
|
2925
|
+
#
|
2926
|
+
# @param [String] key
|
2927
|
+
# @param [String, Array<String>] member one member or array of members
|
2928
|
+
# @return [Array<Array<String>, nil>] returns array of elements, where each
|
2929
|
+
# element is either array of longitude and latitude or nil
|
2930
|
+
def geopos(key, member)
|
2931
|
+
synchronize do |client|
|
2932
|
+
client.call([:geopos, key, member])
|
2933
|
+
end
|
2934
|
+
end
|
2935
|
+
|
2936
|
+
# Returns the distance between two members of a geospatial index
|
2937
|
+
#
|
2938
|
+
# @param [String ]key
|
2939
|
+
# @param [Array<String>] members
|
2940
|
+
# @param ['m', 'km', 'mi', 'ft'] unit
|
2941
|
+
# @return [String, nil] returns distance in spefied unit if both members present, nil otherwise.
|
2942
|
+
def geodist(key, member1, member2, unit = 'm')
|
2943
|
+
synchronize do |client|
|
2944
|
+
client.call([:geodist, key, member1, member2, unit])
|
2945
|
+
end
|
2946
|
+
end
|
2947
|
+
|
2948
|
+
# Returns the stream information each subcommand.
|
2949
|
+
#
|
2950
|
+
# @example stream
|
2951
|
+
# redis.xinfo(:stream, 'mystream')
|
2952
|
+
# @example groups
|
2953
|
+
# redis.xinfo(:groups, 'mystream')
|
2954
|
+
# @example consumers
|
2955
|
+
# redis.xinfo(:consumers, 'mystream', 'mygroup')
|
2956
|
+
#
|
2957
|
+
# @param subcommand [String] e.g. `stream` `groups` `consumers`
|
2958
|
+
# @param key [String] the stream key
|
2959
|
+
# @param group [String] the consumer group name, required if subcommand is `consumers`
|
2960
|
+
#
|
2961
|
+
# @return [Hash] information of the stream if subcommand is `stream`
|
2962
|
+
# @return [Array<Hash>] information of the consumer groups if subcommand is `groups`
|
2963
|
+
# @return [Array<Hash>] information of the consumers if subcommand is `consumers`
|
2964
|
+
def xinfo(subcommand, key, group = nil)
|
2965
|
+
args = [:xinfo, subcommand, key, group].compact
|
2966
|
+
synchronize do |client|
|
2967
|
+
client.call(args) do |reply|
|
2968
|
+
case subcommand.to_s.downcase
|
2969
|
+
when 'stream' then Hashify.call(reply)
|
2970
|
+
when 'groups', 'consumers' then reply.map { |arr| Hashify.call(arr) }
|
2971
|
+
else reply
|
2972
|
+
end
|
2973
|
+
end
|
2974
|
+
end
|
2975
|
+
end
|
2976
|
+
|
2977
|
+
# Add new entry to the stream.
|
2978
|
+
#
|
2979
|
+
# @example Without options
|
2980
|
+
# redis.xadd('mystream', f1: 'v1', f2: 'v2')
|
2981
|
+
# @example With options
|
2982
|
+
# redis.xadd('mystream', { f1: 'v1', f2: 'v2' }, id: '0-0', maxlen: 1000, approximate: true)
|
2983
|
+
#
|
2984
|
+
# @param key [String] the stream key
|
2985
|
+
# @param entry [Hash] one or multiple field-value pairs
|
2986
|
+
# @param opts [Hash] several options for `XADD` command
|
2987
|
+
#
|
2988
|
+
# @option opts [String] :id the entry id, default value is `*`, it means auto generation
|
2989
|
+
# @option opts [Integer] :maxlen max length of entries
|
2990
|
+
# @option opts [Boolean] :approximate whether to add `~` modifier of maxlen or not
|
2991
|
+
#
|
2992
|
+
# @return [String] the entry id
|
2993
|
+
def xadd(key, entry, approximate: nil, maxlen: nil, id: '*')
|
2994
|
+
args = [:xadd, key]
|
2995
|
+
if maxlen
|
2996
|
+
args << "MAXLEN"
|
2997
|
+
args << "~" if approximate
|
2998
|
+
args << maxlen
|
2999
|
+
end
|
3000
|
+
args << id
|
3001
|
+
args.concat(entry.to_a.flatten)
|
3002
|
+
synchronize { |client| client.call(args) }
|
3003
|
+
end
|
3004
|
+
|
3005
|
+
# Trims older entries of the stream if needed.
|
3006
|
+
#
|
3007
|
+
# @example Without options
|
3008
|
+
# redis.xtrim('mystream', 1000)
|
3009
|
+
# @example With options
|
3010
|
+
# redis.xtrim('mystream', 1000, approximate: true)
|
3011
|
+
#
|
3012
|
+
# @param key [String] the stream key
|
3013
|
+
# @param mexlen [Integer] max length of entries
|
3014
|
+
# @param approximate [Boolean] whether to add `~` modifier of maxlen or not
|
3015
|
+
#
|
3016
|
+
# @return [Integer] the number of entries actually deleted
|
3017
|
+
def xtrim(key, maxlen, approximate: false)
|
3018
|
+
args = [:xtrim, key, 'MAXLEN', (approximate ? '~' : nil), maxlen].compact
|
3019
|
+
synchronize { |client| client.call(args) }
|
3020
|
+
end
|
3021
|
+
|
3022
|
+
# Delete entries by entry ids.
|
3023
|
+
#
|
3024
|
+
# @example With splatted entry ids
|
3025
|
+
# redis.xdel('mystream', '0-1', '0-2')
|
3026
|
+
# @example With arrayed entry ids
|
3027
|
+
# redis.xdel('mystream', ['0-1', '0-2'])
|
3028
|
+
#
|
3029
|
+
# @param key [String] the stream key
|
3030
|
+
# @param ids [Array<String>] one or multiple entry ids
|
3031
|
+
#
|
3032
|
+
# @return [Integer] the number of entries actually deleted
|
3033
|
+
def xdel(key, *ids)
|
3034
|
+
args = [:xdel, key].concat(ids.flatten)
|
3035
|
+
synchronize { |client| client.call(args) }
|
3036
|
+
end
|
3037
|
+
|
3038
|
+
# Fetches entries of the stream in ascending order.
|
3039
|
+
#
|
3040
|
+
# @example Without options
|
3041
|
+
# redis.xrange('mystream')
|
3042
|
+
# @example With a specific start
|
3043
|
+
# redis.xrange('mystream', '0-1')
|
3044
|
+
# @example With a specific start and end
|
3045
|
+
# redis.xrange('mystream', '0-1', '0-3')
|
3046
|
+
# @example With count options
|
3047
|
+
# redis.xrange('mystream', count: 10)
|
3048
|
+
#
|
3049
|
+
# @param key [String] the stream key
|
3050
|
+
# @param start [String] first entry id of range, default value is `-`
|
3051
|
+
# @param end [String] last entry id of range, default value is `+`
|
3052
|
+
# @param count [Integer] the number of entries as limit
|
3053
|
+
#
|
3054
|
+
# @return [Array<Array<String, Hash>>] the ids and entries pairs
|
3055
|
+
def xrange(key, start = '-', range_end = '+', count: nil)
|
3056
|
+
args = [:xrange, key, start, range_end]
|
3057
|
+
args.concat(['COUNT', count]) if count
|
3058
|
+
synchronize { |client| client.call(args, &HashifyStreamEntries) }
|
3059
|
+
end
|
3060
|
+
|
3061
|
+
# Fetches entries of the stream in descending order.
|
3062
|
+
#
|
3063
|
+
# @example Without options
|
3064
|
+
# redis.xrevrange('mystream')
|
3065
|
+
# @example With a specific end
|
3066
|
+
# redis.xrevrange('mystream', '0-3')
|
3067
|
+
# @example With a specific end and start
|
3068
|
+
# redis.xrevrange('mystream', '0-3', '0-1')
|
3069
|
+
# @example With count options
|
3070
|
+
# redis.xrevrange('mystream', count: 10)
|
3071
|
+
#
|
3072
|
+
# @param key [String] the stream key
|
3073
|
+
# @param end [String] first entry id of range, default value is `+`
|
3074
|
+
# @param start [String] last entry id of range, default value is `-`
|
3075
|
+
# @params count [Integer] the number of entries as limit
|
3076
|
+
#
|
3077
|
+
# @return [Array<Array<String, Hash>>] the ids and entries pairs
|
3078
|
+
def xrevrange(key, range_end = '+', start = '-', count: nil)
|
3079
|
+
args = [:xrevrange, key, range_end, start]
|
3080
|
+
args.concat(['COUNT', count]) if count
|
3081
|
+
synchronize { |client| client.call(args, &HashifyStreamEntries) }
|
3082
|
+
end
|
3083
|
+
|
3084
|
+
# Returns the number of entries inside a stream.
|
3085
|
+
#
|
3086
|
+
# @example With key
|
3087
|
+
# redis.xlen('mystream')
|
3088
|
+
#
|
3089
|
+
# @param key [String] the stream key
|
3090
|
+
#
|
3091
|
+
# @return [Integer] the number of entries
|
3092
|
+
def xlen(key)
|
3093
|
+
synchronize { |client| client.call([:xlen, key]) }
|
3094
|
+
end
|
3095
|
+
|
3096
|
+
# Fetches entries from one or multiple streams. Optionally blocking.
|
3097
|
+
#
|
3098
|
+
# @example With a key
|
3099
|
+
# redis.xread('mystream', '0-0')
|
3100
|
+
# @example With multiple keys
|
3101
|
+
# redis.xread(%w[mystream1 mystream2], %w[0-0 0-0])
|
3102
|
+
# @example With count option
|
3103
|
+
# redis.xread('mystream', '0-0', count: 2)
|
3104
|
+
# @example With block option
|
3105
|
+
# redis.xread('mystream', '$', block: 1000)
|
3106
|
+
#
|
3107
|
+
# @param keys [Array<String>] one or multiple stream keys
|
3108
|
+
# @param ids [Array<String>] one or multiple entry ids
|
3109
|
+
# @param count [Integer] the number of entries as limit per stream
|
3110
|
+
# @param block [Integer] the number of milliseconds as blocking timeout
|
3111
|
+
#
|
3112
|
+
# @return [Hash{String => Hash{String => Hash}}] the entries
|
3113
|
+
def xread(keys, ids, count: nil, block: nil)
|
3114
|
+
args = [:xread]
|
3115
|
+
args << 'COUNT' << count if count
|
3116
|
+
args << 'BLOCK' << block.to_i if block
|
3117
|
+
_xread(args, keys, ids, block)
|
3118
|
+
end
|
3119
|
+
|
3120
|
+
# Manages the consumer group of the stream.
|
3121
|
+
#
|
3122
|
+
# @example With `create` subcommand
|
3123
|
+
# redis.xgroup(:create, 'mystream', 'mygroup', '$')
|
3124
|
+
# @example With `setid` subcommand
|
3125
|
+
# redis.xgroup(:setid, 'mystream', 'mygroup', '$')
|
3126
|
+
# @example With `destroy` subcommand
|
3127
|
+
# redis.xgroup(:destroy, 'mystream', 'mygroup')
|
3128
|
+
# @example With `delconsumer` subcommand
|
3129
|
+
# redis.xgroup(:delconsumer, 'mystream', 'mygroup', 'consumer1')
|
3130
|
+
#
|
3131
|
+
# @param subcommand [String] `create` `setid` `destroy` `delconsumer`
|
3132
|
+
# @param key [String] the stream key
|
3133
|
+
# @param group [String] the consumer group name
|
3134
|
+
# @param id_or_consumer [String]
|
3135
|
+
# * the entry id or `$`, required if subcommand is `create` or `setid`
|
3136
|
+
# * the consumer name, required if subcommand is `delconsumer`
|
3137
|
+
# @param mkstream [Boolean] whether to create an empty stream automatically or not
|
3138
|
+
#
|
3139
|
+
# @return [String] `OK` if subcommand is `create` or `setid`
|
3140
|
+
# @return [Integer] effected count if subcommand is `destroy` or `delconsumer`
|
3141
|
+
def xgroup(subcommand, key, group, id_or_consumer = nil, mkstream: false)
|
3142
|
+
args = [:xgroup, subcommand, key, group, id_or_consumer, (mkstream ? 'MKSTREAM' : nil)].compact
|
3143
|
+
synchronize { |client| client.call(args) }
|
3144
|
+
end
|
3145
|
+
|
3146
|
+
# Fetches a subset of the entries from one or multiple streams related with the consumer group.
|
3147
|
+
# Optionally blocking.
|
3148
|
+
#
|
3149
|
+
# @example With a key
|
3150
|
+
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>')
|
3151
|
+
# @example With multiple keys
|
3152
|
+
# redis.xreadgroup('mygroup', 'consumer1', %w[mystream1 mystream2], %w[> >])
|
3153
|
+
# @example With count option
|
3154
|
+
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', count: 2)
|
3155
|
+
# @example With block option
|
3156
|
+
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', block: 1000)
|
3157
|
+
# @example With noack option
|
3158
|
+
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', noack: true)
|
3159
|
+
#
|
3160
|
+
# @param group [String] the consumer group name
|
3161
|
+
# @param consumer [String] the consumer name
|
3162
|
+
# @param keys [Array<String>] one or multiple stream keys
|
3163
|
+
# @param ids [Array<String>] one or multiple entry ids
|
3164
|
+
# @param opts [Hash] several options for `XREADGROUP` command
|
3165
|
+
#
|
3166
|
+
# @option opts [Integer] :count the number of entries as limit
|
3167
|
+
# @option opts [Integer] :block the number of milliseconds as blocking timeout
|
3168
|
+
# @option opts [Boolean] :noack whether message loss is acceptable or not
|
3169
|
+
#
|
3170
|
+
# @return [Hash{String => Hash{String => Hash}}] the entries
|
3171
|
+
def xreadgroup(group, consumer, keys, ids, count: nil, block: nil, noack: nil)
|
3172
|
+
args = [:xreadgroup, 'GROUP', group, consumer]
|
3173
|
+
args << 'COUNT' << count if count
|
3174
|
+
args << 'BLOCK' << block.to_i if block
|
3175
|
+
args << 'NOACK' if noack
|
3176
|
+
_xread(args, keys, ids, block)
|
3177
|
+
end
|
3178
|
+
|
3179
|
+
# Removes one or multiple entries from the pending entries list of a stream consumer group.
|
3180
|
+
#
|
3181
|
+
# @example With a entry id
|
3182
|
+
# redis.xack('mystream', 'mygroup', '1526569495631-0')
|
3183
|
+
# @example With splatted entry ids
|
3184
|
+
# redis.xack('mystream', 'mygroup', '0-1', '0-2')
|
3185
|
+
# @example With arrayed entry ids
|
3186
|
+
# redis.xack('mystream', 'mygroup', %w[0-1 0-2])
|
3187
|
+
#
|
3188
|
+
# @param key [String] the stream key
|
3189
|
+
# @param group [String] the consumer group name
|
3190
|
+
# @param ids [Array<String>] one or multiple entry ids
|
3191
|
+
#
|
3192
|
+
# @return [Integer] the number of entries successfully acknowledged
|
3193
|
+
def xack(key, group, *ids)
|
3194
|
+
args = [:xack, key, group].concat(ids.flatten)
|
3195
|
+
synchronize { |client| client.call(args) }
|
3196
|
+
end
|
3197
|
+
|
3198
|
+
# Changes the ownership of a pending entry
|
3199
|
+
#
|
3200
|
+
# @example With splatted entry ids
|
3201
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-1', '0-2')
|
3202
|
+
# @example With arrayed entry ids
|
3203
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2])
|
3204
|
+
# @example With idle option
|
3205
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], idle: 1000)
|
3206
|
+
# @example With time option
|
3207
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], time: 1542866959000)
|
3208
|
+
# @example With retrycount option
|
3209
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], retrycount: 10)
|
3210
|
+
# @example With force option
|
3211
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], force: true)
|
3212
|
+
# @example With justid option
|
3213
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], justid: true)
|
3214
|
+
#
|
3215
|
+
# @param key [String] the stream key
|
3216
|
+
# @param group [String] the consumer group name
|
3217
|
+
# @param consumer [String] the consumer name
|
3218
|
+
# @param min_idle_time [Integer] the number of milliseconds
|
3219
|
+
# @param ids [Array<String>] one or multiple entry ids
|
3220
|
+
# @param opts [Hash] several options for `XCLAIM` command
|
3221
|
+
#
|
3222
|
+
# @option opts [Integer] :idle the number of milliseconds as last time it was delivered of the entry
|
3223
|
+
# @option opts [Integer] :time the number of milliseconds as a specific Unix Epoch time
|
3224
|
+
# @option opts [Integer] :retrycount the number of retry counter
|
3225
|
+
# @option opts [Boolean] :force whether to create the pending entry to the pending entries list or not
|
3226
|
+
# @option opts [Boolean] :justid whether to fetch just an array of entry ids or not
|
3227
|
+
#
|
3228
|
+
# @return [Hash{String => Hash}] the entries successfully claimed
|
3229
|
+
# @return [Array<String>] the entry ids successfully claimed if justid option is `true`
|
3230
|
+
def xclaim(key, group, consumer, min_idle_time, *ids, **opts)
|
3231
|
+
args = [:xclaim, key, group, consumer, min_idle_time].concat(ids.flatten)
|
3232
|
+
args.concat(['IDLE', opts[:idle].to_i]) if opts[:idle]
|
3233
|
+
args.concat(['TIME', opts[:time].to_i]) if opts[:time]
|
3234
|
+
args.concat(['RETRYCOUNT', opts[:retrycount]]) if opts[:retrycount]
|
3235
|
+
args << 'FORCE' if opts[:force]
|
3236
|
+
args << 'JUSTID' if opts[:justid]
|
3237
|
+
blk = opts[:justid] ? Noop : HashifyStreamEntries
|
3238
|
+
synchronize { |client| client.call(args, &blk) }
|
3239
|
+
end
|
3240
|
+
|
3241
|
+
# Fetches not acknowledging pending entries
|
3242
|
+
#
|
3243
|
+
# @example With key and group
|
3244
|
+
# redis.xpending('mystream', 'mygroup')
|
3245
|
+
# @example With range options
|
3246
|
+
# redis.xpending('mystream', 'mygroup', '-', '+', 10)
|
3247
|
+
# @example With range and consumer options
|
3248
|
+
# redis.xpending('mystream', 'mygroup', '-', '+', 10, 'consumer1')
|
3249
|
+
#
|
3250
|
+
# @param key [String] the stream key
|
3251
|
+
# @param group [String] the consumer group name
|
3252
|
+
# @param start [String] start first entry id of range
|
3253
|
+
# @param end [String] end last entry id of range
|
3254
|
+
# @param count [Integer] count the number of entries as limit
|
3255
|
+
# @param consumer [String] the consumer name
|
3256
|
+
#
|
3257
|
+
# @return [Hash] the summary of pending entries
|
3258
|
+
# @return [Array<Hash>] the pending entries details if options were specified
|
3259
|
+
def xpending(key, group, *args)
|
3260
|
+
command_args = [:xpending, key, group]
|
3261
|
+
case args.size
|
3262
|
+
when 0, 3, 4
|
3263
|
+
command_args.concat(args)
|
3264
|
+
else
|
3265
|
+
raise ArgumentError, "wrong number of arguments (given #{args.size + 2}, expected 2, 5 or 6)"
|
3266
|
+
end
|
3267
|
+
|
3268
|
+
summary_needed = args.empty?
|
3269
|
+
blk = summary_needed ? HashifyStreamPendings : HashifyStreamPendingDetails
|
3270
|
+
synchronize { |client| client.call(command_args, &blk) }
|
3271
|
+
end
|
3272
|
+
|
2664
3273
|
# Interact with the sentinel command (masters, master, slaves, failover)
|
2665
3274
|
#
|
2666
3275
|
# @param [String] subcommand e.g. `masters`, `master`, `slaves`
|
@@ -2674,8 +3283,8 @@ class Redis
|
|
2674
3283
|
when "get-master-addr-by-name"
|
2675
3284
|
reply
|
2676
3285
|
else
|
2677
|
-
if reply.
|
2678
|
-
if reply[0].
|
3286
|
+
if reply.is_a?(Array)
|
3287
|
+
if reply[0].is_a?(Array)
|
2679
3288
|
reply.map(&Hashify)
|
2680
3289
|
else
|
2681
3290
|
Hashify.call(reply)
|
@@ -2688,6 +3297,46 @@ class Redis
|
|
2688
3297
|
end
|
2689
3298
|
end
|
2690
3299
|
|
3300
|
+
# Sends `CLUSTER *` command to random node and returns its reply.
|
3301
|
+
#
|
3302
|
+
# @see https://redis.io/commands#cluster Reference of cluster command
|
3303
|
+
#
|
3304
|
+
# @param subcommand [String, Symbol] the subcommand of cluster command
|
3305
|
+
# e.g. `:slots`, `:nodes`, `:slaves`, `:info`
|
3306
|
+
#
|
3307
|
+
# @return [Object] depends on the subcommand
|
3308
|
+
def cluster(subcommand, *args)
|
3309
|
+
subcommand = subcommand.to_s.downcase
|
3310
|
+
block = case subcommand
|
3311
|
+
when 'slots'
|
3312
|
+
HashifyClusterSlots
|
3313
|
+
when 'nodes'
|
3314
|
+
HashifyClusterNodes
|
3315
|
+
when 'slaves'
|
3316
|
+
HashifyClusterSlaves
|
3317
|
+
when 'info'
|
3318
|
+
HashifyInfo
|
3319
|
+
else
|
3320
|
+
Noop
|
3321
|
+
end
|
3322
|
+
|
3323
|
+
# @see https://github.com/antirez/redis/blob/unstable/src/redis-trib.rb#L127 raw reply expected
|
3324
|
+
block = Noop unless @cluster_mode
|
3325
|
+
|
3326
|
+
synchronize do |client|
|
3327
|
+
client.call([:cluster, subcommand] + args, &block)
|
3328
|
+
end
|
3329
|
+
end
|
3330
|
+
|
3331
|
+
# Sends `ASKING` command to random node and returns its reply.
|
3332
|
+
#
|
3333
|
+
# @see https://redis.io/topics/cluster-spec#ask-redirection ASK redirection
|
3334
|
+
#
|
3335
|
+
# @return [String] `'OK'`
|
3336
|
+
def asking
|
3337
|
+
synchronize { |client| client.call(%i[asking]) }
|
3338
|
+
end
|
3339
|
+
|
2691
3340
|
def id
|
2692
3341
|
@original_client.id
|
2693
3342
|
end
|
@@ -2701,68 +3350,169 @@ class Redis
|
|
2701
3350
|
end
|
2702
3351
|
|
2703
3352
|
def connection
|
3353
|
+
return @original_client.connection_info if @cluster_mode
|
3354
|
+
|
2704
3355
|
{
|
2705
|
-
:
|
2706
|
-
:
|
2707
|
-
:
|
2708
|
-
:
|
2709
|
-
:
|
3356
|
+
host: @original_client.host,
|
3357
|
+
port: @original_client.port,
|
3358
|
+
db: @original_client.db,
|
3359
|
+
id: @original_client.id,
|
3360
|
+
location: @original_client.location
|
2710
3361
|
}
|
2711
3362
|
end
|
2712
3363
|
|
2713
|
-
def method_missing(command, *args)
|
3364
|
+
def method_missing(command, *args) # rubocop:disable Style/MissingRespondToMissing
|
2714
3365
|
synchronize do |client|
|
2715
3366
|
client.call([command] + args)
|
2716
3367
|
end
|
2717
3368
|
end
|
2718
3369
|
|
2719
|
-
private
|
3370
|
+
private
|
2720
3371
|
|
2721
3372
|
# Commands returning 1 for true and 0 for false may be executed in a pipeline
|
2722
3373
|
# where the method call will return nil. Propagate the nil instead of falsely
|
2723
3374
|
# returning false.
|
2724
|
-
Boolify =
|
2725
|
-
|
2726
|
-
|
2727
|
-
|
3375
|
+
Boolify = lambda { |value|
|
3376
|
+
case value
|
3377
|
+
when 1
|
3378
|
+
true
|
3379
|
+
when 0
|
3380
|
+
false
|
3381
|
+
else
|
3382
|
+
value
|
3383
|
+
end
|
3384
|
+
}
|
2728
3385
|
|
2729
|
-
BoolifySet =
|
2730
|
-
|
2731
|
-
|
2732
|
-
|
2733
|
-
|
2734
|
-
|
2735
|
-
|
2736
|
-
|
3386
|
+
BoolifySet = lambda { |value|
|
3387
|
+
case value
|
3388
|
+
when "OK"
|
3389
|
+
true
|
3390
|
+
when nil
|
3391
|
+
false
|
3392
|
+
else
|
3393
|
+
value
|
3394
|
+
end
|
3395
|
+
}
|
2737
3396
|
|
2738
|
-
Hashify =
|
2739
|
-
|
2740
|
-
|
2741
|
-
|
2742
|
-
|
2743
|
-
|
2744
|
-
|
2745
|
-
|
3397
|
+
Hashify = lambda { |value|
|
3398
|
+
if value.respond_to?(:each_slice)
|
3399
|
+
value.each_slice(2).to_h
|
3400
|
+
else
|
3401
|
+
value
|
3402
|
+
end
|
3403
|
+
}
|
3404
|
+
|
3405
|
+
Floatify = lambda { |value|
|
3406
|
+
case value
|
3407
|
+
when "inf"
|
3408
|
+
Float::INFINITY
|
3409
|
+
when "-inf"
|
3410
|
+
-Float::INFINITY
|
3411
|
+
when String
|
3412
|
+
Float(value)
|
3413
|
+
else
|
3414
|
+
value
|
3415
|
+
end
|
3416
|
+
}
|
2746
3417
|
|
2747
|
-
|
2748
|
-
|
2749
|
-
|
2750
|
-
|
2751
|
-
|
2752
|
-
|
2753
|
-
|
2754
|
-
|
2755
|
-
|
3418
|
+
FloatifyPairs = lambda { |value|
|
3419
|
+
return value unless value.respond_to?(:each_slice)
|
3420
|
+
|
3421
|
+
value.each_slice(2).map do |member, score|
|
3422
|
+
[member, Floatify.call(score)]
|
3423
|
+
end
|
3424
|
+
}
|
3425
|
+
|
3426
|
+
HashifyInfo = lambda { |reply|
|
3427
|
+
lines = reply.split("\r\n").grep_v(/^(#|$)/)
|
3428
|
+
lines.map! { |line| line.split(':', 2) }
|
3429
|
+
lines.compact!
|
3430
|
+
lines.to_h
|
3431
|
+
}
|
3432
|
+
|
3433
|
+
HashifyStreams = lambda { |reply|
|
3434
|
+
case reply
|
3435
|
+
when nil
|
3436
|
+
{}
|
3437
|
+
else
|
3438
|
+
reply.map { |key, entries| [key, HashifyStreamEntries.call(entries)] }.to_h
|
3439
|
+
end
|
3440
|
+
}
|
3441
|
+
|
3442
|
+
EMPTY_STREAM_RESPONSE = [nil].freeze
|
3443
|
+
private_constant :EMPTY_STREAM_RESPONSE
|
3444
|
+
|
3445
|
+
HashifyStreamEntries = lambda { |reply|
|
3446
|
+
reply.compact.map do |entry_id, values|
|
3447
|
+
[entry_id, values.each_slice(2).to_h]
|
3448
|
+
end
|
3449
|
+
}
|
3450
|
+
|
3451
|
+
HashifyStreamPendings = lambda { |reply|
|
3452
|
+
{
|
3453
|
+
'size' => reply[0],
|
3454
|
+
'min_entry_id' => reply[1],
|
3455
|
+
'max_entry_id' => reply[2],
|
3456
|
+
'consumers' => reply[3].nil? ? {} : reply[3].to_h
|
2756
3457
|
}
|
3458
|
+
}
|
2757
3459
|
|
2758
|
-
|
2759
|
-
|
2760
|
-
|
2761
|
-
|
2762
|
-
|
2763
|
-
|
2764
|
-
|
3460
|
+
HashifyStreamPendingDetails = lambda { |reply|
|
3461
|
+
reply.map do |arr|
|
3462
|
+
{
|
3463
|
+
'entry_id' => arr[0],
|
3464
|
+
'consumer' => arr[1],
|
3465
|
+
'elapsed' => arr[2],
|
3466
|
+
'count' => arr[3]
|
3467
|
+
}
|
3468
|
+
end
|
3469
|
+
}
|
3470
|
+
|
3471
|
+
HashifyClusterNodeInfo = lambda { |str|
|
3472
|
+
arr = str.split(' ')
|
3473
|
+
{
|
3474
|
+
'node_id' => arr[0],
|
3475
|
+
'ip_port' => arr[1],
|
3476
|
+
'flags' => arr[2].split(','),
|
3477
|
+
'master_node_id' => arr[3],
|
3478
|
+
'ping_sent' => arr[4],
|
3479
|
+
'pong_recv' => arr[5],
|
3480
|
+
'config_epoch' => arr[6],
|
3481
|
+
'link_state' => arr[7],
|
3482
|
+
'slots' => arr[8].nil? ? nil : Range.new(*arr[8].split('-'))
|
2765
3483
|
}
|
3484
|
+
}
|
3485
|
+
|
3486
|
+
HashifyClusterSlots = lambda { |reply|
|
3487
|
+
reply.map do |arr|
|
3488
|
+
first_slot, last_slot = arr[0..1]
|
3489
|
+
master = { 'ip' => arr[2][0], 'port' => arr[2][1], 'node_id' => arr[2][2] }
|
3490
|
+
replicas = arr[3..-1].map { |r| { 'ip' => r[0], 'port' => r[1], 'node_id' => r[2] } }
|
3491
|
+
{
|
3492
|
+
'start_slot' => first_slot,
|
3493
|
+
'end_slot' => last_slot,
|
3494
|
+
'master' => master,
|
3495
|
+
'replicas' => replicas
|
3496
|
+
}
|
3497
|
+
end
|
3498
|
+
}
|
3499
|
+
|
3500
|
+
HashifyClusterNodes = lambda { |reply|
|
3501
|
+
reply.split(/[\r\n]+/).map { |str| HashifyClusterNodeInfo.call(str) }
|
3502
|
+
}
|
3503
|
+
|
3504
|
+
HashifyClusterSlaves = lambda { |reply|
|
3505
|
+
reply.map { |str| HashifyClusterNodeInfo.call(str) }
|
3506
|
+
}
|
3507
|
+
|
3508
|
+
Noop = ->(reply) { reply }
|
3509
|
+
|
3510
|
+
def _geoarguments(*args, options: nil, sort: nil, count: nil)
|
3511
|
+
args.push sort if sort
|
3512
|
+
args.push 'count', count if count
|
3513
|
+
args.push options if options
|
3514
|
+
args
|
3515
|
+
end
|
2766
3516
|
|
2767
3517
|
def _subscription(method, timeout, channels, block)
|
2768
3518
|
return @client.call([method] + channels) if subscribed?
|
@@ -2779,10 +3529,29 @@ private
|
|
2779
3529
|
end
|
2780
3530
|
end
|
2781
3531
|
|
3532
|
+
def _xread(args, keys, ids, blocking_timeout_msec)
|
3533
|
+
keys = keys.is_a?(Array) ? keys : [keys]
|
3534
|
+
ids = ids.is_a?(Array) ? ids : [ids]
|
3535
|
+
args << 'STREAMS'
|
3536
|
+
args.concat(keys)
|
3537
|
+
args.concat(ids)
|
3538
|
+
|
3539
|
+
synchronize do |client|
|
3540
|
+
if blocking_timeout_msec.nil?
|
3541
|
+
client.call(args, &HashifyStreams)
|
3542
|
+
elsif blocking_timeout_msec.to_f.zero?
|
3543
|
+
client.call_without_timeout(args, &HashifyStreams)
|
3544
|
+
else
|
3545
|
+
timeout = client.timeout.to_f + blocking_timeout_msec.to_f / 1000.0
|
3546
|
+
client.call_with_timeout(args, timeout, &HashifyStreams)
|
3547
|
+
end
|
3548
|
+
end
|
3549
|
+
end
|
2782
3550
|
end
|
2783
3551
|
|
2784
|
-
|
2785
|
-
|
2786
|
-
|
2787
|
-
|
2788
|
-
|
3552
|
+
require_relative "redis/version"
|
3553
|
+
require_relative "redis/connection"
|
3554
|
+
require_relative "redis/client"
|
3555
|
+
require_relative "redis/cluster"
|
3556
|
+
require_relative "redis/pipeline"
|
3557
|
+
require_relative "redis/subscribe"
|