redis 3.3.5 → 4.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +140 -2
- data/README.md +144 -79
- data/lib/redis.rb +1260 -403
- data/lib/redis/client.rb +152 -90
- data/lib/redis/cluster.rb +291 -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 +108 -0
- data/lib/redis/cluster/node_key.rb +31 -0
- data/lib/redis/cluster/node_loader.rb +37 -0
- data/lib/redis/cluster/option.rb +93 -0
- data/lib/redis/cluster/slot.rb +86 -0
- data/lib/redis/cluster/slot_loader.rb +49 -0
- data/lib/redis/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 +154 -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."
|
|
15
|
+
|
|
16
|
+
::Kernel.warn(message)
|
|
17
|
+
end
|
|
9
18
|
|
|
10
|
-
|
|
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,104 @@ 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)
|
|
558
|
+
keys.flatten!(1)
|
|
559
|
+
return 0 if keys.empty?
|
|
560
|
+
|
|
493
561
|
synchronize do |client|
|
|
494
562
|
client.call([:del] + keys)
|
|
495
563
|
end
|
|
496
564
|
end
|
|
497
565
|
|
|
498
|
-
#
|
|
566
|
+
# Unlink one or more keys.
|
|
499
567
|
#
|
|
500
|
-
# @param [String]
|
|
568
|
+
# @param [String, Array<String>] keys
|
|
569
|
+
# @return [Integer] number of keys that were unlinked
|
|
570
|
+
def unlink(*keys)
|
|
571
|
+
synchronize do |client|
|
|
572
|
+
client.call([:unlink] + keys)
|
|
573
|
+
end
|
|
574
|
+
end
|
|
575
|
+
|
|
576
|
+
# Determine how many of the keys exists.
|
|
577
|
+
#
|
|
578
|
+
# @param [String, Array<String>] keys
|
|
579
|
+
# @return [Integer]
|
|
580
|
+
def exists(*keys)
|
|
581
|
+
if !Redis.exists_returns_integer && keys.size == 1
|
|
582
|
+
if Redis.exists_returns_integer.nil?
|
|
583
|
+
message = "`Redis#exists(key)` will return an Integer in redis-rb 4.3. `exists?` returns a boolean, you " \
|
|
584
|
+
"should use it instead. To opt-in to the new behavior now you can set Redis.exists_returns_integer = " \
|
|
585
|
+
"true. To disable this message and keep the current (boolean) behaviour of 'exists' you can set " \
|
|
586
|
+
"`Redis.exists_returns_integer = false`, but this option will be removed in 5.0. " \
|
|
587
|
+
"(#{::Kernel.caller(1, 1).first})\n"
|
|
588
|
+
|
|
589
|
+
::Kernel.warn(message)
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
exists?(*keys)
|
|
593
|
+
else
|
|
594
|
+
_exists(*keys)
|
|
595
|
+
end
|
|
596
|
+
end
|
|
597
|
+
|
|
598
|
+
def _exists(*keys)
|
|
599
|
+
synchronize do |client|
|
|
600
|
+
client.call([:exists, *keys])
|
|
601
|
+
end
|
|
602
|
+
end
|
|
603
|
+
|
|
604
|
+
# Determine if any of the keys exists.
|
|
605
|
+
#
|
|
606
|
+
# @param [String, Array<String>] keys
|
|
501
607
|
# @return [Boolean]
|
|
502
|
-
def exists(
|
|
608
|
+
def exists?(*keys)
|
|
503
609
|
synchronize do |client|
|
|
504
|
-
client.call([:exists,
|
|
610
|
+
client.call([:exists, *keys]) do |value|
|
|
611
|
+
value > 0
|
|
612
|
+
end
|
|
505
613
|
end
|
|
506
614
|
end
|
|
507
615
|
|
|
@@ -512,7 +620,7 @@ class Redis
|
|
|
512
620
|
def keys(pattern = "*")
|
|
513
621
|
synchronize do |client|
|
|
514
622
|
client.call([:keys, pattern]) do |reply|
|
|
515
|
-
if reply.
|
|
623
|
+
if reply.is_a?(String)
|
|
516
624
|
reply.split(" ")
|
|
517
625
|
else
|
|
518
626
|
reply
|
|
@@ -538,7 +646,7 @@ class Redis
|
|
|
538
646
|
# # => "bar"
|
|
539
647
|
#
|
|
540
648
|
# @param [String] key
|
|
541
|
-
# @param [
|
|
649
|
+
# @param [Integer] db
|
|
542
650
|
# @return [Boolean] whether the key was moved or not
|
|
543
651
|
def move(key, db)
|
|
544
652
|
synchronize do |client|
|
|
@@ -602,36 +710,33 @@ class Redis
|
|
|
602
710
|
# - `:order => String`: combination of `ASC`, `DESC` and optionally `ALPHA`
|
|
603
711
|
# - `:store => String`: key to store the result at
|
|
604
712
|
#
|
|
605
|
-
# @return [Array<String>, Array<Array<String>>,
|
|
713
|
+
# @return [Array<String>, Array<Array<String>>, Integer]
|
|
606
714
|
# - when `:get` is not specified, or holds a single element, an array of elements
|
|
607
715
|
# - when `:get` is specified, and holds more than one element, an array of
|
|
608
716
|
# elements where every element is an array with the result for every
|
|
609
717
|
# element specified in `:get`
|
|
610
718
|
# - when `:store` is specified, the number of elements in the stored result
|
|
611
|
-
def sort(key,
|
|
612
|
-
args = []
|
|
719
|
+
def sort(key, by: nil, limit: nil, get: nil, order: nil, store: nil)
|
|
720
|
+
args = [:sort, key]
|
|
721
|
+
args << "BY" << by if by
|
|
613
722
|
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
args.concat(["LIMIT"] + limit) if limit
|
|
723
|
+
if limit
|
|
724
|
+
args << "LIMIT"
|
|
725
|
+
args.concat(limit)
|
|
726
|
+
end
|
|
619
727
|
|
|
620
|
-
get = Array(
|
|
621
|
-
|
|
728
|
+
get = Array(get)
|
|
729
|
+
get.each do |item|
|
|
730
|
+
args << "GET" << item
|
|
731
|
+
end
|
|
622
732
|
|
|
623
|
-
order = options[:order]
|
|
624
733
|
args.concat(order.split(" ")) if order
|
|
625
|
-
|
|
626
|
-
store = options[:store]
|
|
627
|
-
args.concat(["STORE", store]) if store
|
|
734
|
+
args << "STORE" << store if store
|
|
628
735
|
|
|
629
736
|
synchronize do |client|
|
|
630
|
-
client.call(
|
|
737
|
+
client.call(args) do |reply|
|
|
631
738
|
if get.size > 1 && !store
|
|
632
|
-
if reply
|
|
633
|
-
reply.each_slice(get.size).to_a
|
|
634
|
-
end
|
|
739
|
+
reply.each_slice(get.size).to_a if reply
|
|
635
740
|
else
|
|
636
741
|
reply
|
|
637
742
|
end
|
|
@@ -656,7 +761,7 @@ class Redis
|
|
|
656
761
|
# # => 4
|
|
657
762
|
#
|
|
658
763
|
# @param [String] key
|
|
659
|
-
# @return [
|
|
764
|
+
# @return [Integer] value after decrementing it
|
|
660
765
|
def decr(key)
|
|
661
766
|
synchronize do |client|
|
|
662
767
|
client.call([:decr, key])
|
|
@@ -670,8 +775,8 @@ class Redis
|
|
|
670
775
|
# # => 0
|
|
671
776
|
#
|
|
672
777
|
# @param [String] key
|
|
673
|
-
# @param [
|
|
674
|
-
# @return [
|
|
778
|
+
# @param [Integer] decrement
|
|
779
|
+
# @return [Integer] value after decrementing it
|
|
675
780
|
def decrby(key, decrement)
|
|
676
781
|
synchronize do |client|
|
|
677
782
|
client.call([:decrby, key, decrement])
|
|
@@ -685,7 +790,7 @@ class Redis
|
|
|
685
790
|
# # => 6
|
|
686
791
|
#
|
|
687
792
|
# @param [String] key
|
|
688
|
-
# @return [
|
|
793
|
+
# @return [Integer] value after incrementing it
|
|
689
794
|
def incr(key)
|
|
690
795
|
synchronize do |client|
|
|
691
796
|
client.call([:incr, key])
|
|
@@ -699,8 +804,8 @@ class Redis
|
|
|
699
804
|
# # => 10
|
|
700
805
|
#
|
|
701
806
|
# @param [String] key
|
|
702
|
-
# @param [
|
|
703
|
-
# @return [
|
|
807
|
+
# @param [Integer] increment
|
|
808
|
+
# @return [Integer] value after incrementing it
|
|
704
809
|
def incrby(key, increment)
|
|
705
810
|
synchronize do |client|
|
|
706
811
|
client.call([:incrby, key, increment])
|
|
@@ -727,41 +832,33 @@ class Redis
|
|
|
727
832
|
# @param [String] key
|
|
728
833
|
# @param [String] value
|
|
729
834
|
# @param [Hash] options
|
|
730
|
-
# - `:ex =>
|
|
731
|
-
# - `:px =>
|
|
835
|
+
# - `:ex => Integer`: Set the specified expire time, in seconds.
|
|
836
|
+
# - `:px => Integer`: Set the specified expire time, in milliseconds.
|
|
732
837
|
# - `:nx => true`: Only set the key if it does not already exist.
|
|
733
838
|
# - `:xx => true`: Only set the key if it already exist.
|
|
839
|
+
# - `:keepttl => true`: Retain the time to live associated with the key.
|
|
734
840
|
# @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
|
|
841
|
+
def set(key, value, ex: nil, px: nil, nx: nil, xx: nil, keepttl: nil)
|
|
842
|
+
args = [:set, key, value.to_s]
|
|
843
|
+
args << "EX" << ex if ex
|
|
844
|
+
args << "PX" << px if px
|
|
845
|
+
args << "NX" if nx
|
|
846
|
+
args << "XX" if xx
|
|
847
|
+
args << "KEEPTTL" if keepttl
|
|
749
848
|
|
|
750
849
|
synchronize do |client|
|
|
751
850
|
if nx || xx
|
|
752
|
-
client.call(
|
|
851
|
+
client.call(args, &BoolifySet)
|
|
753
852
|
else
|
|
754
|
-
client.call(
|
|
853
|
+
client.call(args)
|
|
755
854
|
end
|
|
756
855
|
end
|
|
757
856
|
end
|
|
758
857
|
|
|
759
|
-
alias :[]= :set
|
|
760
|
-
|
|
761
858
|
# Set the time to live in seconds of a key.
|
|
762
859
|
#
|
|
763
860
|
# @param [String] key
|
|
764
|
-
# @param [
|
|
861
|
+
# @param [Integer] ttl
|
|
765
862
|
# @param [String] value
|
|
766
863
|
# @return [String] `"OK"`
|
|
767
864
|
def setex(key, ttl, value)
|
|
@@ -773,7 +870,7 @@ class Redis
|
|
|
773
870
|
# Set the time to live in milliseconds of a key.
|
|
774
871
|
#
|
|
775
872
|
# @param [String] key
|
|
776
|
-
# @param [
|
|
873
|
+
# @param [Integer] ttl
|
|
777
874
|
# @param [String] value
|
|
778
875
|
# @return [String] `"OK"`
|
|
779
876
|
def psetex(key, ttl, value)
|
|
@@ -835,7 +932,7 @@ class Redis
|
|
|
835
932
|
# @see #mapped_msetnx
|
|
836
933
|
def msetnx(*args)
|
|
837
934
|
synchronize do |client|
|
|
838
|
-
client.call([:msetnx
|
|
935
|
+
client.call([:msetnx, *args], &Boolify)
|
|
839
936
|
end
|
|
840
937
|
end
|
|
841
938
|
|
|
@@ -863,12 +960,10 @@ class Redis
|
|
|
863
960
|
end
|
|
864
961
|
end
|
|
865
962
|
|
|
866
|
-
alias :[] :get
|
|
867
|
-
|
|
868
963
|
# Get the values of all the given keys.
|
|
869
964
|
#
|
|
870
965
|
# @example
|
|
871
|
-
# redis.mget("key1", "
|
|
966
|
+
# redis.mget("key1", "key2")
|
|
872
967
|
# # => ["v1", "v2"]
|
|
873
968
|
#
|
|
874
969
|
# @param [Array<String>] keys
|
|
@@ -877,7 +972,7 @@ class Redis
|
|
|
877
972
|
# @see #mapped_mget
|
|
878
973
|
def mget(*keys, &blk)
|
|
879
974
|
synchronize do |client|
|
|
880
|
-
client.call([:mget
|
|
975
|
+
client.call([:mget, *keys], &blk)
|
|
881
976
|
end
|
|
882
977
|
end
|
|
883
978
|
|
|
@@ -893,7 +988,7 @@ class Redis
|
|
|
893
988
|
# @see #mget
|
|
894
989
|
def mapped_mget(*keys)
|
|
895
990
|
mget(*keys) do |reply|
|
|
896
|
-
if reply.
|
|
991
|
+
if reply.is_a?(Array)
|
|
897
992
|
Hash[keys.zip(reply)]
|
|
898
993
|
else
|
|
899
994
|
reply
|
|
@@ -904,9 +999,9 @@ class Redis
|
|
|
904
999
|
# Overwrite part of a string at key starting at the specified offset.
|
|
905
1000
|
#
|
|
906
1001
|
# @param [String] key
|
|
907
|
-
# @param [
|
|
1002
|
+
# @param [Integer] offset byte offset
|
|
908
1003
|
# @param [String] value
|
|
909
|
-
# @return [
|
|
1004
|
+
# @return [Integer] length of the string after it was modified
|
|
910
1005
|
def setrange(key, offset, value)
|
|
911
1006
|
synchronize do |client|
|
|
912
1007
|
client.call([:setrange, key, offset, value.to_s])
|
|
@@ -916,10 +1011,10 @@ class Redis
|
|
|
916
1011
|
# Get a substring of the string stored at a key.
|
|
917
1012
|
#
|
|
918
1013
|
# @param [String] key
|
|
919
|
-
# @param [
|
|
920
|
-
# @param [
|
|
1014
|
+
# @param [Integer] start zero-based start offset
|
|
1015
|
+
# @param [Integer] stop zero-based end offset. Use -1 for representing
|
|
921
1016
|
# the end of the string
|
|
922
|
-
# @return [
|
|
1017
|
+
# @return [Integer] `0` or `1`
|
|
923
1018
|
def getrange(key, start, stop)
|
|
924
1019
|
synchronize do |client|
|
|
925
1020
|
client.call([:getrange, key, start, stop])
|
|
@@ -929,9 +1024,9 @@ class Redis
|
|
|
929
1024
|
# Sets or clears the bit at offset in the string value stored at key.
|
|
930
1025
|
#
|
|
931
1026
|
# @param [String] key
|
|
932
|
-
# @param [
|
|
933
|
-
# @param [
|
|
934
|
-
# @return [
|
|
1027
|
+
# @param [Integer] offset bit offset
|
|
1028
|
+
# @param [Integer] value bit value `0` or `1`
|
|
1029
|
+
# @return [Integer] the original bit value stored at `offset`
|
|
935
1030
|
def setbit(key, offset, value)
|
|
936
1031
|
synchronize do |client|
|
|
937
1032
|
client.call([:setbit, key, offset, value])
|
|
@@ -941,8 +1036,8 @@ class Redis
|
|
|
941
1036
|
# Returns the bit value at offset in the string value stored at key.
|
|
942
1037
|
#
|
|
943
1038
|
# @param [String] key
|
|
944
|
-
# @param [
|
|
945
|
-
# @return [
|
|
1039
|
+
# @param [Integer] offset bit offset
|
|
1040
|
+
# @return [Integer] `0` or `1`
|
|
946
1041
|
def getbit(key, offset)
|
|
947
1042
|
synchronize do |client|
|
|
948
1043
|
client.call([:getbit, key, offset])
|
|
@@ -953,7 +1048,7 @@ class Redis
|
|
|
953
1048
|
#
|
|
954
1049
|
# @param [String] key
|
|
955
1050
|
# @param [String] value value to append
|
|
956
|
-
# @return [
|
|
1051
|
+
# @return [Integer] length of the string after appending
|
|
957
1052
|
def append(key, value)
|
|
958
1053
|
synchronize do |client|
|
|
959
1054
|
client.call([:append, key, value])
|
|
@@ -963,9 +1058,9 @@ class Redis
|
|
|
963
1058
|
# Count the number of set bits in a range of the string value stored at key.
|
|
964
1059
|
#
|
|
965
1060
|
# @param [String] key
|
|
966
|
-
# @param [
|
|
967
|
-
# @param [
|
|
968
|
-
# @return [
|
|
1061
|
+
# @param [Integer] start start index
|
|
1062
|
+
# @param [Integer] stop stop index
|
|
1063
|
+
# @return [Integer] the number of bits set to 1
|
|
969
1064
|
def bitcount(key, start = 0, stop = -1)
|
|
970
1065
|
synchronize do |client|
|
|
971
1066
|
client.call([:bitcount, key, start, stop])
|
|
@@ -977,25 +1072,23 @@ class Redis
|
|
|
977
1072
|
# @param [String] operation e.g. `and`, `or`, `xor`, `not`
|
|
978
1073
|
# @param [String] destkey destination key
|
|
979
1074
|
# @param [String, Array<String>] keys one or more source keys to perform `operation`
|
|
980
|
-
# @return [
|
|
1075
|
+
# @return [Integer] the length of the string stored in `destkey`
|
|
981
1076
|
def bitop(operation, destkey, *keys)
|
|
982
1077
|
synchronize do |client|
|
|
983
|
-
client.call([:bitop, operation, destkey
|
|
1078
|
+
client.call([:bitop, operation, destkey, *keys])
|
|
984
1079
|
end
|
|
985
1080
|
end
|
|
986
1081
|
|
|
987
1082
|
# Return the position of the first bit set to 1 or 0 in a string.
|
|
988
1083
|
#
|
|
989
1084
|
# @param [String] key
|
|
990
|
-
# @param [
|
|
991
|
-
# @param [
|
|
992
|
-
# @param [
|
|
993
|
-
# @return [
|
|
1085
|
+
# @param [Integer] bit whether to look for the first 1 or 0 bit
|
|
1086
|
+
# @param [Integer] start start index
|
|
1087
|
+
# @param [Integer] stop stop index
|
|
1088
|
+
# @return [Integer] the position of the first 1/0 bit.
|
|
994
1089
|
# -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
|
|
1090
|
+
def bitpos(key, bit, start = nil, stop = nil)
|
|
1091
|
+
raise(ArgumentError, 'stop parameter specified without start parameter') if stop && !start
|
|
999
1092
|
|
|
1000
1093
|
synchronize do |client|
|
|
1001
1094
|
command = [:bitpos, key, bit]
|
|
@@ -1020,7 +1113,7 @@ class Redis
|
|
|
1020
1113
|
# Get the length of the value stored in a key.
|
|
1021
1114
|
#
|
|
1022
1115
|
# @param [String] key
|
|
1023
|
-
# @return [
|
|
1116
|
+
# @return [Integer] the length of the value stored in the key, or 0
|
|
1024
1117
|
# if the key does not exist
|
|
1025
1118
|
def strlen(key)
|
|
1026
1119
|
synchronize do |client|
|
|
@@ -1031,7 +1124,7 @@ class Redis
|
|
|
1031
1124
|
# Get the length of a list.
|
|
1032
1125
|
#
|
|
1033
1126
|
# @param [String] key
|
|
1034
|
-
# @return [
|
|
1127
|
+
# @return [Integer]
|
|
1035
1128
|
def llen(key)
|
|
1036
1129
|
synchronize do |client|
|
|
1037
1130
|
client.call([:llen, key])
|
|
@@ -1041,8 +1134,8 @@ class Redis
|
|
|
1041
1134
|
# Prepend one or more values to a list, creating the list if it doesn't exist
|
|
1042
1135
|
#
|
|
1043
1136
|
# @param [String] key
|
|
1044
|
-
# @param [String, Array] value string value, or array of string values to push
|
|
1045
|
-
# @return [
|
|
1137
|
+
# @param [String, Array<String>] value string value, or array of string values to push
|
|
1138
|
+
# @return [Integer] the length of the list after the push operation
|
|
1046
1139
|
def lpush(key, value)
|
|
1047
1140
|
synchronize do |client|
|
|
1048
1141
|
client.call([:lpush, key, value])
|
|
@@ -1053,7 +1146,7 @@ class Redis
|
|
|
1053
1146
|
#
|
|
1054
1147
|
# @param [String] key
|
|
1055
1148
|
# @param [String] value
|
|
1056
|
-
# @return [
|
|
1149
|
+
# @return [Integer] the length of the list after the push operation
|
|
1057
1150
|
def lpushx(key, value)
|
|
1058
1151
|
synchronize do |client|
|
|
1059
1152
|
client.call([:lpushx, key, value])
|
|
@@ -1063,8 +1156,8 @@ class Redis
|
|
|
1063
1156
|
# Append one or more values to a list, creating the list if it doesn't exist
|
|
1064
1157
|
#
|
|
1065
1158
|
# @param [String] key
|
|
1066
|
-
# @param [String] value
|
|
1067
|
-
# @return [
|
|
1159
|
+
# @param [String, Array<String>] value string value, or array of string values to push
|
|
1160
|
+
# @return [Integer] the length of the list after the push operation
|
|
1068
1161
|
def rpush(key, value)
|
|
1069
1162
|
synchronize do |client|
|
|
1070
1163
|
client.call([:rpush, key, value])
|
|
@@ -1075,30 +1168,36 @@ class Redis
|
|
|
1075
1168
|
#
|
|
1076
1169
|
# @param [String] key
|
|
1077
1170
|
# @param [String] value
|
|
1078
|
-
# @return [
|
|
1171
|
+
# @return [Integer] the length of the list after the push operation
|
|
1079
1172
|
def rpushx(key, value)
|
|
1080
1173
|
synchronize do |client|
|
|
1081
1174
|
client.call([:rpushx, key, value])
|
|
1082
1175
|
end
|
|
1083
1176
|
end
|
|
1084
1177
|
|
|
1085
|
-
# Remove and get the first
|
|
1178
|
+
# Remove and get the first elements in a list.
|
|
1086
1179
|
#
|
|
1087
1180
|
# @param [String] key
|
|
1088
|
-
# @
|
|
1089
|
-
|
|
1181
|
+
# @param [Integer] count number of elements to remove
|
|
1182
|
+
# @return [String, Array<String>] the values of the first elements
|
|
1183
|
+
def lpop(key, count = nil)
|
|
1090
1184
|
synchronize do |client|
|
|
1091
|
-
|
|
1185
|
+
command = [:lpop, key]
|
|
1186
|
+
command << count if count
|
|
1187
|
+
client.call(command)
|
|
1092
1188
|
end
|
|
1093
1189
|
end
|
|
1094
1190
|
|
|
1095
|
-
# Remove and get the last
|
|
1191
|
+
# Remove and get the last elements in a list.
|
|
1096
1192
|
#
|
|
1097
1193
|
# @param [String] key
|
|
1098
|
-
# @
|
|
1099
|
-
|
|
1194
|
+
# @param [Integer] count number of elements to remove
|
|
1195
|
+
# @return [String, Array<String>] the values of the last elements
|
|
1196
|
+
def rpop(key, count = nil)
|
|
1100
1197
|
synchronize do |client|
|
|
1101
|
-
|
|
1198
|
+
command = [:rpop, key]
|
|
1199
|
+
command << count if count
|
|
1200
|
+
client.call(command)
|
|
1102
1201
|
end
|
|
1103
1202
|
end
|
|
1104
1203
|
|
|
@@ -1113,28 +1212,27 @@ class Redis
|
|
|
1113
1212
|
end
|
|
1114
1213
|
end
|
|
1115
1214
|
|
|
1116
|
-
def _bpop(cmd, args)
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
case args.last
|
|
1120
|
-
when Hash
|
|
1215
|
+
def _bpop(cmd, args, &blk)
|
|
1216
|
+
timeout = if args.last.is_a?(Hash)
|
|
1121
1217
|
options = args.pop
|
|
1122
|
-
|
|
1218
|
+
options[:timeout]
|
|
1219
|
+
elsif args.last.respond_to?(:to_int)
|
|
1123
1220
|
# Issue deprecation notice in obnoxious mode...
|
|
1124
|
-
|
|
1221
|
+
args.pop.to_int
|
|
1125
1222
|
end
|
|
1126
1223
|
|
|
1224
|
+
timeout ||= 0
|
|
1225
|
+
|
|
1127
1226
|
if args.size > 1
|
|
1128
1227
|
# Issue deprecation notice in obnoxious mode...
|
|
1129
1228
|
end
|
|
1130
1229
|
|
|
1131
1230
|
keys = args.flatten
|
|
1132
|
-
timeout = options[:timeout] || 0
|
|
1133
1231
|
|
|
1134
1232
|
synchronize do |client|
|
|
1135
1233
|
command = [cmd, keys, timeout]
|
|
1136
1234
|
timeout += client.timeout if timeout > 0
|
|
1137
|
-
client.call_with_timeout(command, timeout)
|
|
1235
|
+
client.call_with_timeout(command, timeout, &blk)
|
|
1138
1236
|
end
|
|
1139
1237
|
end
|
|
1140
1238
|
|
|
@@ -1154,7 +1252,7 @@ class Redis
|
|
|
1154
1252
|
# @param [String, Array<String>] keys one or more keys to perform the
|
|
1155
1253
|
# blocking pop on
|
|
1156
1254
|
# @param [Hash] options
|
|
1157
|
-
# - `:timeout =>
|
|
1255
|
+
# - `:timeout => Integer`: timeout in seconds, defaults to no timeout
|
|
1158
1256
|
#
|
|
1159
1257
|
# @return [nil, [String, String]]
|
|
1160
1258
|
# - `nil` when the operation timed out
|
|
@@ -1168,7 +1266,7 @@ class Redis
|
|
|
1168
1266
|
# @param [String, Array<String>] keys one or more keys to perform the
|
|
1169
1267
|
# blocking pop on
|
|
1170
1268
|
# @param [Hash] options
|
|
1171
|
-
# - `:timeout =>
|
|
1269
|
+
# - `:timeout => Integer`: timeout in seconds, defaults to no timeout
|
|
1172
1270
|
#
|
|
1173
1271
|
# @return [nil, [String, String]]
|
|
1174
1272
|
# - `nil` when the operation timed out
|
|
@@ -1185,20 +1283,12 @@ class Redis
|
|
|
1185
1283
|
# @param [String] source source key
|
|
1186
1284
|
# @param [String] destination destination key
|
|
1187
1285
|
# @param [Hash] options
|
|
1188
|
-
# - `:timeout =>
|
|
1286
|
+
# - `:timeout => Integer`: timeout in seconds, defaults to no timeout
|
|
1189
1287
|
#
|
|
1190
1288
|
# @return [nil, String]
|
|
1191
1289
|
# - `nil` when the operation timed out
|
|
1192
1290
|
# - 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
|
-
|
|
1291
|
+
def brpoplpush(source, destination, deprecated_timeout = 0, timeout: deprecated_timeout)
|
|
1202
1292
|
synchronize do |client|
|
|
1203
1293
|
command = [:brpoplpush, source, destination, timeout]
|
|
1204
1294
|
timeout += client.timeout if timeout > 0
|
|
@@ -1209,7 +1299,7 @@ class Redis
|
|
|
1209
1299
|
# Get an element from a list by its index.
|
|
1210
1300
|
#
|
|
1211
1301
|
# @param [String] key
|
|
1212
|
-
# @param [
|
|
1302
|
+
# @param [Integer] index
|
|
1213
1303
|
# @return [String]
|
|
1214
1304
|
def lindex(key, index)
|
|
1215
1305
|
synchronize do |client|
|
|
@@ -1223,7 +1313,7 @@ class Redis
|
|
|
1223
1313
|
# @param [String, Symbol] where `BEFORE` or `AFTER`
|
|
1224
1314
|
# @param [String] pivot reference element
|
|
1225
1315
|
# @param [String] value
|
|
1226
|
-
# @return [
|
|
1316
|
+
# @return [Integer] length of the list after the insert operation, or `-1`
|
|
1227
1317
|
# when the element `pivot` was not found
|
|
1228
1318
|
def linsert(key, where, pivot, value)
|
|
1229
1319
|
synchronize do |client|
|
|
@@ -1234,8 +1324,8 @@ class Redis
|
|
|
1234
1324
|
# Get a range of elements from a list.
|
|
1235
1325
|
#
|
|
1236
1326
|
# @param [String] key
|
|
1237
|
-
# @param [
|
|
1238
|
-
# @param [
|
|
1327
|
+
# @param [Integer] start start index
|
|
1328
|
+
# @param [Integer] stop stop index
|
|
1239
1329
|
# @return [Array<String>]
|
|
1240
1330
|
def lrange(key, start, stop)
|
|
1241
1331
|
synchronize do |client|
|
|
@@ -1246,12 +1336,12 @@ class Redis
|
|
|
1246
1336
|
# Remove elements from a list.
|
|
1247
1337
|
#
|
|
1248
1338
|
# @param [String] key
|
|
1249
|
-
# @param [
|
|
1339
|
+
# @param [Integer] count number of elements to remove. Use a positive
|
|
1250
1340
|
# value to remove the first `count` occurrences of `value`. A negative
|
|
1251
1341
|
# value to remove the last `count` occurrences of `value`. Or zero, to
|
|
1252
1342
|
# remove all occurrences of `value` from the list.
|
|
1253
1343
|
# @param [String] value
|
|
1254
|
-
# @return [
|
|
1344
|
+
# @return [Integer] the number of removed elements
|
|
1255
1345
|
def lrem(key, count, value)
|
|
1256
1346
|
synchronize do |client|
|
|
1257
1347
|
client.call([:lrem, key, count, value])
|
|
@@ -1261,7 +1351,7 @@ class Redis
|
|
|
1261
1351
|
# Set the value of an element in a list by its index.
|
|
1262
1352
|
#
|
|
1263
1353
|
# @param [String] key
|
|
1264
|
-
# @param [
|
|
1354
|
+
# @param [Integer] index
|
|
1265
1355
|
# @param [String] value
|
|
1266
1356
|
# @return [String] `OK`
|
|
1267
1357
|
def lset(key, index, value)
|
|
@@ -1273,8 +1363,8 @@ class Redis
|
|
|
1273
1363
|
# Trim a list to the specified range.
|
|
1274
1364
|
#
|
|
1275
1365
|
# @param [String] key
|
|
1276
|
-
# @param [
|
|
1277
|
-
# @param [
|
|
1366
|
+
# @param [Integer] start start index
|
|
1367
|
+
# @param [Integer] stop stop index
|
|
1278
1368
|
# @return [String] `OK`
|
|
1279
1369
|
def ltrim(key, start, stop)
|
|
1280
1370
|
synchronize do |client|
|
|
@@ -1285,7 +1375,7 @@ class Redis
|
|
|
1285
1375
|
# Get the number of members in a set.
|
|
1286
1376
|
#
|
|
1287
1377
|
# @param [String] key
|
|
1288
|
-
# @return [
|
|
1378
|
+
# @return [Integer]
|
|
1289
1379
|
def scard(key)
|
|
1290
1380
|
synchronize do |client|
|
|
1291
1381
|
client.call([:scard, key])
|
|
@@ -1296,8 +1386,8 @@ class Redis
|
|
|
1296
1386
|
#
|
|
1297
1387
|
# @param [String] key
|
|
1298
1388
|
# @param [String, Array<String>] member one member, or array of members
|
|
1299
|
-
# @return [Boolean,
|
|
1300
|
-
# holding whether or not adding the member succeeded, or `
|
|
1389
|
+
# @return [Boolean, Integer] `Boolean` when a single member is specified,
|
|
1390
|
+
# holding whether or not adding the member succeeded, or `Integer` when an
|
|
1301
1391
|
# array of members is specified, holding the number of members that were
|
|
1302
1392
|
# successfully added
|
|
1303
1393
|
def sadd(key, member)
|
|
@@ -1318,8 +1408,8 @@ class Redis
|
|
|
1318
1408
|
#
|
|
1319
1409
|
# @param [String] key
|
|
1320
1410
|
# @param [String, Array<String>] member one member, or array of members
|
|
1321
|
-
# @return [Boolean,
|
|
1322
|
-
# holding whether or not removing the member succeeded, or `
|
|
1411
|
+
# @return [Boolean, Integer] `Boolean` when a single member is specified,
|
|
1412
|
+
# holding whether or not removing the member succeeded, or `Integer` when an
|
|
1323
1413
|
# array of members is specified, holding the number of members that were
|
|
1324
1414
|
# successfully removed
|
|
1325
1415
|
def srem(key, member)
|
|
@@ -1340,7 +1430,7 @@ class Redis
|
|
|
1340
1430
|
#
|
|
1341
1431
|
# @param [String] key
|
|
1342
1432
|
# @return [String]
|
|
1343
|
-
# @param [
|
|
1433
|
+
# @param [Integer] count
|
|
1344
1434
|
def spop(key, count = nil)
|
|
1345
1435
|
synchronize do |client|
|
|
1346
1436
|
if count.nil?
|
|
@@ -1354,7 +1444,7 @@ class Redis
|
|
|
1354
1444
|
# Get one or more random members from a set.
|
|
1355
1445
|
#
|
|
1356
1446
|
# @param [String] key
|
|
1357
|
-
# @param [
|
|
1447
|
+
# @param [Integer] count
|
|
1358
1448
|
# @return [String]
|
|
1359
1449
|
def srandmember(key, count = nil)
|
|
1360
1450
|
synchronize do |client|
|
|
@@ -1405,7 +1495,7 @@ class Redis
|
|
|
1405
1495
|
# @return [Array<String>] members in the difference
|
|
1406
1496
|
def sdiff(*keys)
|
|
1407
1497
|
synchronize do |client|
|
|
1408
|
-
client.call([:sdiff
|
|
1498
|
+
client.call([:sdiff, *keys])
|
|
1409
1499
|
end
|
|
1410
1500
|
end
|
|
1411
1501
|
|
|
@@ -1413,10 +1503,10 @@ class Redis
|
|
|
1413
1503
|
#
|
|
1414
1504
|
# @param [String] destination destination key
|
|
1415
1505
|
# @param [String, Array<String>] keys keys pointing to sets to subtract
|
|
1416
|
-
# @return [
|
|
1506
|
+
# @return [Integer] number of elements in the resulting set
|
|
1417
1507
|
def sdiffstore(destination, *keys)
|
|
1418
1508
|
synchronize do |client|
|
|
1419
|
-
client.call([:sdiffstore, destination
|
|
1509
|
+
client.call([:sdiffstore, destination, *keys])
|
|
1420
1510
|
end
|
|
1421
1511
|
end
|
|
1422
1512
|
|
|
@@ -1426,7 +1516,7 @@ class Redis
|
|
|
1426
1516
|
# @return [Array<String>] members in the intersection
|
|
1427
1517
|
def sinter(*keys)
|
|
1428
1518
|
synchronize do |client|
|
|
1429
|
-
client.call([:sinter
|
|
1519
|
+
client.call([:sinter, *keys])
|
|
1430
1520
|
end
|
|
1431
1521
|
end
|
|
1432
1522
|
|
|
@@ -1434,10 +1524,10 @@ class Redis
|
|
|
1434
1524
|
#
|
|
1435
1525
|
# @param [String] destination destination key
|
|
1436
1526
|
# @param [String, Array<String>] keys keys pointing to sets to intersect
|
|
1437
|
-
# @return [
|
|
1527
|
+
# @return [Integer] number of elements in the resulting set
|
|
1438
1528
|
def sinterstore(destination, *keys)
|
|
1439
1529
|
synchronize do |client|
|
|
1440
|
-
client.call([:sinterstore, destination
|
|
1530
|
+
client.call([:sinterstore, destination, *keys])
|
|
1441
1531
|
end
|
|
1442
1532
|
end
|
|
1443
1533
|
|
|
@@ -1447,7 +1537,7 @@ class Redis
|
|
|
1447
1537
|
# @return [Array<String>] members in the union
|
|
1448
1538
|
def sunion(*keys)
|
|
1449
1539
|
synchronize do |client|
|
|
1450
|
-
client.call([:sunion
|
|
1540
|
+
client.call([:sunion, *keys])
|
|
1451
1541
|
end
|
|
1452
1542
|
end
|
|
1453
1543
|
|
|
@@ -1455,10 +1545,10 @@ class Redis
|
|
|
1455
1545
|
#
|
|
1456
1546
|
# @param [String] destination destination key
|
|
1457
1547
|
# @param [String, Array<String>] keys keys pointing to sets to unify
|
|
1458
|
-
# @return [
|
|
1548
|
+
# @return [Integer] number of elements in the resulting set
|
|
1459
1549
|
def sunionstore(destination, *keys)
|
|
1460
1550
|
synchronize do |client|
|
|
1461
|
-
client.call([:sunionstore, destination
|
|
1551
|
+
client.call([:sunionstore, destination, *keys])
|
|
1462
1552
|
end
|
|
1463
1553
|
end
|
|
1464
1554
|
|
|
@@ -1469,7 +1559,7 @@ class Redis
|
|
|
1469
1559
|
# # => 4
|
|
1470
1560
|
#
|
|
1471
1561
|
# @param [String] key
|
|
1472
|
-
# @return [
|
|
1562
|
+
# @return [Integer]
|
|
1473
1563
|
def zcard(key)
|
|
1474
1564
|
synchronize do |client|
|
|
1475
1565
|
client.call([:zcard, key])
|
|
@@ -1500,38 +1590,27 @@ class Redis
|
|
|
1500
1590
|
# - `:incr => true`: When this option is specified ZADD acts like
|
|
1501
1591
|
# ZINCRBY; only one score-element pair can be specified in this mode
|
|
1502
1592
|
#
|
|
1503
|
-
# @return [Boolean,
|
|
1593
|
+
# @return [Boolean, Integer, Float]
|
|
1504
1594
|
# - `Boolean` when a single pair is specified, holding whether or not it was
|
|
1505
1595
|
# **added** to the sorted set.
|
|
1506
|
-
# - `
|
|
1596
|
+
# - `Integer` when an array of pairs is specified, holding the number of
|
|
1507
1597
|
# pairs that were **added** to the sorted set.
|
|
1508
1598
|
# - `Float` when option :incr is specified, holding the score of the member
|
|
1509
1599
|
# 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
|
|
1600
|
+
def zadd(key, *args, nx: nil, xx: nil, ch: nil, incr: nil)
|
|
1601
|
+
command = [:zadd, key]
|
|
1602
|
+
command << "NX" if nx
|
|
1603
|
+
command << "XX" if xx
|
|
1604
|
+
command << "CH" if ch
|
|
1605
|
+
command << "INCR" if incr
|
|
1527
1606
|
|
|
1528
1607
|
synchronize do |client|
|
|
1529
1608
|
if args.size == 1 && args[0].is_a?(Array)
|
|
1530
1609
|
# Variadic: return float if INCR, integer if !INCR
|
|
1531
|
-
client.call(
|
|
1610
|
+
client.call(command + args[0], &(incr ? Floatify : nil))
|
|
1532
1611
|
elsif args.size == 2
|
|
1533
1612
|
# Single pair: return float if INCR, boolean if !INCR
|
|
1534
|
-
client.call(
|
|
1613
|
+
client.call(command + args, &(incr ? Floatify : Boolify))
|
|
1535
1614
|
else
|
|
1536
1615
|
raise ArgumentError, "wrong number of arguments"
|
|
1537
1616
|
end
|
|
@@ -1566,10 +1645,10 @@ class Redis
|
|
|
1566
1645
|
# - a single member
|
|
1567
1646
|
# - an array of members
|
|
1568
1647
|
#
|
|
1569
|
-
# @return [Boolean,
|
|
1648
|
+
# @return [Boolean, Integer]
|
|
1570
1649
|
# - `Boolean` when a single member is specified, holding whether or not it
|
|
1571
1650
|
# was removed from the sorted set
|
|
1572
|
-
# - `
|
|
1651
|
+
# - `Integer` when an array of pairs is specified, holding the number of
|
|
1573
1652
|
# members that were removed to the sorted set
|
|
1574
1653
|
def zrem(key, member)
|
|
1575
1654
|
synchronize do |client|
|
|
@@ -1585,6 +1664,90 @@ class Redis
|
|
|
1585
1664
|
end
|
|
1586
1665
|
end
|
|
1587
1666
|
|
|
1667
|
+
# Removes and returns up to count members with the highest scores in the sorted set stored at key.
|
|
1668
|
+
#
|
|
1669
|
+
# @example Popping a member
|
|
1670
|
+
# redis.zpopmax('zset')
|
|
1671
|
+
# #=> ['b', 2.0]
|
|
1672
|
+
# @example With count option
|
|
1673
|
+
# redis.zpopmax('zset', 2)
|
|
1674
|
+
# #=> [['b', 2.0], ['a', 1.0]]
|
|
1675
|
+
#
|
|
1676
|
+
# @params key [String] a key of the sorted set
|
|
1677
|
+
# @params count [Integer] a number of members
|
|
1678
|
+
#
|
|
1679
|
+
# @return [Array<String, Float>] element and score pair if count is not specified
|
|
1680
|
+
# @return [Array<Array<String, Float>>] list of popped elements and scores
|
|
1681
|
+
def zpopmax(key, count = nil)
|
|
1682
|
+
synchronize do |client|
|
|
1683
|
+
members = client.call([:zpopmax, key, count].compact, &FloatifyPairs)
|
|
1684
|
+
count.to_i > 1 ? members : members.first
|
|
1685
|
+
end
|
|
1686
|
+
end
|
|
1687
|
+
|
|
1688
|
+
# Removes and returns up to count members with the lowest scores in the sorted set stored at key.
|
|
1689
|
+
#
|
|
1690
|
+
# @example Popping a member
|
|
1691
|
+
# redis.zpopmin('zset')
|
|
1692
|
+
# #=> ['a', 1.0]
|
|
1693
|
+
# @example With count option
|
|
1694
|
+
# redis.zpopmin('zset', 2)
|
|
1695
|
+
# #=> [['a', 1.0], ['b', 2.0]]
|
|
1696
|
+
#
|
|
1697
|
+
# @params key [String] a key of the sorted set
|
|
1698
|
+
# @params count [Integer] a number of members
|
|
1699
|
+
#
|
|
1700
|
+
# @return [Array<String, Float>] element and score pair if count is not specified
|
|
1701
|
+
# @return [Array<Array<String, Float>>] list of popped elements and scores
|
|
1702
|
+
def zpopmin(key, count = nil)
|
|
1703
|
+
synchronize do |client|
|
|
1704
|
+
members = client.call([:zpopmin, key, count].compact, &FloatifyPairs)
|
|
1705
|
+
count.to_i > 1 ? members : members.first
|
|
1706
|
+
end
|
|
1707
|
+
end
|
|
1708
|
+
|
|
1709
|
+
# Removes and returns up to count members with the highest scores in the sorted set stored at keys,
|
|
1710
|
+
# or block until one is available.
|
|
1711
|
+
#
|
|
1712
|
+
# @example Popping a member from a sorted set
|
|
1713
|
+
# redis.bzpopmax('zset', 1)
|
|
1714
|
+
# #=> ['zset', 'b', 2.0]
|
|
1715
|
+
# @example Popping a member from multiple sorted sets
|
|
1716
|
+
# redis.bzpopmax('zset1', 'zset2', 1)
|
|
1717
|
+
# #=> ['zset1', 'b', 2.0]
|
|
1718
|
+
#
|
|
1719
|
+
# @params keys [Array<String>] one or multiple keys of the sorted sets
|
|
1720
|
+
# @params timeout [Integer] the maximum number of seconds to block
|
|
1721
|
+
#
|
|
1722
|
+
# @return [Array<String, String, Float>] a touple of key, member and score
|
|
1723
|
+
# @return [nil] when no element could be popped and the timeout expired
|
|
1724
|
+
def bzpopmax(*args)
|
|
1725
|
+
_bpop(:bzpopmax, args) do |reply|
|
|
1726
|
+
reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
|
|
1727
|
+
end
|
|
1728
|
+
end
|
|
1729
|
+
|
|
1730
|
+
# Removes and returns up to count members with the lowest scores in the sorted set stored at keys,
|
|
1731
|
+
# or block until one is available.
|
|
1732
|
+
#
|
|
1733
|
+
# @example Popping a member from a sorted set
|
|
1734
|
+
# redis.bzpopmin('zset', 1)
|
|
1735
|
+
# #=> ['zset', 'a', 1.0]
|
|
1736
|
+
# @example Popping a member from multiple sorted sets
|
|
1737
|
+
# redis.bzpopmin('zset1', 'zset2', 1)
|
|
1738
|
+
# #=> ['zset1', 'a', 1.0]
|
|
1739
|
+
#
|
|
1740
|
+
# @params keys [Array<String>] one or multiple keys of the sorted sets
|
|
1741
|
+
# @params timeout [Integer] the maximum number of seconds to block
|
|
1742
|
+
#
|
|
1743
|
+
# @return [Array<String, String, Float>] a touple of key, member and score
|
|
1744
|
+
# @return [nil] when no element could be popped and the timeout expired
|
|
1745
|
+
def bzpopmin(*args)
|
|
1746
|
+
_bpop(:bzpopmin, args) do |reply|
|
|
1747
|
+
reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
|
|
1748
|
+
end
|
|
1749
|
+
end
|
|
1750
|
+
|
|
1588
1751
|
# Get the score associated with the given member in a sorted set.
|
|
1589
1752
|
#
|
|
1590
1753
|
# @example Get the score for member "a"
|
|
@@ -1610,18 +1773,16 @@ class Redis
|
|
|
1610
1773
|
# # => [["a", 32.0], ["b", 64.0]]
|
|
1611
1774
|
#
|
|
1612
1775
|
# @param [String] key
|
|
1613
|
-
# @param [
|
|
1614
|
-
# @param [
|
|
1776
|
+
# @param [Integer] start start index
|
|
1777
|
+
# @param [Integer] stop stop index
|
|
1615
1778
|
# @param [Hash] options
|
|
1616
1779
|
# - `:with_scores => true`: include scores in output
|
|
1617
1780
|
#
|
|
1618
1781
|
# @return [Array<String>, Array<[String, Float]>]
|
|
1619
1782
|
# - when `:with_scores` is not specified, an array of members
|
|
1620
1783
|
# - 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]
|
|
1784
|
+
def zrange(key, start, stop, withscores: false, with_scores: withscores)
|
|
1785
|
+
args = [:zrange, key, start, stop]
|
|
1625
1786
|
|
|
1626
1787
|
if with_scores
|
|
1627
1788
|
args << "WITHSCORES"
|
|
@@ -1629,7 +1790,7 @@ class Redis
|
|
|
1629
1790
|
end
|
|
1630
1791
|
|
|
1631
1792
|
synchronize do |client|
|
|
1632
|
-
client.call(
|
|
1793
|
+
client.call(args, &block)
|
|
1633
1794
|
end
|
|
1634
1795
|
end
|
|
1635
1796
|
|
|
@@ -1644,10 +1805,8 @@ class Redis
|
|
|
1644
1805
|
# # => [["b", 64.0], ["a", 32.0]]
|
|
1645
1806
|
#
|
|
1646
1807
|
# @see #zrange
|
|
1647
|
-
def zrevrange(key, start, stop,
|
|
1648
|
-
args = []
|
|
1649
|
-
|
|
1650
|
-
with_scores = options[:with_scores] || options[:withscores]
|
|
1808
|
+
def zrevrange(key, start, stop, withscores: false, with_scores: withscores)
|
|
1809
|
+
args = [:zrevrange, key, start, stop]
|
|
1651
1810
|
|
|
1652
1811
|
if with_scores
|
|
1653
1812
|
args << "WITHSCORES"
|
|
@@ -1655,7 +1814,7 @@ class Redis
|
|
|
1655
1814
|
end
|
|
1656
1815
|
|
|
1657
1816
|
synchronize do |client|
|
|
1658
|
-
client.call(
|
|
1817
|
+
client.call(args, &block)
|
|
1659
1818
|
end
|
|
1660
1819
|
end
|
|
1661
1820
|
|
|
@@ -1663,7 +1822,7 @@ class Redis
|
|
|
1663
1822
|
#
|
|
1664
1823
|
# @param [String] key
|
|
1665
1824
|
# @param [String] member
|
|
1666
|
-
# @return [
|
|
1825
|
+
# @return [Integer]
|
|
1667
1826
|
def zrank(key, member)
|
|
1668
1827
|
synchronize do |client|
|
|
1669
1828
|
client.call([:zrank, key, member])
|
|
@@ -1675,7 +1834,7 @@ class Redis
|
|
|
1675
1834
|
#
|
|
1676
1835
|
# @param [String] key
|
|
1677
1836
|
# @param [String] member
|
|
1678
|
-
# @return [
|
|
1837
|
+
# @return [Integer]
|
|
1679
1838
|
def zrevrank(key, member)
|
|
1680
1839
|
synchronize do |client|
|
|
1681
1840
|
client.call([:zrevrank, key, member])
|
|
@@ -1692,15 +1851,39 @@ class Redis
|
|
|
1692
1851
|
# # => 5
|
|
1693
1852
|
#
|
|
1694
1853
|
# @param [String] key
|
|
1695
|
-
# @param [
|
|
1696
|
-
# @param [
|
|
1697
|
-
# @return [
|
|
1854
|
+
# @param [Integer] start start index
|
|
1855
|
+
# @param [Integer] stop stop index
|
|
1856
|
+
# @return [Integer] number of members that were removed
|
|
1698
1857
|
def zremrangebyrank(key, start, stop)
|
|
1699
1858
|
synchronize do |client|
|
|
1700
1859
|
client.call([:zremrangebyrank, key, start, stop])
|
|
1701
1860
|
end
|
|
1702
1861
|
end
|
|
1703
1862
|
|
|
1863
|
+
# Count the members, with the same score in a sorted set, within the given lexicographical range.
|
|
1864
|
+
#
|
|
1865
|
+
# @example Count members matching a
|
|
1866
|
+
# redis.zlexcount("zset", "[a", "[a\xff")
|
|
1867
|
+
# # => 1
|
|
1868
|
+
# @example Count members matching a-z
|
|
1869
|
+
# redis.zlexcount("zset", "[a", "[z\xff")
|
|
1870
|
+
# # => 26
|
|
1871
|
+
#
|
|
1872
|
+
# @param [String] key
|
|
1873
|
+
# @param [String] min
|
|
1874
|
+
# - inclusive minimum is specified by prefixing `(`
|
|
1875
|
+
# - exclusive minimum is specified by prefixing `[`
|
|
1876
|
+
# @param [String] max
|
|
1877
|
+
# - inclusive maximum is specified by prefixing `(`
|
|
1878
|
+
# - exclusive maximum is specified by prefixing `[`
|
|
1879
|
+
#
|
|
1880
|
+
# @return [Integer] number of members within the specified lexicographical range
|
|
1881
|
+
def zlexcount(key, min, max)
|
|
1882
|
+
synchronize do |client|
|
|
1883
|
+
client.call([:zlexcount, key, min, max])
|
|
1884
|
+
end
|
|
1885
|
+
end
|
|
1886
|
+
|
|
1704
1887
|
# Return a range of members with the same score in a sorted set, by lexicographical ordering
|
|
1705
1888
|
#
|
|
1706
1889
|
# @example Retrieve members matching a
|
|
@@ -1722,14 +1905,16 @@ class Redis
|
|
|
1722
1905
|
# `count` members
|
|
1723
1906
|
#
|
|
1724
1907
|
# @return [Array<String>, Array<[String, Float]>]
|
|
1725
|
-
def zrangebylex(key, min, max,
|
|
1726
|
-
args = []
|
|
1908
|
+
def zrangebylex(key, min, max, limit: nil)
|
|
1909
|
+
args = [:zrangebylex, key, min, max]
|
|
1727
1910
|
|
|
1728
|
-
|
|
1729
|
-
|
|
1911
|
+
if limit
|
|
1912
|
+
args << "LIMIT"
|
|
1913
|
+
args.concat(limit)
|
|
1914
|
+
end
|
|
1730
1915
|
|
|
1731
1916
|
synchronize do |client|
|
|
1732
|
-
client.call(
|
|
1917
|
+
client.call(args)
|
|
1733
1918
|
end
|
|
1734
1919
|
end
|
|
1735
1920
|
|
|
@@ -1744,14 +1929,16 @@ class Redis
|
|
|
1744
1929
|
# # => ["abbygail", "abby"]
|
|
1745
1930
|
#
|
|
1746
1931
|
# @see #zrangebylex
|
|
1747
|
-
def zrevrangebylex(key, max, min,
|
|
1748
|
-
args = []
|
|
1932
|
+
def zrevrangebylex(key, max, min, limit: nil)
|
|
1933
|
+
args = [:zrevrangebylex, key, max, min]
|
|
1749
1934
|
|
|
1750
|
-
|
|
1751
|
-
|
|
1935
|
+
if limit
|
|
1936
|
+
args << "LIMIT"
|
|
1937
|
+
args.concat(limit)
|
|
1938
|
+
end
|
|
1752
1939
|
|
|
1753
1940
|
synchronize do |client|
|
|
1754
|
-
client.call(
|
|
1941
|
+
client.call(args)
|
|
1755
1942
|
end
|
|
1756
1943
|
end
|
|
1757
1944
|
|
|
@@ -1782,21 +1969,21 @@ class Redis
|
|
|
1782
1969
|
# @return [Array<String>, Array<[String, Float]>]
|
|
1783
1970
|
# - when `:with_scores` is not specified, an array of members
|
|
1784
1971
|
# - 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]
|
|
1972
|
+
def zrangebyscore(key, min, max, withscores: false, with_scores: withscores, limit: nil)
|
|
1973
|
+
args = [:zrangebyscore, key, min, max]
|
|
1789
1974
|
|
|
1790
1975
|
if with_scores
|
|
1791
1976
|
args << "WITHSCORES"
|
|
1792
1977
|
block = FloatifyPairs
|
|
1793
1978
|
end
|
|
1794
1979
|
|
|
1795
|
-
|
|
1796
|
-
|
|
1980
|
+
if limit
|
|
1981
|
+
args << "LIMIT"
|
|
1982
|
+
args.concat(limit)
|
|
1983
|
+
end
|
|
1797
1984
|
|
|
1798
1985
|
synchronize do |client|
|
|
1799
|
-
client.call(
|
|
1986
|
+
client.call(args, &block)
|
|
1800
1987
|
end
|
|
1801
1988
|
end
|
|
1802
1989
|
|
|
@@ -1814,21 +2001,21 @@ class Redis
|
|
|
1814
2001
|
# # => [["b", 64.0], ["a", 32.0]]
|
|
1815
2002
|
#
|
|
1816
2003
|
# @see #zrangebyscore
|
|
1817
|
-
def zrevrangebyscore(key, max, min,
|
|
1818
|
-
args = []
|
|
1819
|
-
|
|
1820
|
-
with_scores = options[:with_scores] || options[:withscores]
|
|
2004
|
+
def zrevrangebyscore(key, max, min, withscores: false, with_scores: withscores, limit: nil)
|
|
2005
|
+
args = [:zrevrangebyscore, key, max, min]
|
|
1821
2006
|
|
|
1822
2007
|
if with_scores
|
|
1823
|
-
args <<
|
|
2008
|
+
args << "WITHSCORES"
|
|
1824
2009
|
block = FloatifyPairs
|
|
1825
2010
|
end
|
|
1826
2011
|
|
|
1827
|
-
|
|
1828
|
-
|
|
2012
|
+
if limit
|
|
2013
|
+
args << "LIMIT"
|
|
2014
|
+
args.concat(limit)
|
|
2015
|
+
end
|
|
1829
2016
|
|
|
1830
2017
|
synchronize do |client|
|
|
1831
|
-
client.call(
|
|
2018
|
+
client.call(args, &block)
|
|
1832
2019
|
end
|
|
1833
2020
|
end
|
|
1834
2021
|
|
|
@@ -1848,7 +2035,7 @@ class Redis
|
|
|
1848
2035
|
# @param [String] max
|
|
1849
2036
|
# - inclusive maximum score is specified verbatim
|
|
1850
2037
|
# - exclusive maximum score is specified by prefixing `(`
|
|
1851
|
-
# @return [
|
|
2038
|
+
# @return [Integer] number of members that were removed
|
|
1852
2039
|
def zremrangebyscore(key, min, max)
|
|
1853
2040
|
synchronize do |client|
|
|
1854
2041
|
client.call([:zremrangebyscore, key, min, max])
|
|
@@ -1871,13 +2058,52 @@ class Redis
|
|
|
1871
2058
|
# @param [String] max
|
|
1872
2059
|
# - inclusive maximum score is specified verbatim
|
|
1873
2060
|
# - exclusive maximum score is specified by prefixing `(`
|
|
1874
|
-
# @return [
|
|
2061
|
+
# @return [Integer] number of members in within the specified range
|
|
1875
2062
|
def zcount(key, min, max)
|
|
1876
2063
|
synchronize do |client|
|
|
1877
2064
|
client.call([:zcount, key, min, max])
|
|
1878
2065
|
end
|
|
1879
2066
|
end
|
|
1880
2067
|
|
|
2068
|
+
# Return the intersection of multiple sorted sets
|
|
2069
|
+
#
|
|
2070
|
+
# @example Retrieve the intersection of `2*zsetA` and `1*zsetB`
|
|
2071
|
+
# redis.zinter("zsetA", "zsetB", :weights => [2.0, 1.0])
|
|
2072
|
+
# # => ["v1", "v2"]
|
|
2073
|
+
# @example Retrieve the intersection of `2*zsetA` and `1*zsetB`, and their scores
|
|
2074
|
+
# redis.zinter("zsetA", "zsetB", :weights => [2.0, 1.0], :with_scores => true)
|
|
2075
|
+
# # => [["v1", 3.0], ["v2", 6.0]]
|
|
2076
|
+
#
|
|
2077
|
+
# @param [String, Array<String>] keys one or more keys to intersect
|
|
2078
|
+
# @param [Hash] options
|
|
2079
|
+
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
|
2080
|
+
# sorted sets
|
|
2081
|
+
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
|
2082
|
+
# - `:with_scores => true`: include scores in output
|
|
2083
|
+
#
|
|
2084
|
+
# @return [Array<String>, Array<[String, Float]>]
|
|
2085
|
+
# - when `:with_scores` is not specified, an array of members
|
|
2086
|
+
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
|
2087
|
+
def zinter(*keys, weights: nil, aggregate: nil, with_scores: false)
|
|
2088
|
+
args = [:zinter, keys.size, *keys]
|
|
2089
|
+
|
|
2090
|
+
if weights
|
|
2091
|
+
args << "WEIGHTS"
|
|
2092
|
+
args.concat(weights)
|
|
2093
|
+
end
|
|
2094
|
+
|
|
2095
|
+
args << "AGGREGATE" << aggregate if aggregate
|
|
2096
|
+
|
|
2097
|
+
if with_scores
|
|
2098
|
+
args << "WITHSCORES"
|
|
2099
|
+
block = FloatifyPairs
|
|
2100
|
+
end
|
|
2101
|
+
|
|
2102
|
+
synchronize do |client|
|
|
2103
|
+
client.call(args, &block)
|
|
2104
|
+
end
|
|
2105
|
+
end
|
|
2106
|
+
|
|
1881
2107
|
# Intersect multiple sorted sets and store the resulting sorted set in a new
|
|
1882
2108
|
# key.
|
|
1883
2109
|
#
|
|
@@ -1891,18 +2117,19 @@ class Redis
|
|
|
1891
2117
|
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
|
1892
2118
|
# sorted sets
|
|
1893
2119
|
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
|
1894
|
-
# @return [
|
|
1895
|
-
def zinterstore(destination, keys,
|
|
1896
|
-
args = []
|
|
2120
|
+
# @return [Integer] number of elements in the resulting sorted set
|
|
2121
|
+
def zinterstore(destination, keys, weights: nil, aggregate: nil)
|
|
2122
|
+
args = [:zinterstore, destination, keys.size, *keys]
|
|
1897
2123
|
|
|
1898
|
-
|
|
1899
|
-
|
|
2124
|
+
if weights
|
|
2125
|
+
args << "WEIGHTS"
|
|
2126
|
+
args.concat(weights)
|
|
2127
|
+
end
|
|
1900
2128
|
|
|
1901
|
-
aggregate
|
|
1902
|
-
args.concat(["AGGREGATE", aggregate]) if aggregate
|
|
2129
|
+
args << "AGGREGATE" << aggregate if aggregate
|
|
1903
2130
|
|
|
1904
2131
|
synchronize do |client|
|
|
1905
|
-
client.call(
|
|
2132
|
+
client.call(args)
|
|
1906
2133
|
end
|
|
1907
2134
|
end
|
|
1908
2135
|
|
|
@@ -1918,40 +2145,46 @@ class Redis
|
|
|
1918
2145
|
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
|
1919
2146
|
# sorted sets
|
|
1920
2147
|
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
|
1921
|
-
# @return [
|
|
1922
|
-
def zunionstore(destination, keys,
|
|
1923
|
-
args = []
|
|
2148
|
+
# @return [Integer] number of elements in the resulting sorted set
|
|
2149
|
+
def zunionstore(destination, keys, weights: nil, aggregate: nil)
|
|
2150
|
+
args = [:zunionstore, destination, keys.size, *keys]
|
|
1924
2151
|
|
|
1925
|
-
|
|
1926
|
-
|
|
2152
|
+
if weights
|
|
2153
|
+
args << "WEIGHTS"
|
|
2154
|
+
args.concat(weights)
|
|
2155
|
+
end
|
|
1927
2156
|
|
|
1928
|
-
aggregate
|
|
1929
|
-
args.concat(["AGGREGATE", aggregate]) if aggregate
|
|
2157
|
+
args << "AGGREGATE" << aggregate if aggregate
|
|
1930
2158
|
|
|
1931
2159
|
synchronize do |client|
|
|
1932
|
-
client.call(
|
|
2160
|
+
client.call(args)
|
|
1933
2161
|
end
|
|
1934
2162
|
end
|
|
1935
2163
|
|
|
1936
2164
|
# Get the number of fields in a hash.
|
|
1937
2165
|
#
|
|
1938
2166
|
# @param [String] key
|
|
1939
|
-
# @return [
|
|
2167
|
+
# @return [Integer] number of fields in the hash
|
|
1940
2168
|
def hlen(key)
|
|
1941
2169
|
synchronize do |client|
|
|
1942
2170
|
client.call([:hlen, key])
|
|
1943
2171
|
end
|
|
1944
2172
|
end
|
|
1945
2173
|
|
|
1946
|
-
# Set
|
|
2174
|
+
# Set one or more hash values.
|
|
2175
|
+
#
|
|
2176
|
+
# @example
|
|
2177
|
+
# redis.hset("hash", "f1", "v1", "f2", "v2") # => 2
|
|
2178
|
+
# redis.hset("hash", { "f1" => "v1", "f2" => "v2" }) # => 2
|
|
1947
2179
|
#
|
|
1948
2180
|
# @param [String] key
|
|
1949
|
-
# @param [String]
|
|
1950
|
-
# @
|
|
1951
|
-
|
|
1952
|
-
|
|
2181
|
+
# @param [Array<String> | Hash<String, String>] attrs array or hash of fields and values
|
|
2182
|
+
# @return [Integer] The number of fields that were added to the hash
|
|
2183
|
+
def hset(key, *attrs)
|
|
2184
|
+
attrs = attrs.first.flatten if attrs.size == 1 && attrs.first.is_a?(Hash)
|
|
2185
|
+
|
|
1953
2186
|
synchronize do |client|
|
|
1954
|
-
client.call([:hset, key,
|
|
2187
|
+
client.call([:hset, key, *attrs])
|
|
1955
2188
|
end
|
|
1956
2189
|
end
|
|
1957
2190
|
|
|
@@ -2040,7 +2273,7 @@ class Redis
|
|
|
2040
2273
|
# @see #hmget
|
|
2041
2274
|
def mapped_hmget(key, *fields)
|
|
2042
2275
|
hmget(key, *fields) do |reply|
|
|
2043
|
-
if reply.
|
|
2276
|
+
if reply.is_a?(Array)
|
|
2044
2277
|
Hash[fields.zip(reply)]
|
|
2045
2278
|
else
|
|
2046
2279
|
reply
|
|
@@ -2052,10 +2285,10 @@ class Redis
|
|
|
2052
2285
|
#
|
|
2053
2286
|
# @param [String] key
|
|
2054
2287
|
# @param [String, Array<String>] field
|
|
2055
|
-
# @return [
|
|
2056
|
-
def hdel(key,
|
|
2288
|
+
# @return [Integer] the number of fields that were removed from the hash
|
|
2289
|
+
def hdel(key, *fields)
|
|
2057
2290
|
synchronize do |client|
|
|
2058
|
-
client.call([:hdel, key,
|
|
2291
|
+
client.call([:hdel, key, *fields])
|
|
2059
2292
|
end
|
|
2060
2293
|
end
|
|
2061
2294
|
|
|
@@ -2074,8 +2307,8 @@ class Redis
|
|
|
2074
2307
|
#
|
|
2075
2308
|
# @param [String] key
|
|
2076
2309
|
# @param [String] field
|
|
2077
|
-
# @param [
|
|
2078
|
-
# @return [
|
|
2310
|
+
# @param [Integer] increment
|
|
2311
|
+
# @return [Integer] value of the field after incrementing it
|
|
2079
2312
|
def hincrby(key, field, increment)
|
|
2080
2313
|
synchronize do |client|
|
|
2081
2314
|
client.call([:hincrby, key, field, increment])
|
|
@@ -2133,20 +2366,21 @@ class Redis
|
|
|
2133
2366
|
|
|
2134
2367
|
def subscribed?
|
|
2135
2368
|
synchronize do |client|
|
|
2136
|
-
client.
|
|
2369
|
+
client.is_a? SubscribedClient
|
|
2137
2370
|
end
|
|
2138
2371
|
end
|
|
2139
2372
|
|
|
2140
2373
|
# Listen for messages published to the given channels.
|
|
2141
2374
|
def subscribe(*channels, &block)
|
|
2142
|
-
synchronize do |
|
|
2375
|
+
synchronize do |_client|
|
|
2143
2376
|
_subscription(:subscribe, 0, channels, block)
|
|
2144
2377
|
end
|
|
2145
2378
|
end
|
|
2146
2379
|
|
|
2147
|
-
# Listen for messages published to the given channels. Throw a timeout error
|
|
2380
|
+
# Listen for messages published to the given channels. Throw a timeout error
|
|
2381
|
+
# if there is no messages for a timeout period.
|
|
2148
2382
|
def subscribe_with_timeout(timeout, *channels, &block)
|
|
2149
|
-
synchronize do |
|
|
2383
|
+
synchronize do |_client|
|
|
2150
2384
|
_subscription(:subscribe_with_timeout, timeout, channels, block)
|
|
2151
2385
|
end
|
|
2152
2386
|
end
|
|
@@ -2154,21 +2388,23 @@ class Redis
|
|
|
2154
2388
|
# Stop listening for messages posted to the given channels.
|
|
2155
2389
|
def unsubscribe(*channels)
|
|
2156
2390
|
synchronize do |client|
|
|
2157
|
-
raise
|
|
2391
|
+
raise "Can't unsubscribe if not subscribed." unless subscribed?
|
|
2392
|
+
|
|
2158
2393
|
client.unsubscribe(*channels)
|
|
2159
2394
|
end
|
|
2160
2395
|
end
|
|
2161
2396
|
|
|
2162
2397
|
# Listen for messages published to channels matching the given patterns.
|
|
2163
2398
|
def psubscribe(*channels, &block)
|
|
2164
|
-
synchronize do |
|
|
2399
|
+
synchronize do |_client|
|
|
2165
2400
|
_subscription(:psubscribe, 0, channels, block)
|
|
2166
2401
|
end
|
|
2167
2402
|
end
|
|
2168
2403
|
|
|
2169
|
-
# Listen for messages published to channels matching the given patterns.
|
|
2404
|
+
# Listen for messages published to channels matching the given patterns.
|
|
2405
|
+
# Throw a timeout error if there is no messages for a timeout period.
|
|
2170
2406
|
def psubscribe_with_timeout(timeout, *channels, &block)
|
|
2171
|
-
synchronize do |
|
|
2407
|
+
synchronize do |_client|
|
|
2172
2408
|
_subscription(:psubscribe_with_timeout, timeout, channels, block)
|
|
2173
2409
|
end
|
|
2174
2410
|
end
|
|
@@ -2176,7 +2412,8 @@ class Redis
|
|
|
2176
2412
|
# Stop listening for messages posted to channels matching the given patterns.
|
|
2177
2413
|
def punsubscribe(*channels)
|
|
2178
2414
|
synchronize do |client|
|
|
2179
|
-
raise
|
|
2415
|
+
raise "Can't unsubscribe if not subscribed." unless subscribed?
|
|
2416
|
+
|
|
2180
2417
|
client.punsubscribe(*channels)
|
|
2181
2418
|
end
|
|
2182
2419
|
end
|
|
@@ -2221,7 +2458,7 @@ class Redis
|
|
|
2221
2458
|
# @see #multi
|
|
2222
2459
|
def watch(*keys)
|
|
2223
2460
|
synchronize do |client|
|
|
2224
|
-
res = client.call([:watch
|
|
2461
|
+
res = client.call([:watch, *keys])
|
|
2225
2462
|
|
|
2226
2463
|
if block_given?
|
|
2227
2464
|
begin
|
|
@@ -2251,13 +2488,13 @@ class Redis
|
|
|
2251
2488
|
end
|
|
2252
2489
|
|
|
2253
2490
|
def pipelined
|
|
2254
|
-
synchronize do |
|
|
2491
|
+
synchronize do |prior_client|
|
|
2255
2492
|
begin
|
|
2256
|
-
|
|
2493
|
+
@client = Pipeline.new(prior_client)
|
|
2257
2494
|
yield(self)
|
|
2258
|
-
|
|
2495
|
+
prior_client.call_pipeline(@client)
|
|
2259
2496
|
ensure
|
|
2260
|
-
@client =
|
|
2497
|
+
@client = prior_client
|
|
2261
2498
|
end
|
|
2262
2499
|
end
|
|
2263
2500
|
end
|
|
@@ -2293,17 +2530,16 @@ class Redis
|
|
|
2293
2530
|
# @see #watch
|
|
2294
2531
|
# @see #unwatch
|
|
2295
2532
|
def multi
|
|
2296
|
-
synchronize do |
|
|
2533
|
+
synchronize do |prior_client|
|
|
2297
2534
|
if !block_given?
|
|
2298
|
-
|
|
2535
|
+
prior_client.call([:multi])
|
|
2299
2536
|
else
|
|
2300
2537
|
begin
|
|
2301
|
-
|
|
2302
|
-
original, @client = @client, pipeline
|
|
2538
|
+
@client = Pipeline::Multi.new(prior_client)
|
|
2303
2539
|
yield(self)
|
|
2304
|
-
|
|
2540
|
+
prior_client.call_pipeline(@client)
|
|
2305
2541
|
ensure
|
|
2306
|
-
@client =
|
|
2542
|
+
@client = prior_client
|
|
2307
2543
|
end
|
|
2308
2544
|
end
|
|
2309
2545
|
end
|
|
@@ -2450,18 +2686,13 @@ class Redis
|
|
|
2450
2686
|
_eval(:evalsha, args)
|
|
2451
2687
|
end
|
|
2452
2688
|
|
|
2453
|
-
def _scan(command, cursor, args,
|
|
2689
|
+
def _scan(command, cursor, args, match: nil, count: nil, type: nil, &block)
|
|
2454
2690
|
# SSCAN/ZSCAN/HSCAN already prepend the key to +args+.
|
|
2455
2691
|
|
|
2456
2692
|
args << cursor
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
end
|
|
2461
|
-
|
|
2462
|
-
if count = options[:count]
|
|
2463
|
-
args.concat(["COUNT", count])
|
|
2464
|
-
end
|
|
2693
|
+
args << "MATCH" << match if match
|
|
2694
|
+
args << "COUNT" << count if count
|
|
2695
|
+
args << "TYPE" << type if type
|
|
2465
2696
|
|
|
2466
2697
|
synchronize do |client|
|
|
2467
2698
|
client.call([command] + args, &block)
|
|
@@ -2476,15 +2707,19 @@ class Redis
|
|
|
2476
2707
|
# @example Retrieve a batch of keys matching a pattern
|
|
2477
2708
|
# redis.scan(4, :match => "key:1?")
|
|
2478
2709
|
# # => ["92", ["key:13", "key:18"]]
|
|
2710
|
+
# @example Retrieve a batch of keys of a certain type
|
|
2711
|
+
# redis.scan(92, :type => "zset")
|
|
2712
|
+
# # => ["173", ["sortedset:14", "sortedset:78"]]
|
|
2479
2713
|
#
|
|
2480
2714
|
# @param [String, Integer] cursor the cursor of the iteration
|
|
2481
2715
|
# @param [Hash] options
|
|
2482
2716
|
# - `:match => String`: only return keys matching the pattern
|
|
2483
2717
|
# - `:count => Integer`: return count keys at most per iteration
|
|
2718
|
+
# - `:type => String`: return keys only of the given type
|
|
2484
2719
|
#
|
|
2485
2720
|
# @return [String, Array<String>] the next cursor and all found keys
|
|
2486
|
-
def scan(cursor, options
|
|
2487
|
-
_scan(:scan, cursor, [], options)
|
|
2721
|
+
def scan(cursor, **options)
|
|
2722
|
+
_scan(:scan, cursor, [], **options)
|
|
2488
2723
|
end
|
|
2489
2724
|
|
|
2490
2725
|
# Scan the keyspace
|
|
@@ -2496,17 +2731,23 @@ class Redis
|
|
|
2496
2731
|
# redis.scan_each(:match => "key:1?") {|key| puts key}
|
|
2497
2732
|
# # => key:13
|
|
2498
2733
|
# # => key:18
|
|
2734
|
+
# @example Execute block for each key of a type
|
|
2735
|
+
# redis.scan_each(:type => "hash") {|key| puts redis.type(key)}
|
|
2736
|
+
# # => "hash"
|
|
2737
|
+
# # => "hash"
|
|
2499
2738
|
#
|
|
2500
2739
|
# @param [Hash] options
|
|
2501
2740
|
# - `:match => String`: only return keys matching the pattern
|
|
2502
2741
|
# - `:count => Integer`: return count keys at most per iteration
|
|
2742
|
+
# - `:type => String`: return keys only of the given type
|
|
2503
2743
|
#
|
|
2504
2744
|
# @return [Enumerator] an enumerator for all found keys
|
|
2505
|
-
def scan_each(options
|
|
2506
|
-
return to_enum(:scan_each, options) unless block_given?
|
|
2745
|
+
def scan_each(**options, &block)
|
|
2746
|
+
return to_enum(:scan_each, **options) unless block_given?
|
|
2747
|
+
|
|
2507
2748
|
cursor = 0
|
|
2508
2749
|
loop do
|
|
2509
|
-
cursor, keys = scan(cursor, options)
|
|
2750
|
+
cursor, keys = scan(cursor, **options)
|
|
2510
2751
|
keys.each(&block)
|
|
2511
2752
|
break if cursor == "0"
|
|
2512
2753
|
end
|
|
@@ -2523,8 +2764,8 @@ class Redis
|
|
|
2523
2764
|
# - `:count => Integer`: return count keys at most per iteration
|
|
2524
2765
|
#
|
|
2525
2766
|
# @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|
|
|
2767
|
+
def hscan(key, cursor, **options)
|
|
2768
|
+
_scan(:hscan, cursor, [key], **options) do |reply|
|
|
2528
2769
|
[reply[0], reply[1].each_slice(2).to_a]
|
|
2529
2770
|
end
|
|
2530
2771
|
end
|
|
@@ -2540,11 +2781,12 @@ class Redis
|
|
|
2540
2781
|
# - `:count => Integer`: return count keys at most per iteration
|
|
2541
2782
|
#
|
|
2542
2783
|
# @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?
|
|
2784
|
+
def hscan_each(key, **options, &block)
|
|
2785
|
+
return to_enum(:hscan_each, key, **options) unless block_given?
|
|
2786
|
+
|
|
2545
2787
|
cursor = 0
|
|
2546
2788
|
loop do
|
|
2547
|
-
cursor, values = hscan(key, cursor, options)
|
|
2789
|
+
cursor, values = hscan(key, cursor, **options)
|
|
2548
2790
|
values.each(&block)
|
|
2549
2791
|
break if cursor == "0"
|
|
2550
2792
|
end
|
|
@@ -2562,8 +2804,8 @@ class Redis
|
|
|
2562
2804
|
#
|
|
2563
2805
|
# @return [String, Array<[String, Float]>] the next cursor and all found
|
|
2564
2806
|
# members and scores
|
|
2565
|
-
def zscan(key, cursor, options
|
|
2566
|
-
_scan(:zscan, cursor, [key], options) do |reply|
|
|
2807
|
+
def zscan(key, cursor, **options)
|
|
2808
|
+
_scan(:zscan, cursor, [key], **options) do |reply|
|
|
2567
2809
|
[reply[0], FloatifyPairs.call(reply[1])]
|
|
2568
2810
|
end
|
|
2569
2811
|
end
|
|
@@ -2579,11 +2821,12 @@ class Redis
|
|
|
2579
2821
|
# - `:count => Integer`: return count keys at most per iteration
|
|
2580
2822
|
#
|
|
2581
2823
|
# @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?
|
|
2824
|
+
def zscan_each(key, **options, &block)
|
|
2825
|
+
return to_enum(:zscan_each, key, **options) unless block_given?
|
|
2826
|
+
|
|
2584
2827
|
cursor = 0
|
|
2585
2828
|
loop do
|
|
2586
|
-
cursor, values = zscan(key, cursor, options)
|
|
2829
|
+
cursor, values = zscan(key, cursor, **options)
|
|
2587
2830
|
values.each(&block)
|
|
2588
2831
|
break if cursor == "0"
|
|
2589
2832
|
end
|
|
@@ -2600,8 +2843,8 @@ class Redis
|
|
|
2600
2843
|
# - `:count => Integer`: return count keys at most per iteration
|
|
2601
2844
|
#
|
|
2602
2845
|
# @return [String, Array<String>] the next cursor and all found members
|
|
2603
|
-
def sscan(key, cursor, options
|
|
2604
|
-
_scan(:sscan, cursor, [key], options)
|
|
2846
|
+
def sscan(key, cursor, **options)
|
|
2847
|
+
_scan(:sscan, cursor, [key], **options)
|
|
2605
2848
|
end
|
|
2606
2849
|
|
|
2607
2850
|
# Scan a set
|
|
@@ -2615,11 +2858,12 @@ class Redis
|
|
|
2615
2858
|
# - `:count => Integer`: return count keys at most per iteration
|
|
2616
2859
|
#
|
|
2617
2860
|
# @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?
|
|
2861
|
+
def sscan_each(key, **options, &block)
|
|
2862
|
+
return to_enum(:sscan_each, key, **options) unless block_given?
|
|
2863
|
+
|
|
2620
2864
|
cursor = 0
|
|
2621
2865
|
loop do
|
|
2622
|
-
cursor, keys = sscan(key, cursor, options)
|
|
2866
|
+
cursor, keys = sscan(key, cursor, **options)
|
|
2623
2867
|
keys.each(&block)
|
|
2624
2868
|
break if cursor == "0"
|
|
2625
2869
|
end
|
|
@@ -2642,7 +2886,7 @@ class Redis
|
|
|
2642
2886
|
# union of the HyperLogLogs contained in the keys.
|
|
2643
2887
|
#
|
|
2644
2888
|
# @param [String, Array<String>] keys
|
|
2645
|
-
# @return [
|
|
2889
|
+
# @return [Integer]
|
|
2646
2890
|
def pfcount(*keys)
|
|
2647
2891
|
synchronize do |client|
|
|
2648
2892
|
client.call([:pfcount] + keys)
|
|
@@ -2661,6 +2905,445 @@ class Redis
|
|
|
2661
2905
|
end
|
|
2662
2906
|
end
|
|
2663
2907
|
|
|
2908
|
+
# Adds the specified geospatial items (latitude, longitude, name) to the specified key
|
|
2909
|
+
#
|
|
2910
|
+
# @param [String] key
|
|
2911
|
+
# @param [Array] member arguemnts for member or members: longitude, latitude, name
|
|
2912
|
+
# @return [Integer] number of elements added to the sorted set
|
|
2913
|
+
def geoadd(key, *member)
|
|
2914
|
+
synchronize do |client|
|
|
2915
|
+
client.call([:geoadd, key, *member])
|
|
2916
|
+
end
|
|
2917
|
+
end
|
|
2918
|
+
|
|
2919
|
+
# Returns geohash string representing position for specified members of the specified key.
|
|
2920
|
+
#
|
|
2921
|
+
# @param [String] key
|
|
2922
|
+
# @param [String, Array<String>] member one member or array of members
|
|
2923
|
+
# @return [Array<String, nil>] returns array containg geohash string if member is present, nil otherwise
|
|
2924
|
+
def geohash(key, member)
|
|
2925
|
+
synchronize do |client|
|
|
2926
|
+
client.call([:geohash, key, member])
|
|
2927
|
+
end
|
|
2928
|
+
end
|
|
2929
|
+
|
|
2930
|
+
# Query a sorted set representing a geospatial index to fetch members matching a
|
|
2931
|
+
# given maximum distance from a point
|
|
2932
|
+
#
|
|
2933
|
+
# @param [Array] args key, longitude, latitude, radius, unit(m|km|ft|mi)
|
|
2934
|
+
# @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest
|
|
2935
|
+
# or the farthest to the nearest relative to the center
|
|
2936
|
+
# @param [Integer] count limit the results to the first N matching items
|
|
2937
|
+
# @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
|
|
2938
|
+
# @return [Array<String>] may be changed with `options`
|
|
2939
|
+
|
|
2940
|
+
def georadius(*args, **geoptions)
|
|
2941
|
+
geoarguments = _geoarguments(*args, **geoptions)
|
|
2942
|
+
|
|
2943
|
+
synchronize do |client|
|
|
2944
|
+
client.call([:georadius, *geoarguments])
|
|
2945
|
+
end
|
|
2946
|
+
end
|
|
2947
|
+
|
|
2948
|
+
# Query a sorted set representing a geospatial index to fetch members matching a
|
|
2949
|
+
# given maximum distance from an already existing member
|
|
2950
|
+
#
|
|
2951
|
+
# @param [Array] args key, member, radius, unit(m|km|ft|mi)
|
|
2952
|
+
# @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest or the farthest
|
|
2953
|
+
# to the nearest relative to the center
|
|
2954
|
+
# @param [Integer] count limit the results to the first N matching items
|
|
2955
|
+
# @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
|
|
2956
|
+
# @return [Array<String>] may be changed with `options`
|
|
2957
|
+
|
|
2958
|
+
def georadiusbymember(*args, **geoptions)
|
|
2959
|
+
geoarguments = _geoarguments(*args, **geoptions)
|
|
2960
|
+
|
|
2961
|
+
synchronize do |client|
|
|
2962
|
+
client.call([:georadiusbymember, *geoarguments])
|
|
2963
|
+
end
|
|
2964
|
+
end
|
|
2965
|
+
|
|
2966
|
+
# Returns longitude and latitude of members of a geospatial index
|
|
2967
|
+
#
|
|
2968
|
+
# @param [String] key
|
|
2969
|
+
# @param [String, Array<String>] member one member or array of members
|
|
2970
|
+
# @return [Array<Array<String>, nil>] returns array of elements, where each
|
|
2971
|
+
# element is either array of longitude and latitude or nil
|
|
2972
|
+
def geopos(key, member)
|
|
2973
|
+
synchronize do |client|
|
|
2974
|
+
client.call([:geopos, key, member])
|
|
2975
|
+
end
|
|
2976
|
+
end
|
|
2977
|
+
|
|
2978
|
+
# Returns the distance between two members of a geospatial index
|
|
2979
|
+
#
|
|
2980
|
+
# @param [String ]key
|
|
2981
|
+
# @param [Array<String>] members
|
|
2982
|
+
# @param ['m', 'km', 'mi', 'ft'] unit
|
|
2983
|
+
# @return [String, nil] returns distance in spefied unit if both members present, nil otherwise.
|
|
2984
|
+
def geodist(key, member1, member2, unit = 'm')
|
|
2985
|
+
synchronize do |client|
|
|
2986
|
+
client.call([:geodist, key, member1, member2, unit])
|
|
2987
|
+
end
|
|
2988
|
+
end
|
|
2989
|
+
|
|
2990
|
+
# Returns the stream information each subcommand.
|
|
2991
|
+
#
|
|
2992
|
+
# @example stream
|
|
2993
|
+
# redis.xinfo(:stream, 'mystream')
|
|
2994
|
+
# @example groups
|
|
2995
|
+
# redis.xinfo(:groups, 'mystream')
|
|
2996
|
+
# @example consumers
|
|
2997
|
+
# redis.xinfo(:consumers, 'mystream', 'mygroup')
|
|
2998
|
+
#
|
|
2999
|
+
# @param subcommand [String] e.g. `stream` `groups` `consumers`
|
|
3000
|
+
# @param key [String] the stream key
|
|
3001
|
+
# @param group [String] the consumer group name, required if subcommand is `consumers`
|
|
3002
|
+
#
|
|
3003
|
+
# @return [Hash] information of the stream if subcommand is `stream`
|
|
3004
|
+
# @return [Array<Hash>] information of the consumer groups if subcommand is `groups`
|
|
3005
|
+
# @return [Array<Hash>] information of the consumers if subcommand is `consumers`
|
|
3006
|
+
def xinfo(subcommand, key, group = nil)
|
|
3007
|
+
args = [:xinfo, subcommand, key, group].compact
|
|
3008
|
+
synchronize do |client|
|
|
3009
|
+
client.call(args) do |reply|
|
|
3010
|
+
case subcommand.to_s.downcase
|
|
3011
|
+
when 'stream' then Hashify.call(reply)
|
|
3012
|
+
when 'groups', 'consumers' then reply.map { |arr| Hashify.call(arr) }
|
|
3013
|
+
else reply
|
|
3014
|
+
end
|
|
3015
|
+
end
|
|
3016
|
+
end
|
|
3017
|
+
end
|
|
3018
|
+
|
|
3019
|
+
# Add new entry to the stream.
|
|
3020
|
+
#
|
|
3021
|
+
# @example Without options
|
|
3022
|
+
# redis.xadd('mystream', f1: 'v1', f2: 'v2')
|
|
3023
|
+
# @example With options
|
|
3024
|
+
# redis.xadd('mystream', { f1: 'v1', f2: 'v2' }, id: '0-0', maxlen: 1000, approximate: true)
|
|
3025
|
+
#
|
|
3026
|
+
# @param key [String] the stream key
|
|
3027
|
+
# @param entry [Hash] one or multiple field-value pairs
|
|
3028
|
+
# @param opts [Hash] several options for `XADD` command
|
|
3029
|
+
#
|
|
3030
|
+
# @option opts [String] :id the entry id, default value is `*`, it means auto generation
|
|
3031
|
+
# @option opts [Integer] :maxlen max length of entries
|
|
3032
|
+
# @option opts [Boolean] :approximate whether to add `~` modifier of maxlen or not
|
|
3033
|
+
#
|
|
3034
|
+
# @return [String] the entry id
|
|
3035
|
+
def xadd(key, entry, approximate: nil, maxlen: nil, id: '*')
|
|
3036
|
+
args = [:xadd, key]
|
|
3037
|
+
if maxlen
|
|
3038
|
+
args << "MAXLEN"
|
|
3039
|
+
args << "~" if approximate
|
|
3040
|
+
args << maxlen
|
|
3041
|
+
end
|
|
3042
|
+
args << id
|
|
3043
|
+
args.concat(entry.to_a.flatten)
|
|
3044
|
+
synchronize { |client| client.call(args) }
|
|
3045
|
+
end
|
|
3046
|
+
|
|
3047
|
+
# Trims older entries of the stream if needed.
|
|
3048
|
+
#
|
|
3049
|
+
# @example Without options
|
|
3050
|
+
# redis.xtrim('mystream', 1000)
|
|
3051
|
+
# @example With options
|
|
3052
|
+
# redis.xtrim('mystream', 1000, approximate: true)
|
|
3053
|
+
#
|
|
3054
|
+
# @param key [String] the stream key
|
|
3055
|
+
# @param mexlen [Integer] max length of entries
|
|
3056
|
+
# @param approximate [Boolean] whether to add `~` modifier of maxlen or not
|
|
3057
|
+
#
|
|
3058
|
+
# @return [Integer] the number of entries actually deleted
|
|
3059
|
+
def xtrim(key, maxlen, approximate: false)
|
|
3060
|
+
args = [:xtrim, key, 'MAXLEN', (approximate ? '~' : nil), maxlen].compact
|
|
3061
|
+
synchronize { |client| client.call(args) }
|
|
3062
|
+
end
|
|
3063
|
+
|
|
3064
|
+
# Delete entries by entry ids.
|
|
3065
|
+
#
|
|
3066
|
+
# @example With splatted entry ids
|
|
3067
|
+
# redis.xdel('mystream', '0-1', '0-2')
|
|
3068
|
+
# @example With arrayed entry ids
|
|
3069
|
+
# redis.xdel('mystream', ['0-1', '0-2'])
|
|
3070
|
+
#
|
|
3071
|
+
# @param key [String] the stream key
|
|
3072
|
+
# @param ids [Array<String>] one or multiple entry ids
|
|
3073
|
+
#
|
|
3074
|
+
# @return [Integer] the number of entries actually deleted
|
|
3075
|
+
def xdel(key, *ids)
|
|
3076
|
+
args = [:xdel, key].concat(ids.flatten)
|
|
3077
|
+
synchronize { |client| client.call(args) }
|
|
3078
|
+
end
|
|
3079
|
+
|
|
3080
|
+
# Fetches entries of the stream in ascending order.
|
|
3081
|
+
#
|
|
3082
|
+
# @example Without options
|
|
3083
|
+
# redis.xrange('mystream')
|
|
3084
|
+
# @example With a specific start
|
|
3085
|
+
# redis.xrange('mystream', '0-1')
|
|
3086
|
+
# @example With a specific start and end
|
|
3087
|
+
# redis.xrange('mystream', '0-1', '0-3')
|
|
3088
|
+
# @example With count options
|
|
3089
|
+
# redis.xrange('mystream', count: 10)
|
|
3090
|
+
#
|
|
3091
|
+
# @param key [String] the stream key
|
|
3092
|
+
# @param start [String] first entry id of range, default value is `-`
|
|
3093
|
+
# @param end [String] last entry id of range, default value is `+`
|
|
3094
|
+
# @param count [Integer] the number of entries as limit
|
|
3095
|
+
#
|
|
3096
|
+
# @return [Array<Array<String, Hash>>] the ids and entries pairs
|
|
3097
|
+
def xrange(key, start = '-', range_end = '+', count: nil)
|
|
3098
|
+
args = [:xrange, key, start, range_end]
|
|
3099
|
+
args.concat(['COUNT', count]) if count
|
|
3100
|
+
synchronize { |client| client.call(args, &HashifyStreamEntries) }
|
|
3101
|
+
end
|
|
3102
|
+
|
|
3103
|
+
# Fetches entries of the stream in descending order.
|
|
3104
|
+
#
|
|
3105
|
+
# @example Without options
|
|
3106
|
+
# redis.xrevrange('mystream')
|
|
3107
|
+
# @example With a specific end
|
|
3108
|
+
# redis.xrevrange('mystream', '0-3')
|
|
3109
|
+
# @example With a specific end and start
|
|
3110
|
+
# redis.xrevrange('mystream', '0-3', '0-1')
|
|
3111
|
+
# @example With count options
|
|
3112
|
+
# redis.xrevrange('mystream', count: 10)
|
|
3113
|
+
#
|
|
3114
|
+
# @param key [String] the stream key
|
|
3115
|
+
# @param end [String] first entry id of range, default value is `+`
|
|
3116
|
+
# @param start [String] last entry id of range, default value is `-`
|
|
3117
|
+
# @params count [Integer] the number of entries as limit
|
|
3118
|
+
#
|
|
3119
|
+
# @return [Array<Array<String, Hash>>] the ids and entries pairs
|
|
3120
|
+
def xrevrange(key, range_end = '+', start = '-', count: nil)
|
|
3121
|
+
args = [:xrevrange, key, range_end, start]
|
|
3122
|
+
args.concat(['COUNT', count]) if count
|
|
3123
|
+
synchronize { |client| client.call(args, &HashifyStreamEntries) }
|
|
3124
|
+
end
|
|
3125
|
+
|
|
3126
|
+
# Returns the number of entries inside a stream.
|
|
3127
|
+
#
|
|
3128
|
+
# @example With key
|
|
3129
|
+
# redis.xlen('mystream')
|
|
3130
|
+
#
|
|
3131
|
+
# @param key [String] the stream key
|
|
3132
|
+
#
|
|
3133
|
+
# @return [Integer] the number of entries
|
|
3134
|
+
def xlen(key)
|
|
3135
|
+
synchronize { |client| client.call([:xlen, key]) }
|
|
3136
|
+
end
|
|
3137
|
+
|
|
3138
|
+
# Fetches entries from one or multiple streams. Optionally blocking.
|
|
3139
|
+
#
|
|
3140
|
+
# @example With a key
|
|
3141
|
+
# redis.xread('mystream', '0-0')
|
|
3142
|
+
# @example With multiple keys
|
|
3143
|
+
# redis.xread(%w[mystream1 mystream2], %w[0-0 0-0])
|
|
3144
|
+
# @example With count option
|
|
3145
|
+
# redis.xread('mystream', '0-0', count: 2)
|
|
3146
|
+
# @example With block option
|
|
3147
|
+
# redis.xread('mystream', '$', block: 1000)
|
|
3148
|
+
#
|
|
3149
|
+
# @param keys [Array<String>] one or multiple stream keys
|
|
3150
|
+
# @param ids [Array<String>] one or multiple entry ids
|
|
3151
|
+
# @param count [Integer] the number of entries as limit per stream
|
|
3152
|
+
# @param block [Integer] the number of milliseconds as blocking timeout
|
|
3153
|
+
#
|
|
3154
|
+
# @return [Hash{String => Hash{String => Hash}}] the entries
|
|
3155
|
+
def xread(keys, ids, count: nil, block: nil)
|
|
3156
|
+
args = [:xread]
|
|
3157
|
+
args << 'COUNT' << count if count
|
|
3158
|
+
args << 'BLOCK' << block.to_i if block
|
|
3159
|
+
_xread(args, keys, ids, block)
|
|
3160
|
+
end
|
|
3161
|
+
|
|
3162
|
+
# Manages the consumer group of the stream.
|
|
3163
|
+
#
|
|
3164
|
+
# @example With `create` subcommand
|
|
3165
|
+
# redis.xgroup(:create, 'mystream', 'mygroup', '$')
|
|
3166
|
+
# @example With `setid` subcommand
|
|
3167
|
+
# redis.xgroup(:setid, 'mystream', 'mygroup', '$')
|
|
3168
|
+
# @example With `destroy` subcommand
|
|
3169
|
+
# redis.xgroup(:destroy, 'mystream', 'mygroup')
|
|
3170
|
+
# @example With `delconsumer` subcommand
|
|
3171
|
+
# redis.xgroup(:delconsumer, 'mystream', 'mygroup', 'consumer1')
|
|
3172
|
+
#
|
|
3173
|
+
# @param subcommand [String] `create` `setid` `destroy` `delconsumer`
|
|
3174
|
+
# @param key [String] the stream key
|
|
3175
|
+
# @param group [String] the consumer group name
|
|
3176
|
+
# @param id_or_consumer [String]
|
|
3177
|
+
# * the entry id or `$`, required if subcommand is `create` or `setid`
|
|
3178
|
+
# * the consumer name, required if subcommand is `delconsumer`
|
|
3179
|
+
# @param mkstream [Boolean] whether to create an empty stream automatically or not
|
|
3180
|
+
#
|
|
3181
|
+
# @return [String] `OK` if subcommand is `create` or `setid`
|
|
3182
|
+
# @return [Integer] effected count if subcommand is `destroy` or `delconsumer`
|
|
3183
|
+
def xgroup(subcommand, key, group, id_or_consumer = nil, mkstream: false)
|
|
3184
|
+
args = [:xgroup, subcommand, key, group, id_or_consumer, (mkstream ? 'MKSTREAM' : nil)].compact
|
|
3185
|
+
synchronize { |client| client.call(args) }
|
|
3186
|
+
end
|
|
3187
|
+
|
|
3188
|
+
# Fetches a subset of the entries from one or multiple streams related with the consumer group.
|
|
3189
|
+
# Optionally blocking.
|
|
3190
|
+
#
|
|
3191
|
+
# @example With a key
|
|
3192
|
+
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>')
|
|
3193
|
+
# @example With multiple keys
|
|
3194
|
+
# redis.xreadgroup('mygroup', 'consumer1', %w[mystream1 mystream2], %w[> >])
|
|
3195
|
+
# @example With count option
|
|
3196
|
+
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', count: 2)
|
|
3197
|
+
# @example With block option
|
|
3198
|
+
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', block: 1000)
|
|
3199
|
+
# @example With noack option
|
|
3200
|
+
# redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', noack: true)
|
|
3201
|
+
#
|
|
3202
|
+
# @param group [String] the consumer group name
|
|
3203
|
+
# @param consumer [String] the consumer name
|
|
3204
|
+
# @param keys [Array<String>] one or multiple stream keys
|
|
3205
|
+
# @param ids [Array<String>] one or multiple entry ids
|
|
3206
|
+
# @param opts [Hash] several options for `XREADGROUP` command
|
|
3207
|
+
#
|
|
3208
|
+
# @option opts [Integer] :count the number of entries as limit
|
|
3209
|
+
# @option opts [Integer] :block the number of milliseconds as blocking timeout
|
|
3210
|
+
# @option opts [Boolean] :noack whether message loss is acceptable or not
|
|
3211
|
+
#
|
|
3212
|
+
# @return [Hash{String => Hash{String => Hash}}] the entries
|
|
3213
|
+
def xreadgroup(group, consumer, keys, ids, count: nil, block: nil, noack: nil)
|
|
3214
|
+
args = [:xreadgroup, 'GROUP', group, consumer]
|
|
3215
|
+
args << 'COUNT' << count if count
|
|
3216
|
+
args << 'BLOCK' << block.to_i if block
|
|
3217
|
+
args << 'NOACK' if noack
|
|
3218
|
+
_xread(args, keys, ids, block)
|
|
3219
|
+
end
|
|
3220
|
+
|
|
3221
|
+
# Removes one or multiple entries from the pending entries list of a stream consumer group.
|
|
3222
|
+
#
|
|
3223
|
+
# @example With a entry id
|
|
3224
|
+
# redis.xack('mystream', 'mygroup', '1526569495631-0')
|
|
3225
|
+
# @example With splatted entry ids
|
|
3226
|
+
# redis.xack('mystream', 'mygroup', '0-1', '0-2')
|
|
3227
|
+
# @example With arrayed entry ids
|
|
3228
|
+
# redis.xack('mystream', 'mygroup', %w[0-1 0-2])
|
|
3229
|
+
#
|
|
3230
|
+
# @param key [String] the stream key
|
|
3231
|
+
# @param group [String] the consumer group name
|
|
3232
|
+
# @param ids [Array<String>] one or multiple entry ids
|
|
3233
|
+
#
|
|
3234
|
+
# @return [Integer] the number of entries successfully acknowledged
|
|
3235
|
+
def xack(key, group, *ids)
|
|
3236
|
+
args = [:xack, key, group].concat(ids.flatten)
|
|
3237
|
+
synchronize { |client| client.call(args) }
|
|
3238
|
+
end
|
|
3239
|
+
|
|
3240
|
+
# Changes the ownership of a pending entry
|
|
3241
|
+
#
|
|
3242
|
+
# @example With splatted entry ids
|
|
3243
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-1', '0-2')
|
|
3244
|
+
# @example With arrayed entry ids
|
|
3245
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2])
|
|
3246
|
+
# @example With idle option
|
|
3247
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], idle: 1000)
|
|
3248
|
+
# @example With time option
|
|
3249
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], time: 1542866959000)
|
|
3250
|
+
# @example With retrycount option
|
|
3251
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], retrycount: 10)
|
|
3252
|
+
# @example With force option
|
|
3253
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], force: true)
|
|
3254
|
+
# @example With justid option
|
|
3255
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], justid: true)
|
|
3256
|
+
#
|
|
3257
|
+
# @param key [String] the stream key
|
|
3258
|
+
# @param group [String] the consumer group name
|
|
3259
|
+
# @param consumer [String] the consumer name
|
|
3260
|
+
# @param min_idle_time [Integer] the number of milliseconds
|
|
3261
|
+
# @param ids [Array<String>] one or multiple entry ids
|
|
3262
|
+
# @param opts [Hash] several options for `XCLAIM` command
|
|
3263
|
+
#
|
|
3264
|
+
# @option opts [Integer] :idle the number of milliseconds as last time it was delivered of the entry
|
|
3265
|
+
# @option opts [Integer] :time the number of milliseconds as a specific Unix Epoch time
|
|
3266
|
+
# @option opts [Integer] :retrycount the number of retry counter
|
|
3267
|
+
# @option opts [Boolean] :force whether to create the pending entry to the pending entries list or not
|
|
3268
|
+
# @option opts [Boolean] :justid whether to fetch just an array of entry ids or not
|
|
3269
|
+
#
|
|
3270
|
+
# @return [Hash{String => Hash}] the entries successfully claimed
|
|
3271
|
+
# @return [Array<String>] the entry ids successfully claimed if justid option is `true`
|
|
3272
|
+
def xclaim(key, group, consumer, min_idle_time, *ids, **opts)
|
|
3273
|
+
args = [:xclaim, key, group, consumer, min_idle_time].concat(ids.flatten)
|
|
3274
|
+
args.concat(['IDLE', opts[:idle].to_i]) if opts[:idle]
|
|
3275
|
+
args.concat(['TIME', opts[:time].to_i]) if opts[:time]
|
|
3276
|
+
args.concat(['RETRYCOUNT', opts[:retrycount]]) if opts[:retrycount]
|
|
3277
|
+
args << 'FORCE' if opts[:force]
|
|
3278
|
+
args << 'JUSTID' if opts[:justid]
|
|
3279
|
+
blk = opts[:justid] ? Noop : HashifyStreamEntries
|
|
3280
|
+
synchronize { |client| client.call(args, &blk) }
|
|
3281
|
+
end
|
|
3282
|
+
|
|
3283
|
+
# Transfers ownership of pending stream entries that match the specified criteria.
|
|
3284
|
+
#
|
|
3285
|
+
# @example Claim next pending message stuck > 5 minutes and mark as retry
|
|
3286
|
+
# redis.xautoclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0')
|
|
3287
|
+
# @example Claim 50 next pending messages stuck > 5 minutes and mark as retry
|
|
3288
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0', count: 50)
|
|
3289
|
+
# @example Claim next pending message stuck > 5 minutes and don't mark as retry
|
|
3290
|
+
# redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0', justid: true)
|
|
3291
|
+
# @example Claim next pending message after this id stuck > 5 minutes and mark as retry
|
|
3292
|
+
# redis.xautoclaim('mystream', 'mygroup', 'consumer1', 3600000, '1641321233-0')
|
|
3293
|
+
#
|
|
3294
|
+
# @param key [String] the stream key
|
|
3295
|
+
# @param group [String] the consumer group name
|
|
3296
|
+
# @param consumer [String] the consumer name
|
|
3297
|
+
# @param min_idle_time [Integer] the number of milliseconds
|
|
3298
|
+
# @param start [String] entry id to start scanning from or 0-0 for everything
|
|
3299
|
+
# @param count [Integer] number of messages to claim (default 1)
|
|
3300
|
+
# @param justid [Boolean] whether to fetch just an array of entry ids or not.
|
|
3301
|
+
# Does not increment retry count when true
|
|
3302
|
+
#
|
|
3303
|
+
# @return [Hash{String => Hash}] the entries successfully claimed
|
|
3304
|
+
# @return [Array<String>] the entry ids successfully claimed if justid option is `true`
|
|
3305
|
+
def xautoclaim(key, group, consumer, min_idle_time, start, count: nil, justid: false)
|
|
3306
|
+
args = [:xautoclaim, key, group, consumer, min_idle_time, start]
|
|
3307
|
+
if count
|
|
3308
|
+
args << 'COUNT' << count.to_s
|
|
3309
|
+
end
|
|
3310
|
+
args << 'JUSTID' if justid
|
|
3311
|
+
blk = justid ? HashifyStreamAutoclaimJustId : HashifyStreamAutoclaim
|
|
3312
|
+
synchronize { |client| client.call(args, &blk) }
|
|
3313
|
+
end
|
|
3314
|
+
|
|
3315
|
+
# Fetches not acknowledging pending entries
|
|
3316
|
+
#
|
|
3317
|
+
# @example With key and group
|
|
3318
|
+
# redis.xpending('mystream', 'mygroup')
|
|
3319
|
+
# @example With range options
|
|
3320
|
+
# redis.xpending('mystream', 'mygroup', '-', '+', 10)
|
|
3321
|
+
# @example With range and consumer options
|
|
3322
|
+
# redis.xpending('mystream', 'mygroup', '-', '+', 10, 'consumer1')
|
|
3323
|
+
#
|
|
3324
|
+
# @param key [String] the stream key
|
|
3325
|
+
# @param group [String] the consumer group name
|
|
3326
|
+
# @param start [String] start first entry id of range
|
|
3327
|
+
# @param end [String] end last entry id of range
|
|
3328
|
+
# @param count [Integer] count the number of entries as limit
|
|
3329
|
+
# @param consumer [String] the consumer name
|
|
3330
|
+
#
|
|
3331
|
+
# @return [Hash] the summary of pending entries
|
|
3332
|
+
# @return [Array<Hash>] the pending entries details if options were specified
|
|
3333
|
+
def xpending(key, group, *args)
|
|
3334
|
+
command_args = [:xpending, key, group]
|
|
3335
|
+
case args.size
|
|
3336
|
+
when 0, 3, 4
|
|
3337
|
+
command_args.concat(args)
|
|
3338
|
+
else
|
|
3339
|
+
raise ArgumentError, "wrong number of arguments (given #{args.size + 2}, expected 2, 5 or 6)"
|
|
3340
|
+
end
|
|
3341
|
+
|
|
3342
|
+
summary_needed = args.empty?
|
|
3343
|
+
blk = summary_needed ? HashifyStreamPendings : HashifyStreamPendingDetails
|
|
3344
|
+
synchronize { |client| client.call(command_args, &blk) }
|
|
3345
|
+
end
|
|
3346
|
+
|
|
2664
3347
|
# Interact with the sentinel command (masters, master, slaves, failover)
|
|
2665
3348
|
#
|
|
2666
3349
|
# @param [String] subcommand e.g. `masters`, `master`, `slaves`
|
|
@@ -2674,8 +3357,8 @@ class Redis
|
|
|
2674
3357
|
when "get-master-addr-by-name"
|
|
2675
3358
|
reply
|
|
2676
3359
|
else
|
|
2677
|
-
if reply.
|
|
2678
|
-
if reply[0].
|
|
3360
|
+
if reply.is_a?(Array)
|
|
3361
|
+
if reply[0].is_a?(Array)
|
|
2679
3362
|
reply.map(&Hashify)
|
|
2680
3363
|
else
|
|
2681
3364
|
Hashify.call(reply)
|
|
@@ -2688,6 +3371,46 @@ class Redis
|
|
|
2688
3371
|
end
|
|
2689
3372
|
end
|
|
2690
3373
|
|
|
3374
|
+
# Sends `CLUSTER *` command to random node and returns its reply.
|
|
3375
|
+
#
|
|
3376
|
+
# @see https://redis.io/commands#cluster Reference of cluster command
|
|
3377
|
+
#
|
|
3378
|
+
# @param subcommand [String, Symbol] the subcommand of cluster command
|
|
3379
|
+
# e.g. `:slots`, `:nodes`, `:slaves`, `:info`
|
|
3380
|
+
#
|
|
3381
|
+
# @return [Object] depends on the subcommand
|
|
3382
|
+
def cluster(subcommand, *args)
|
|
3383
|
+
subcommand = subcommand.to_s.downcase
|
|
3384
|
+
block = case subcommand
|
|
3385
|
+
when 'slots'
|
|
3386
|
+
HashifyClusterSlots
|
|
3387
|
+
when 'nodes'
|
|
3388
|
+
HashifyClusterNodes
|
|
3389
|
+
when 'slaves'
|
|
3390
|
+
HashifyClusterSlaves
|
|
3391
|
+
when 'info'
|
|
3392
|
+
HashifyInfo
|
|
3393
|
+
else
|
|
3394
|
+
Noop
|
|
3395
|
+
end
|
|
3396
|
+
|
|
3397
|
+
# @see https://github.com/antirez/redis/blob/unstable/src/redis-trib.rb#L127 raw reply expected
|
|
3398
|
+
block = Noop unless @cluster_mode
|
|
3399
|
+
|
|
3400
|
+
synchronize do |client|
|
|
3401
|
+
client.call([:cluster, subcommand] + args, &block)
|
|
3402
|
+
end
|
|
3403
|
+
end
|
|
3404
|
+
|
|
3405
|
+
# Sends `ASKING` command to random node and returns its reply.
|
|
3406
|
+
#
|
|
3407
|
+
# @see https://redis.io/topics/cluster-spec#ask-redirection ASK redirection
|
|
3408
|
+
#
|
|
3409
|
+
# @return [String] `'OK'`
|
|
3410
|
+
def asking
|
|
3411
|
+
synchronize { |client| client.call(%i[asking]) }
|
|
3412
|
+
end
|
|
3413
|
+
|
|
2691
3414
|
def id
|
|
2692
3415
|
@original_client.id
|
|
2693
3416
|
end
|
|
@@ -2701,68 +3424,183 @@ class Redis
|
|
|
2701
3424
|
end
|
|
2702
3425
|
|
|
2703
3426
|
def connection
|
|
3427
|
+
return @original_client.connection_info if @cluster_mode
|
|
3428
|
+
|
|
2704
3429
|
{
|
|
2705
|
-
:
|
|
2706
|
-
:
|
|
2707
|
-
:
|
|
2708
|
-
:
|
|
2709
|
-
:
|
|
3430
|
+
host: @original_client.host,
|
|
3431
|
+
port: @original_client.port,
|
|
3432
|
+
db: @original_client.db,
|
|
3433
|
+
id: @original_client.id,
|
|
3434
|
+
location: @original_client.location
|
|
2710
3435
|
}
|
|
2711
3436
|
end
|
|
2712
3437
|
|
|
2713
|
-
def method_missing(command, *args)
|
|
3438
|
+
def method_missing(command, *args) # rubocop:disable Style/MissingRespondToMissing
|
|
2714
3439
|
synchronize do |client|
|
|
2715
3440
|
client.call([command] + args)
|
|
2716
3441
|
end
|
|
2717
3442
|
end
|
|
2718
3443
|
|
|
2719
|
-
private
|
|
3444
|
+
private
|
|
2720
3445
|
|
|
2721
3446
|
# Commands returning 1 for true and 0 for false may be executed in a pipeline
|
|
2722
3447
|
# where the method call will return nil. Propagate the nil instead of falsely
|
|
2723
3448
|
# returning false.
|
|
2724
|
-
Boolify =
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
3449
|
+
Boolify = lambda { |value|
|
|
3450
|
+
case value
|
|
3451
|
+
when 1
|
|
3452
|
+
true
|
|
3453
|
+
when 0
|
|
3454
|
+
false
|
|
3455
|
+
else
|
|
3456
|
+
value
|
|
3457
|
+
end
|
|
3458
|
+
}
|
|
2728
3459
|
|
|
2729
|
-
BoolifySet =
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
3460
|
+
BoolifySet = lambda { |value|
|
|
3461
|
+
case value
|
|
3462
|
+
when "OK"
|
|
3463
|
+
true
|
|
3464
|
+
when nil
|
|
3465
|
+
false
|
|
3466
|
+
else
|
|
3467
|
+
value
|
|
3468
|
+
end
|
|
3469
|
+
}
|
|
3470
|
+
|
|
3471
|
+
Hashify = lambda { |value|
|
|
3472
|
+
if value.respond_to?(:each_slice)
|
|
3473
|
+
value.each_slice(2).to_h
|
|
3474
|
+
else
|
|
3475
|
+
value
|
|
3476
|
+
end
|
|
3477
|
+
}
|
|
3478
|
+
|
|
3479
|
+
Floatify = lambda { |value|
|
|
3480
|
+
case value
|
|
3481
|
+
when "inf"
|
|
3482
|
+
Float::INFINITY
|
|
3483
|
+
when "-inf"
|
|
3484
|
+
-Float::INFINITY
|
|
3485
|
+
when String
|
|
3486
|
+
Float(value)
|
|
3487
|
+
else
|
|
3488
|
+
value
|
|
3489
|
+
end
|
|
3490
|
+
}
|
|
3491
|
+
|
|
3492
|
+
FloatifyPairs = lambda { |value|
|
|
3493
|
+
return value unless value.respond_to?(:each_slice)
|
|
3494
|
+
|
|
3495
|
+
value.each_slice(2).map do |member, score|
|
|
3496
|
+
[member, Floatify.call(score)]
|
|
3497
|
+
end
|
|
3498
|
+
}
|
|
3499
|
+
|
|
3500
|
+
HashifyInfo = lambda { |reply|
|
|
3501
|
+
lines = reply.split("\r\n").grep_v(/^(#|$)/)
|
|
3502
|
+
lines.map! { |line| line.split(':', 2) }
|
|
3503
|
+
lines.compact!
|
|
3504
|
+
lines.to_h
|
|
3505
|
+
}
|
|
3506
|
+
|
|
3507
|
+
HashifyStreams = lambda { |reply|
|
|
3508
|
+
case reply
|
|
3509
|
+
when nil
|
|
3510
|
+
{}
|
|
3511
|
+
else
|
|
3512
|
+
reply.map { |key, entries| [key, HashifyStreamEntries.call(entries)] }.to_h
|
|
3513
|
+
end
|
|
3514
|
+
}
|
|
3515
|
+
|
|
3516
|
+
EMPTY_STREAM_RESPONSE = [nil].freeze
|
|
3517
|
+
private_constant :EMPTY_STREAM_RESPONSE
|
|
3518
|
+
|
|
3519
|
+
HashifyStreamEntries = lambda { |reply|
|
|
3520
|
+
reply.compact.map do |entry_id, values|
|
|
3521
|
+
[entry_id, values.each_slice(2).to_h]
|
|
3522
|
+
end
|
|
3523
|
+
}
|
|
3524
|
+
|
|
3525
|
+
HashifyStreamAutoclaim = lambda { |reply|
|
|
3526
|
+
{
|
|
3527
|
+
'next' => reply[0],
|
|
3528
|
+
'entries' => reply[1].map { |entry| [entry[0], entry[1].each_slice(2).to_h] }
|
|
2736
3529
|
}
|
|
3530
|
+
}
|
|
2737
3531
|
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
hash[field] = value
|
|
2743
|
-
end
|
|
2744
|
-
hash
|
|
3532
|
+
HashifyStreamAutoclaimJustId = lambda { |reply|
|
|
3533
|
+
{
|
|
3534
|
+
'next' => reply[0],
|
|
3535
|
+
'entries' => reply[1]
|
|
2745
3536
|
}
|
|
3537
|
+
}
|
|
2746
3538
|
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
Float(str)
|
|
2754
|
-
end
|
|
2755
|
-
end
|
|
3539
|
+
HashifyStreamPendings = lambda { |reply|
|
|
3540
|
+
{
|
|
3541
|
+
'size' => reply[0],
|
|
3542
|
+
'min_entry_id' => reply[1],
|
|
3543
|
+
'max_entry_id' => reply[2],
|
|
3544
|
+
'consumers' => reply[3].nil? ? {} : reply[3].to_h
|
|
2756
3545
|
}
|
|
3546
|
+
}
|
|
2757
3547
|
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
3548
|
+
HashifyStreamPendingDetails = lambda { |reply|
|
|
3549
|
+
reply.map do |arr|
|
|
3550
|
+
{
|
|
3551
|
+
'entry_id' => arr[0],
|
|
3552
|
+
'consumer' => arr[1],
|
|
3553
|
+
'elapsed' => arr[2],
|
|
3554
|
+
'count' => arr[3]
|
|
3555
|
+
}
|
|
3556
|
+
end
|
|
3557
|
+
}
|
|
3558
|
+
|
|
3559
|
+
HashifyClusterNodeInfo = lambda { |str|
|
|
3560
|
+
arr = str.split(' ')
|
|
3561
|
+
{
|
|
3562
|
+
'node_id' => arr[0],
|
|
3563
|
+
'ip_port' => arr[1],
|
|
3564
|
+
'flags' => arr[2].split(','),
|
|
3565
|
+
'master_node_id' => arr[3],
|
|
3566
|
+
'ping_sent' => arr[4],
|
|
3567
|
+
'pong_recv' => arr[5],
|
|
3568
|
+
'config_epoch' => arr[6],
|
|
3569
|
+
'link_state' => arr[7],
|
|
3570
|
+
'slots' => arr[8].nil? ? nil : Range.new(*arr[8].split('-'))
|
|
2765
3571
|
}
|
|
3572
|
+
}
|
|
3573
|
+
|
|
3574
|
+
HashifyClusterSlots = lambda { |reply|
|
|
3575
|
+
reply.map do |arr|
|
|
3576
|
+
first_slot, last_slot = arr[0..1]
|
|
3577
|
+
master = { 'ip' => arr[2][0], 'port' => arr[2][1], 'node_id' => arr[2][2] }
|
|
3578
|
+
replicas = arr[3..-1].map { |r| { 'ip' => r[0], 'port' => r[1], 'node_id' => r[2] } }
|
|
3579
|
+
{
|
|
3580
|
+
'start_slot' => first_slot,
|
|
3581
|
+
'end_slot' => last_slot,
|
|
3582
|
+
'master' => master,
|
|
3583
|
+
'replicas' => replicas
|
|
3584
|
+
}
|
|
3585
|
+
end
|
|
3586
|
+
}
|
|
3587
|
+
|
|
3588
|
+
HashifyClusterNodes = lambda { |reply|
|
|
3589
|
+
reply.split(/[\r\n]+/).map { |str| HashifyClusterNodeInfo.call(str) }
|
|
3590
|
+
}
|
|
3591
|
+
|
|
3592
|
+
HashifyClusterSlaves = lambda { |reply|
|
|
3593
|
+
reply.map { |str| HashifyClusterNodeInfo.call(str) }
|
|
3594
|
+
}
|
|
3595
|
+
|
|
3596
|
+
Noop = ->(reply) { reply }
|
|
3597
|
+
|
|
3598
|
+
def _geoarguments(*args, options: nil, sort: nil, count: nil)
|
|
3599
|
+
args.push sort if sort
|
|
3600
|
+
args.push 'count', count if count
|
|
3601
|
+
args.push options if options
|
|
3602
|
+
args
|
|
3603
|
+
end
|
|
2766
3604
|
|
|
2767
3605
|
def _subscription(method, timeout, channels, block)
|
|
2768
3606
|
return @client.call([method] + channels) if subscribed?
|
|
@@ -2779,10 +3617,29 @@ private
|
|
|
2779
3617
|
end
|
|
2780
3618
|
end
|
|
2781
3619
|
|
|
3620
|
+
def _xread(args, keys, ids, blocking_timeout_msec)
|
|
3621
|
+
keys = keys.is_a?(Array) ? keys : [keys]
|
|
3622
|
+
ids = ids.is_a?(Array) ? ids : [ids]
|
|
3623
|
+
args << 'STREAMS'
|
|
3624
|
+
args.concat(keys)
|
|
3625
|
+
args.concat(ids)
|
|
3626
|
+
|
|
3627
|
+
synchronize do |client|
|
|
3628
|
+
if blocking_timeout_msec.nil?
|
|
3629
|
+
client.call(args, &HashifyStreams)
|
|
3630
|
+
elsif blocking_timeout_msec.to_f.zero?
|
|
3631
|
+
client.call_without_timeout(args, &HashifyStreams)
|
|
3632
|
+
else
|
|
3633
|
+
timeout = client.timeout.to_f + blocking_timeout_msec.to_f / 1000.0
|
|
3634
|
+
client.call_with_timeout(args, timeout, &HashifyStreams)
|
|
3635
|
+
end
|
|
3636
|
+
end
|
|
3637
|
+
end
|
|
2782
3638
|
end
|
|
2783
3639
|
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
3640
|
+
require_relative "redis/version"
|
|
3641
|
+
require_relative "redis/connection"
|
|
3642
|
+
require_relative "redis/client"
|
|
3643
|
+
require_relative "redis/cluster"
|
|
3644
|
+
require_relative "redis/pipeline"
|
|
3645
|
+
require_relative "redis/subscribe"
|