redis 3.2.2 → 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 +175 -13
- data/README.md +223 -76
- data/lib/redis.rb +1360 -445
- data/lib/redis/client.rb +183 -103
- 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 +9 -6
- data/lib/redis/connection/registry.rb +2 -1
- data/lib/redis/connection/ruby.rb +168 -63
- data/lib/redis/connection/synchrony.rb +29 -7
- data/lib/redis/distributed.rb +156 -74
- 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 +20 -13
- data/lib/redis/version.rb +3 -1
- metadata +41 -170
- data/.gitignore +0 -16
- data/.travis.yml +0 -59
- 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/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 -250
- 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 -437
- 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 -125
- 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 -254
- 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/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 -119
- 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 -32
- 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,67 @@
|
|
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)
|
64
|
+
@queue = Hash.new { |h, k| h[k] = [] }
|
52
65
|
|
53
66
|
super() # Monitor#initialize
|
54
67
|
end
|
@@ -58,7 +71,7 @@ class Redis
|
|
58
71
|
end
|
59
72
|
|
60
73
|
# Run code with the client reconnecting
|
61
|
-
def with_reconnect(val=true, &blk)
|
74
|
+
def with_reconnect(val = true, &blk)
|
62
75
|
synchronize do |client|
|
63
76
|
client.with_reconnect(val, &blk)
|
64
77
|
end
|
@@ -75,24 +88,75 @@ class Redis
|
|
75
88
|
end
|
76
89
|
|
77
90
|
# Disconnect the client as quickly and silently as possible.
|
78
|
-
def
|
91
|
+
def close
|
79
92
|
@original_client.disconnect
|
80
93
|
end
|
94
|
+
alias disconnect! close
|
95
|
+
|
96
|
+
# Sends a command to Redis and returns its reply.
|
97
|
+
#
|
98
|
+
# Replies are converted to Ruby objects according to the RESP protocol, so
|
99
|
+
# you can expect a Ruby array, integer or nil when Redis sends one. Higher
|
100
|
+
# level transformations, such as converting an array of pairs into a Ruby
|
101
|
+
# hash, are up to consumers.
|
102
|
+
#
|
103
|
+
# Redis error replies are raised as Ruby exceptions.
|
104
|
+
def call(*command)
|
105
|
+
synchronize do |client|
|
106
|
+
client.call(command)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Queues a command for pipelining.
|
111
|
+
#
|
112
|
+
# Commands in the queue are executed with the Redis#commit method.
|
113
|
+
#
|
114
|
+
# See http://redis.io/topics/pipelining for more details.
|
115
|
+
#
|
116
|
+
def queue(*command)
|
117
|
+
synchronize do
|
118
|
+
@queue[Thread.current.object_id] << command
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Sends all commands in the queue.
|
123
|
+
#
|
124
|
+
# See http://redis.io/topics/pipelining for more details.
|
125
|
+
#
|
126
|
+
def commit
|
127
|
+
synchronize do |client|
|
128
|
+
begin
|
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)
|
135
|
+
ensure
|
136
|
+
@queue.delete(Thread.current.object_id)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def _client
|
142
|
+
@client
|
143
|
+
end
|
81
144
|
|
82
145
|
# Authenticate to the server.
|
83
146
|
#
|
84
|
-
# @param [String]
|
85
|
-
#
|
147
|
+
# @param [Array<String>] args includes both username and password
|
148
|
+
# or only password
|
86
149
|
# @return [String] `OK`
|
87
|
-
|
150
|
+
# @see https://redis.io/commands/auth AUTH command
|
151
|
+
def auth(*args)
|
88
152
|
synchronize do |client|
|
89
|
-
client.call([:auth,
|
153
|
+
client.call([:auth, *args])
|
90
154
|
end
|
91
155
|
end
|
92
156
|
|
93
157
|
# Change the selected database for the current connection.
|
94
158
|
#
|
95
|
-
# @param [
|
159
|
+
# @param [Integer] db zero-based index of the DB to use (0 to 15)
|
96
160
|
# @return [String] `OK`
|
97
161
|
def select(db)
|
98
162
|
synchronize do |client|
|
@@ -103,10 +167,11 @@ class Redis
|
|
103
167
|
|
104
168
|
# Ping the server.
|
105
169
|
#
|
170
|
+
# @param [optional, String] message
|
106
171
|
# @return [String] `PONG`
|
107
|
-
def ping
|
172
|
+
def ping(message = nil)
|
108
173
|
synchronize do |client|
|
109
|
-
client.call([:ping])
|
174
|
+
client.call([:ping, message].compact)
|
110
175
|
end
|
111
176
|
end
|
112
177
|
|
@@ -160,8 +225,27 @@ class Redis
|
|
160
225
|
def config(action, *args)
|
161
226
|
synchronize do |client|
|
162
227
|
client.call([:config, action] + args) do |reply|
|
163
|
-
if reply.
|
164
|
-
|
228
|
+
if reply.is_a?(Array) && action == :get
|
229
|
+
Hashify.call(reply)
|
230
|
+
else
|
231
|
+
reply
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
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
|
165
249
|
else
|
166
250
|
reply
|
167
251
|
end
|
@@ -171,7 +255,7 @@ class Redis
|
|
171
255
|
|
172
256
|
# Return the number of keys in the selected database.
|
173
257
|
#
|
174
|
-
# @return [
|
258
|
+
# @return [Integer]
|
175
259
|
def dbsize
|
176
260
|
synchronize do |client|
|
177
261
|
client.call([:dbsize])
|
@@ -186,19 +270,31 @@ class Redis
|
|
186
270
|
|
187
271
|
# Remove all keys from all databases.
|
188
272
|
#
|
273
|
+
# @param [Hash] options
|
274
|
+
# - `:async => Boolean`: async flush (default: false)
|
189
275
|
# @return [String] `OK`
|
190
|
-
def flushall
|
276
|
+
def flushall(options = nil)
|
191
277
|
synchronize do |client|
|
192
|
-
|
278
|
+
if options && options[:async]
|
279
|
+
client.call(%i[flushall async])
|
280
|
+
else
|
281
|
+
client.call([:flushall])
|
282
|
+
end
|
193
283
|
end
|
194
284
|
end
|
195
285
|
|
196
286
|
# Remove all keys from the current database.
|
197
287
|
#
|
288
|
+
# @param [Hash] options
|
289
|
+
# - `:async => Boolean`: async flush (default: false)
|
198
290
|
# @return [String] `OK`
|
199
|
-
def flushdb
|
291
|
+
def flushdb(options = nil)
|
200
292
|
synchronize do |client|
|
201
|
-
|
293
|
+
if options && options[:async]
|
294
|
+
client.call(%i[flushdb async])
|
295
|
+
else
|
296
|
+
client.call([:flushdb])
|
297
|
+
end
|
202
298
|
end
|
203
299
|
end
|
204
300
|
|
@@ -209,10 +305,8 @@ class Redis
|
|
209
305
|
def info(cmd = nil)
|
210
306
|
synchronize do |client|
|
211
307
|
client.call([:info, cmd].compact) do |reply|
|
212
|
-
if reply.
|
213
|
-
reply =
|
214
|
-
line.split(":", 2) unless line =~ /^(#|$)/
|
215
|
-
end.compact]
|
308
|
+
if reply.is_a?(String)
|
309
|
+
reply = HashifyInfo.call(reply)
|
216
310
|
|
217
311
|
if cmd && cmd.to_s == "commandstats"
|
218
312
|
# Extract nested hashes for INFO COMMANDSTATS
|
@@ -230,7 +324,7 @@ class Redis
|
|
230
324
|
|
231
325
|
# Get the UNIX time stamp of the last successful save to disk.
|
232
326
|
#
|
233
|
-
# @return [
|
327
|
+
# @return [Integer]
|
234
328
|
def lastsave
|
235
329
|
synchronize do |client|
|
236
330
|
client.call([:lastsave])
|
@@ -282,9 +376,9 @@ class Redis
|
|
282
376
|
# Interact with the slowlog (get, len, reset)
|
283
377
|
#
|
284
378
|
# @param [String] subcommand e.g. `get`, `len`, `reset`
|
285
|
-
# @param [
|
286
|
-
# @return [Array<String>,
|
287
|
-
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)
|
288
382
|
synchronize do |client|
|
289
383
|
args = [:slowlog, subcommand]
|
290
384
|
args << length if length
|
@@ -304,12 +398,12 @@ class Redis
|
|
304
398
|
# @example
|
305
399
|
# r.time # => [ 1333093196, 606806 ]
|
306
400
|
#
|
307
|
-
# @return [Array<
|
401
|
+
# @return [Array<Integer>] tuple of seconds since UNIX epoch and
|
308
402
|
# microseconds in the current second
|
309
403
|
def time
|
310
404
|
synchronize do |client|
|
311
405
|
client.call([:time]) do |reply|
|
312
|
-
reply
|
406
|
+
reply&.map(&:to_i)
|
313
407
|
end
|
314
408
|
end
|
315
409
|
end
|
@@ -320,36 +414,36 @@ class Redis
|
|
320
414
|
# @return [Boolean] whether the timeout was removed or not
|
321
415
|
def persist(key)
|
322
416
|
synchronize do |client|
|
323
|
-
client.call([:persist, key], &
|
417
|
+
client.call([:persist, key], &Boolify)
|
324
418
|
end
|
325
419
|
end
|
326
420
|
|
327
421
|
# Set a key's time to live in seconds.
|
328
422
|
#
|
329
423
|
# @param [String] key
|
330
|
-
# @param [
|
424
|
+
# @param [Integer] seconds time to live
|
331
425
|
# @return [Boolean] whether the timeout was set or not
|
332
426
|
def expire(key, seconds)
|
333
427
|
synchronize do |client|
|
334
|
-
client.call([:expire, key, seconds], &
|
428
|
+
client.call([:expire, key, seconds], &Boolify)
|
335
429
|
end
|
336
430
|
end
|
337
431
|
|
338
432
|
# Set the expiration for a key as a UNIX timestamp.
|
339
433
|
#
|
340
434
|
# @param [String] key
|
341
|
-
# @param [
|
435
|
+
# @param [Integer] unix_time expiry time specified as a UNIX timestamp
|
342
436
|
# @return [Boolean] whether the timeout was set or not
|
343
437
|
def expireat(key, unix_time)
|
344
438
|
synchronize do |client|
|
345
|
-
client.call([:expireat, key, unix_time], &
|
439
|
+
client.call([:expireat, key, unix_time], &Boolify)
|
346
440
|
end
|
347
441
|
end
|
348
442
|
|
349
443
|
# Get the time to live (in seconds) for a key.
|
350
444
|
#
|
351
445
|
# @param [String] key
|
352
|
-
# @return [
|
446
|
+
# @return [Integer] remaining time to live in seconds.
|
353
447
|
#
|
354
448
|
# In Redis 2.6 or older the command returns -1 if the key does not exist or if
|
355
449
|
# the key exist but has no associated expire.
|
@@ -367,29 +461,29 @@ class Redis
|
|
367
461
|
# Set a key's time to live in milliseconds.
|
368
462
|
#
|
369
463
|
# @param [String] key
|
370
|
-
# @param [
|
464
|
+
# @param [Integer] milliseconds time to live
|
371
465
|
# @return [Boolean] whether the timeout was set or not
|
372
466
|
def pexpire(key, milliseconds)
|
373
467
|
synchronize do |client|
|
374
|
-
client.call([:pexpire, key, milliseconds], &
|
468
|
+
client.call([:pexpire, key, milliseconds], &Boolify)
|
375
469
|
end
|
376
470
|
end
|
377
471
|
|
378
472
|
# Set the expiration for a key as number of milliseconds from UNIX Epoch.
|
379
473
|
#
|
380
474
|
# @param [String] key
|
381
|
-
# @param [
|
475
|
+
# @param [Integer] ms_unix_time expiry time specified as number of milliseconds from UNIX Epoch.
|
382
476
|
# @return [Boolean] whether the timeout was set or not
|
383
477
|
def pexpireat(key, ms_unix_time)
|
384
478
|
synchronize do |client|
|
385
|
-
client.call([:pexpireat, key, ms_unix_time], &
|
479
|
+
client.call([:pexpireat, key, ms_unix_time], &Boolify)
|
386
480
|
end
|
387
481
|
end
|
388
482
|
|
389
483
|
# Get the time to live (in milliseconds) for a key.
|
390
484
|
#
|
391
485
|
# @param [String] key
|
392
|
-
# @return [
|
486
|
+
# @return [Integer] remaining time to live in milliseconds
|
393
487
|
# In Redis 2.6 or older the command returns -1 if the key does not exist or if
|
394
488
|
# the key exist but has no associated expire.
|
395
489
|
#
|
@@ -418,50 +512,104 @@ class Redis
|
|
418
512
|
# @param [String] key
|
419
513
|
# @param [String] ttl
|
420
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]
|
421
518
|
# @return [String] `"OK"`
|
422
|
-
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
|
+
|
423
523
|
synchronize do |client|
|
424
|
-
client.call(
|
524
|
+
client.call(args)
|
425
525
|
end
|
426
526
|
end
|
427
527
|
|
428
528
|
# Transfer a key from the connected instance to another instance.
|
429
529
|
#
|
430
|
-
# @param [String] key
|
530
|
+
# @param [String, Array<String>] key
|
431
531
|
# @param [Hash] options
|
432
532
|
# - `:host => String`: host of instance to migrate to
|
433
533
|
# - `:port => Integer`: port of instance to migrate to
|
434
534
|
# - `:db => Integer`: database to migrate to (default: same as source)
|
435
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.
|
436
538
|
# @return [String] `"OK"`
|
437
539
|
def migrate(key, options)
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
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)
|
442
549
|
|
443
|
-
synchronize
|
444
|
-
client.call([:migrate, host, port, key, db, timeout])
|
445
|
-
end
|
550
|
+
synchronize { |client| client.call(args) }
|
446
551
|
end
|
447
552
|
|
448
553
|
# Delete one or more keys.
|
449
554
|
#
|
450
555
|
# @param [String, Array<String>] keys
|
451
|
-
# @return [
|
556
|
+
# @return [Integer] number of keys that were deleted
|
452
557
|
def del(*keys)
|
558
|
+
keys.flatten!(1)
|
559
|
+
return 0 if keys.empty?
|
560
|
+
|
453
561
|
synchronize do |client|
|
454
562
|
client.call([:del] + keys)
|
455
563
|
end
|
456
564
|
end
|
457
565
|
|
458
|
-
#
|
566
|
+
# Unlink one or more keys.
|
459
567
|
#
|
460
|
-
# @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
|
461
607
|
# @return [Boolean]
|
462
|
-
def exists(
|
608
|
+
def exists?(*keys)
|
463
609
|
synchronize do |client|
|
464
|
-
client.call([:exists,
|
610
|
+
client.call([:exists, *keys]) do |value|
|
611
|
+
value > 0
|
612
|
+
end
|
465
613
|
end
|
466
614
|
end
|
467
615
|
|
@@ -472,7 +620,7 @@ class Redis
|
|
472
620
|
def keys(pattern = "*")
|
473
621
|
synchronize do |client|
|
474
622
|
client.call([:keys, pattern]) do |reply|
|
475
|
-
if reply.
|
623
|
+
if reply.is_a?(String)
|
476
624
|
reply.split(" ")
|
477
625
|
else
|
478
626
|
reply
|
@@ -498,11 +646,11 @@ class Redis
|
|
498
646
|
# # => "bar"
|
499
647
|
#
|
500
648
|
# @param [String] key
|
501
|
-
# @param [
|
649
|
+
# @param [Integer] db
|
502
650
|
# @return [Boolean] whether the key was moved or not
|
503
651
|
def move(key, db)
|
504
652
|
synchronize do |client|
|
505
|
-
client.call([:move, key, db], &
|
653
|
+
client.call([:move, key, db], &Boolify)
|
506
654
|
end
|
507
655
|
end
|
508
656
|
|
@@ -539,7 +687,7 @@ class Redis
|
|
539
687
|
# @return [Boolean] whether the key was renamed or not
|
540
688
|
def renamenx(old_name, new_name)
|
541
689
|
synchronize do |client|
|
542
|
-
client.call([:renamenx, old_name, new_name], &
|
690
|
+
client.call([:renamenx, old_name, new_name], &Boolify)
|
543
691
|
end
|
544
692
|
end
|
545
693
|
|
@@ -562,36 +710,33 @@ class Redis
|
|
562
710
|
# - `:order => String`: combination of `ASC`, `DESC` and optionally `ALPHA`
|
563
711
|
# - `:store => String`: key to store the result at
|
564
712
|
#
|
565
|
-
# @return [Array<String>, Array<Array<String>>,
|
713
|
+
# @return [Array<String>, Array<Array<String>>, Integer]
|
566
714
|
# - when `:get` is not specified, or holds a single element, an array of elements
|
567
715
|
# - when `:get` is specified, and holds more than one element, an array of
|
568
716
|
# elements where every element is an array with the result for every
|
569
717
|
# element specified in `:get`
|
570
718
|
# - when `:store` is specified, the number of elements in the stored result
|
571
|
-
def sort(key,
|
572
|
-
args = []
|
573
|
-
|
574
|
-
by = options[:by]
|
575
|
-
args.concat(["BY", by]) if by
|
719
|
+
def sort(key, by: nil, limit: nil, get: nil, order: nil, store: nil)
|
720
|
+
args = [:sort, key]
|
721
|
+
args << "BY" << by if by
|
576
722
|
|
577
|
-
|
578
|
-
|
723
|
+
if limit
|
724
|
+
args << "LIMIT"
|
725
|
+
args.concat(limit)
|
726
|
+
end
|
579
727
|
|
580
|
-
get = Array(
|
581
|
-
|
728
|
+
get = Array(get)
|
729
|
+
get.each do |item|
|
730
|
+
args << "GET" << item
|
731
|
+
end
|
582
732
|
|
583
|
-
order = options[:order]
|
584
733
|
args.concat(order.split(" ")) if order
|
585
|
-
|
586
|
-
store = options[:store]
|
587
|
-
args.concat(["STORE", store]) if store
|
734
|
+
args << "STORE" << store if store
|
588
735
|
|
589
736
|
synchronize do |client|
|
590
|
-
client.call(
|
737
|
+
client.call(args) do |reply|
|
591
738
|
if get.size > 1 && !store
|
592
|
-
if reply
|
593
|
-
reply.each_slice(get.size).to_a
|
594
|
-
end
|
739
|
+
reply.each_slice(get.size).to_a if reply
|
595
740
|
else
|
596
741
|
reply
|
597
742
|
end
|
@@ -616,7 +761,7 @@ class Redis
|
|
616
761
|
# # => 4
|
617
762
|
#
|
618
763
|
# @param [String] key
|
619
|
-
# @return [
|
764
|
+
# @return [Integer] value after decrementing it
|
620
765
|
def decr(key)
|
621
766
|
synchronize do |client|
|
622
767
|
client.call([:decr, key])
|
@@ -630,8 +775,8 @@ class Redis
|
|
630
775
|
# # => 0
|
631
776
|
#
|
632
777
|
# @param [String] key
|
633
|
-
# @param [
|
634
|
-
# @return [
|
778
|
+
# @param [Integer] decrement
|
779
|
+
# @return [Integer] value after decrementing it
|
635
780
|
def decrby(key, decrement)
|
636
781
|
synchronize do |client|
|
637
782
|
client.call([:decrby, key, decrement])
|
@@ -645,7 +790,7 @@ class Redis
|
|
645
790
|
# # => 6
|
646
791
|
#
|
647
792
|
# @param [String] key
|
648
|
-
# @return [
|
793
|
+
# @return [Integer] value after incrementing it
|
649
794
|
def incr(key)
|
650
795
|
synchronize do |client|
|
651
796
|
client.call([:incr, key])
|
@@ -659,8 +804,8 @@ class Redis
|
|
659
804
|
# # => 10
|
660
805
|
#
|
661
806
|
# @param [String] key
|
662
|
-
# @param [
|
663
|
-
# @return [
|
807
|
+
# @param [Integer] increment
|
808
|
+
# @return [Integer] value after incrementing it
|
664
809
|
def incrby(key, increment)
|
665
810
|
synchronize do |client|
|
666
811
|
client.call([:incrby, key, increment])
|
@@ -678,7 +823,7 @@ class Redis
|
|
678
823
|
# @return [Float] value after incrementing it
|
679
824
|
def incrbyfloat(key, increment)
|
680
825
|
synchronize do |client|
|
681
|
-
client.call([:incrbyfloat, key, increment], &
|
826
|
+
client.call([:incrbyfloat, key, increment], &Floatify)
|
682
827
|
end
|
683
828
|
end
|
684
829
|
|
@@ -687,41 +832,33 @@ class Redis
|
|
687
832
|
# @param [String] key
|
688
833
|
# @param [String] value
|
689
834
|
# @param [Hash] options
|
690
|
-
# - `:ex =>
|
691
|
-
# - `:px =>
|
835
|
+
# - `:ex => Integer`: Set the specified expire time, in seconds.
|
836
|
+
# - `:px => Integer`: Set the specified expire time, in milliseconds.
|
692
837
|
# - `:nx => true`: Only set the key if it does not already exist.
|
693
838
|
# - `:xx => true`: Only set the key if it already exist.
|
839
|
+
# - `:keepttl => true`: Retain the time to live associated with the key.
|
694
840
|
# @return [String, Boolean] `"OK"` or true, false if `:nx => true` or `:xx => true`
|
695
|
-
def set(key, value,
|
696
|
-
args = []
|
697
|
-
|
698
|
-
|
699
|
-
args
|
700
|
-
|
701
|
-
|
702
|
-
args.concat(["PX", px]) if px
|
703
|
-
|
704
|
-
nx = options[:nx]
|
705
|
-
args.concat(["NX"]) if nx
|
706
|
-
|
707
|
-
xx = options[:xx]
|
708
|
-
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
|
709
848
|
|
710
849
|
synchronize do |client|
|
711
850
|
if nx || xx
|
712
|
-
client.call(
|
851
|
+
client.call(args, &BoolifySet)
|
713
852
|
else
|
714
|
-
client.call(
|
853
|
+
client.call(args)
|
715
854
|
end
|
716
855
|
end
|
717
856
|
end
|
718
857
|
|
719
|
-
alias :[]= :set
|
720
|
-
|
721
858
|
# Set the time to live in seconds of a key.
|
722
859
|
#
|
723
860
|
# @param [String] key
|
724
|
-
# @param [
|
861
|
+
# @param [Integer] ttl
|
725
862
|
# @param [String] value
|
726
863
|
# @return [String] `"OK"`
|
727
864
|
def setex(key, ttl, value)
|
@@ -733,7 +870,7 @@ class Redis
|
|
733
870
|
# Set the time to live in milliseconds of a key.
|
734
871
|
#
|
735
872
|
# @param [String] key
|
736
|
-
# @param [
|
873
|
+
# @param [Integer] ttl
|
737
874
|
# @param [String] value
|
738
875
|
# @return [String] `"OK"`
|
739
876
|
def psetex(key, ttl, value)
|
@@ -749,7 +886,7 @@ class Redis
|
|
749
886
|
# @return [Boolean] whether the key was set or not
|
750
887
|
def setnx(key, value)
|
751
888
|
synchronize do |client|
|
752
|
-
client.call([:setnx, key, value.to_s], &
|
889
|
+
client.call([:setnx, key, value.to_s], &Boolify)
|
753
890
|
end
|
754
891
|
end
|
755
892
|
|
@@ -795,7 +932,7 @@ class Redis
|
|
795
932
|
# @see #mapped_msetnx
|
796
933
|
def msetnx(*args)
|
797
934
|
synchronize do |client|
|
798
|
-
client.call([:msetnx
|
935
|
+
client.call([:msetnx, *args], &Boolify)
|
799
936
|
end
|
800
937
|
end
|
801
938
|
|
@@ -823,12 +960,10 @@ class Redis
|
|
823
960
|
end
|
824
961
|
end
|
825
962
|
|
826
|
-
alias :[] :get
|
827
|
-
|
828
963
|
# Get the values of all the given keys.
|
829
964
|
#
|
830
965
|
# @example
|
831
|
-
# redis.mget("key1", "
|
966
|
+
# redis.mget("key1", "key2")
|
832
967
|
# # => ["v1", "v2"]
|
833
968
|
#
|
834
969
|
# @param [Array<String>] keys
|
@@ -837,14 +972,14 @@ class Redis
|
|
837
972
|
# @see #mapped_mget
|
838
973
|
def mget(*keys, &blk)
|
839
974
|
synchronize do |client|
|
840
|
-
client.call([:mget
|
975
|
+
client.call([:mget, *keys], &blk)
|
841
976
|
end
|
842
977
|
end
|
843
978
|
|
844
979
|
# Get the values of all the given keys.
|
845
980
|
#
|
846
981
|
# @example
|
847
|
-
# redis.mapped_mget("key1", "
|
982
|
+
# redis.mapped_mget("key1", "key2")
|
848
983
|
# # => { "key1" => "v1", "key2" => "v2" }
|
849
984
|
#
|
850
985
|
# @param [Array<String>] keys array of keys
|
@@ -853,7 +988,7 @@ class Redis
|
|
853
988
|
# @see #mget
|
854
989
|
def mapped_mget(*keys)
|
855
990
|
mget(*keys) do |reply|
|
856
|
-
if reply.
|
991
|
+
if reply.is_a?(Array)
|
857
992
|
Hash[keys.zip(reply)]
|
858
993
|
else
|
859
994
|
reply
|
@@ -864,9 +999,9 @@ class Redis
|
|
864
999
|
# Overwrite part of a string at key starting at the specified offset.
|
865
1000
|
#
|
866
1001
|
# @param [String] key
|
867
|
-
# @param [
|
1002
|
+
# @param [Integer] offset byte offset
|
868
1003
|
# @param [String] value
|
869
|
-
# @return [
|
1004
|
+
# @return [Integer] length of the string after it was modified
|
870
1005
|
def setrange(key, offset, value)
|
871
1006
|
synchronize do |client|
|
872
1007
|
client.call([:setrange, key, offset, value.to_s])
|
@@ -876,10 +1011,10 @@ class Redis
|
|
876
1011
|
# Get a substring of the string stored at a key.
|
877
1012
|
#
|
878
1013
|
# @param [String] key
|
879
|
-
# @param [
|
880
|
-
# @param [
|
1014
|
+
# @param [Integer] start zero-based start offset
|
1015
|
+
# @param [Integer] stop zero-based end offset. Use -1 for representing
|
881
1016
|
# the end of the string
|
882
|
-
# @return [
|
1017
|
+
# @return [Integer] `0` or `1`
|
883
1018
|
def getrange(key, start, stop)
|
884
1019
|
synchronize do |client|
|
885
1020
|
client.call([:getrange, key, start, stop])
|
@@ -889,9 +1024,9 @@ class Redis
|
|
889
1024
|
# Sets or clears the bit at offset in the string value stored at key.
|
890
1025
|
#
|
891
1026
|
# @param [String] key
|
892
|
-
# @param [
|
893
|
-
# @param [
|
894
|
-
# @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`
|
895
1030
|
def setbit(key, offset, value)
|
896
1031
|
synchronize do |client|
|
897
1032
|
client.call([:setbit, key, offset, value])
|
@@ -901,8 +1036,8 @@ class Redis
|
|
901
1036
|
# Returns the bit value at offset in the string value stored at key.
|
902
1037
|
#
|
903
1038
|
# @param [String] key
|
904
|
-
# @param [
|
905
|
-
# @return [
|
1039
|
+
# @param [Integer] offset bit offset
|
1040
|
+
# @return [Integer] `0` or `1`
|
906
1041
|
def getbit(key, offset)
|
907
1042
|
synchronize do |client|
|
908
1043
|
client.call([:getbit, key, offset])
|
@@ -913,7 +1048,7 @@ class Redis
|
|
913
1048
|
#
|
914
1049
|
# @param [String] key
|
915
1050
|
# @param [String] value value to append
|
916
|
-
# @return [
|
1051
|
+
# @return [Integer] length of the string after appending
|
917
1052
|
def append(key, value)
|
918
1053
|
synchronize do |client|
|
919
1054
|
client.call([:append, key, value])
|
@@ -923,9 +1058,9 @@ class Redis
|
|
923
1058
|
# Count the number of set bits in a range of the string value stored at key.
|
924
1059
|
#
|
925
1060
|
# @param [String] key
|
926
|
-
# @param [
|
927
|
-
# @param [
|
928
|
-
# @return [
|
1061
|
+
# @param [Integer] start start index
|
1062
|
+
# @param [Integer] stop stop index
|
1063
|
+
# @return [Integer] the number of bits set to 1
|
929
1064
|
def bitcount(key, start = 0, stop = -1)
|
930
1065
|
synchronize do |client|
|
931
1066
|
client.call([:bitcount, key, start, stop])
|
@@ -937,25 +1072,23 @@ class Redis
|
|
937
1072
|
# @param [String] operation e.g. `and`, `or`, `xor`, `not`
|
938
1073
|
# @param [String] destkey destination key
|
939
1074
|
# @param [String, Array<String>] keys one or more source keys to perform `operation`
|
940
|
-
# @return [
|
1075
|
+
# @return [Integer] the length of the string stored in `destkey`
|
941
1076
|
def bitop(operation, destkey, *keys)
|
942
1077
|
synchronize do |client|
|
943
|
-
client.call([:bitop, operation, destkey
|
1078
|
+
client.call([:bitop, operation, destkey, *keys])
|
944
1079
|
end
|
945
1080
|
end
|
946
1081
|
|
947
1082
|
# Return the position of the first bit set to 1 or 0 in a string.
|
948
1083
|
#
|
949
1084
|
# @param [String] key
|
950
|
-
# @param [
|
951
|
-
# @param [
|
952
|
-
# @param [
|
953
|
-
# @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.
|
954
1089
|
# -1 if looking for 1 and it is not found or start and stop are given.
|
955
|
-
def bitpos(key, bit, start=nil, stop=nil)
|
956
|
-
|
957
|
-
raise(ArgumentError, 'stop parameter specified without start parameter')
|
958
|
-
end
|
1090
|
+
def bitpos(key, bit, start = nil, stop = nil)
|
1091
|
+
raise(ArgumentError, 'stop parameter specified without start parameter') if stop && !start
|
959
1092
|
|
960
1093
|
synchronize do |client|
|
961
1094
|
command = [:bitpos, key, bit]
|
@@ -980,7 +1113,7 @@ class Redis
|
|
980
1113
|
# Get the length of the value stored in a key.
|
981
1114
|
#
|
982
1115
|
# @param [String] key
|
983
|
-
# @return [
|
1116
|
+
# @return [Integer] the length of the value stored in the key, or 0
|
984
1117
|
# if the key does not exist
|
985
1118
|
def strlen(key)
|
986
1119
|
synchronize do |client|
|
@@ -991,7 +1124,7 @@ class Redis
|
|
991
1124
|
# Get the length of a list.
|
992
1125
|
#
|
993
1126
|
# @param [String] key
|
994
|
-
# @return [
|
1127
|
+
# @return [Integer]
|
995
1128
|
def llen(key)
|
996
1129
|
synchronize do |client|
|
997
1130
|
client.call([:llen, key])
|
@@ -1001,8 +1134,8 @@ class Redis
|
|
1001
1134
|
# Prepend one or more values to a list, creating the list if it doesn't exist
|
1002
1135
|
#
|
1003
1136
|
# @param [String] key
|
1004
|
-
# @param [String, Array] value string value, or array of string values to push
|
1005
|
-
# @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
|
1006
1139
|
def lpush(key, value)
|
1007
1140
|
synchronize do |client|
|
1008
1141
|
client.call([:lpush, key, value])
|
@@ -1013,7 +1146,7 @@ class Redis
|
|
1013
1146
|
#
|
1014
1147
|
# @param [String] key
|
1015
1148
|
# @param [String] value
|
1016
|
-
# @return [
|
1149
|
+
# @return [Integer] the length of the list after the push operation
|
1017
1150
|
def lpushx(key, value)
|
1018
1151
|
synchronize do |client|
|
1019
1152
|
client.call([:lpushx, key, value])
|
@@ -1023,8 +1156,8 @@ class Redis
|
|
1023
1156
|
# Append one or more values to a list, creating the list if it doesn't exist
|
1024
1157
|
#
|
1025
1158
|
# @param [String] key
|
1026
|
-
# @param [String] value
|
1027
|
-
# @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
|
1028
1161
|
def rpush(key, value)
|
1029
1162
|
synchronize do |client|
|
1030
1163
|
client.call([:rpush, key, value])
|
@@ -1035,30 +1168,36 @@ class Redis
|
|
1035
1168
|
#
|
1036
1169
|
# @param [String] key
|
1037
1170
|
# @param [String] value
|
1038
|
-
# @return [
|
1171
|
+
# @return [Integer] the length of the list after the push operation
|
1039
1172
|
def rpushx(key, value)
|
1040
1173
|
synchronize do |client|
|
1041
1174
|
client.call([:rpushx, key, value])
|
1042
1175
|
end
|
1043
1176
|
end
|
1044
1177
|
|
1045
|
-
# Remove and get the first
|
1178
|
+
# Remove and get the first elements in a list.
|
1046
1179
|
#
|
1047
1180
|
# @param [String] key
|
1048
|
-
# @
|
1049
|
-
|
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)
|
1050
1184
|
synchronize do |client|
|
1051
|
-
|
1185
|
+
command = [:lpop, key]
|
1186
|
+
command << count if count
|
1187
|
+
client.call(command)
|
1052
1188
|
end
|
1053
1189
|
end
|
1054
1190
|
|
1055
|
-
# Remove and get the last
|
1191
|
+
# Remove and get the last elements in a list.
|
1056
1192
|
#
|
1057
1193
|
# @param [String] key
|
1058
|
-
# @
|
1059
|
-
|
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)
|
1060
1197
|
synchronize do |client|
|
1061
|
-
|
1198
|
+
command = [:rpop, key]
|
1199
|
+
command << count if count
|
1200
|
+
client.call(command)
|
1062
1201
|
end
|
1063
1202
|
end
|
1064
1203
|
|
@@ -1073,28 +1212,27 @@ class Redis
|
|
1073
1212
|
end
|
1074
1213
|
end
|
1075
1214
|
|
1076
|
-
def _bpop(cmd, args)
|
1077
|
-
|
1078
|
-
|
1079
|
-
case args.last
|
1080
|
-
when Hash
|
1215
|
+
def _bpop(cmd, args, &blk)
|
1216
|
+
timeout = if args.last.is_a?(Hash)
|
1081
1217
|
options = args.pop
|
1082
|
-
|
1218
|
+
options[:timeout]
|
1219
|
+
elsif args.last.respond_to?(:to_int)
|
1083
1220
|
# Issue deprecation notice in obnoxious mode...
|
1084
|
-
|
1221
|
+
args.pop.to_int
|
1085
1222
|
end
|
1086
1223
|
|
1224
|
+
timeout ||= 0
|
1225
|
+
|
1087
1226
|
if args.size > 1
|
1088
1227
|
# Issue deprecation notice in obnoxious mode...
|
1089
1228
|
end
|
1090
1229
|
|
1091
1230
|
keys = args.flatten
|
1092
|
-
timeout = options[:timeout] || 0
|
1093
1231
|
|
1094
1232
|
synchronize do |client|
|
1095
1233
|
command = [cmd, keys, timeout]
|
1096
1234
|
timeout += client.timeout if timeout > 0
|
1097
|
-
client.call_with_timeout(command, timeout)
|
1235
|
+
client.call_with_timeout(command, timeout, &blk)
|
1098
1236
|
end
|
1099
1237
|
end
|
1100
1238
|
|
@@ -1114,7 +1252,7 @@ class Redis
|
|
1114
1252
|
# @param [String, Array<String>] keys one or more keys to perform the
|
1115
1253
|
# blocking pop on
|
1116
1254
|
# @param [Hash] options
|
1117
|
-
# - `:timeout =>
|
1255
|
+
# - `:timeout => Integer`: timeout in seconds, defaults to no timeout
|
1118
1256
|
#
|
1119
1257
|
# @return [nil, [String, String]]
|
1120
1258
|
# - `nil` when the operation timed out
|
@@ -1128,7 +1266,7 @@ class Redis
|
|
1128
1266
|
# @param [String, Array<String>] keys one or more keys to perform the
|
1129
1267
|
# blocking pop on
|
1130
1268
|
# @param [Hash] options
|
1131
|
-
# - `:timeout =>
|
1269
|
+
# - `:timeout => Integer`: timeout in seconds, defaults to no timeout
|
1132
1270
|
#
|
1133
1271
|
# @return [nil, [String, String]]
|
1134
1272
|
# - `nil` when the operation timed out
|
@@ -1145,20 +1283,12 @@ class Redis
|
|
1145
1283
|
# @param [String] source source key
|
1146
1284
|
# @param [String] destination destination key
|
1147
1285
|
# @param [Hash] options
|
1148
|
-
# - `:timeout =>
|
1286
|
+
# - `:timeout => Integer`: timeout in seconds, defaults to no timeout
|
1149
1287
|
#
|
1150
1288
|
# @return [nil, String]
|
1151
1289
|
# - `nil` when the operation timed out
|
1152
1290
|
# - the element was popped and pushed otherwise
|
1153
|
-
def brpoplpush(source, destination,
|
1154
|
-
case options
|
1155
|
-
when Integer
|
1156
|
-
# Issue deprecation notice in obnoxious mode...
|
1157
|
-
options = { :timeout => options }
|
1158
|
-
end
|
1159
|
-
|
1160
|
-
timeout = options[:timeout] || 0
|
1161
|
-
|
1291
|
+
def brpoplpush(source, destination, deprecated_timeout = 0, timeout: deprecated_timeout)
|
1162
1292
|
synchronize do |client|
|
1163
1293
|
command = [:brpoplpush, source, destination, timeout]
|
1164
1294
|
timeout += client.timeout if timeout > 0
|
@@ -1169,7 +1299,7 @@ class Redis
|
|
1169
1299
|
# Get an element from a list by its index.
|
1170
1300
|
#
|
1171
1301
|
# @param [String] key
|
1172
|
-
# @param [
|
1302
|
+
# @param [Integer] index
|
1173
1303
|
# @return [String]
|
1174
1304
|
def lindex(key, index)
|
1175
1305
|
synchronize do |client|
|
@@ -1183,7 +1313,7 @@ class Redis
|
|
1183
1313
|
# @param [String, Symbol] where `BEFORE` or `AFTER`
|
1184
1314
|
# @param [String] pivot reference element
|
1185
1315
|
# @param [String] value
|
1186
|
-
# @return [
|
1316
|
+
# @return [Integer] length of the list after the insert operation, or `-1`
|
1187
1317
|
# when the element `pivot` was not found
|
1188
1318
|
def linsert(key, where, pivot, value)
|
1189
1319
|
synchronize do |client|
|
@@ -1194,8 +1324,8 @@ class Redis
|
|
1194
1324
|
# Get a range of elements from a list.
|
1195
1325
|
#
|
1196
1326
|
# @param [String] key
|
1197
|
-
# @param [
|
1198
|
-
# @param [
|
1327
|
+
# @param [Integer] start start index
|
1328
|
+
# @param [Integer] stop stop index
|
1199
1329
|
# @return [Array<String>]
|
1200
1330
|
def lrange(key, start, stop)
|
1201
1331
|
synchronize do |client|
|
@@ -1206,12 +1336,12 @@ class Redis
|
|
1206
1336
|
# Remove elements from a list.
|
1207
1337
|
#
|
1208
1338
|
# @param [String] key
|
1209
|
-
# @param [
|
1339
|
+
# @param [Integer] count number of elements to remove. Use a positive
|
1210
1340
|
# value to remove the first `count` occurrences of `value`. A negative
|
1211
1341
|
# value to remove the last `count` occurrences of `value`. Or zero, to
|
1212
1342
|
# remove all occurrences of `value` from the list.
|
1213
1343
|
# @param [String] value
|
1214
|
-
# @return [
|
1344
|
+
# @return [Integer] the number of removed elements
|
1215
1345
|
def lrem(key, count, value)
|
1216
1346
|
synchronize do |client|
|
1217
1347
|
client.call([:lrem, key, count, value])
|
@@ -1221,7 +1351,7 @@ class Redis
|
|
1221
1351
|
# Set the value of an element in a list by its index.
|
1222
1352
|
#
|
1223
1353
|
# @param [String] key
|
1224
|
-
# @param [
|
1354
|
+
# @param [Integer] index
|
1225
1355
|
# @param [String] value
|
1226
1356
|
# @return [String] `OK`
|
1227
1357
|
def lset(key, index, value)
|
@@ -1233,8 +1363,8 @@ class Redis
|
|
1233
1363
|
# Trim a list to the specified range.
|
1234
1364
|
#
|
1235
1365
|
# @param [String] key
|
1236
|
-
# @param [
|
1237
|
-
# @param [
|
1366
|
+
# @param [Integer] start start index
|
1367
|
+
# @param [Integer] stop stop index
|
1238
1368
|
# @return [String] `OK`
|
1239
1369
|
def ltrim(key, start, stop)
|
1240
1370
|
synchronize do |client|
|
@@ -1245,7 +1375,7 @@ class Redis
|
|
1245
1375
|
# Get the number of members in a set.
|
1246
1376
|
#
|
1247
1377
|
# @param [String] key
|
1248
|
-
# @return [
|
1378
|
+
# @return [Integer]
|
1249
1379
|
def scard(key)
|
1250
1380
|
synchronize do |client|
|
1251
1381
|
client.call([:scard, key])
|
@@ -1256,8 +1386,8 @@ class Redis
|
|
1256
1386
|
#
|
1257
1387
|
# @param [String] key
|
1258
1388
|
# @param [String, Array<String>] member one member, or array of members
|
1259
|
-
# @return [Boolean,
|
1260
|
-
# 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
|
1261
1391
|
# array of members is specified, holding the number of members that were
|
1262
1392
|
# successfully added
|
1263
1393
|
def sadd(key, member)
|
@@ -1268,7 +1398,7 @@ class Redis
|
|
1268
1398
|
reply
|
1269
1399
|
else
|
1270
1400
|
# Single argument: return boolean
|
1271
|
-
|
1401
|
+
Boolify.call(reply)
|
1272
1402
|
end
|
1273
1403
|
end
|
1274
1404
|
end
|
@@ -1278,8 +1408,8 @@ class Redis
|
|
1278
1408
|
#
|
1279
1409
|
# @param [String] key
|
1280
1410
|
# @param [String, Array<String>] member one member, or array of members
|
1281
|
-
# @return [Boolean,
|
1282
|
-
# 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
|
1283
1413
|
# array of members is specified, holding the number of members that were
|
1284
1414
|
# successfully removed
|
1285
1415
|
def srem(key, member)
|
@@ -1290,26 +1420,31 @@ class Redis
|
|
1290
1420
|
reply
|
1291
1421
|
else
|
1292
1422
|
# Single argument: return boolean
|
1293
|
-
|
1423
|
+
Boolify.call(reply)
|
1294
1424
|
end
|
1295
1425
|
end
|
1296
1426
|
end
|
1297
1427
|
end
|
1298
1428
|
|
1299
|
-
# Remove and return
|
1429
|
+
# Remove and return one or more random member from a set.
|
1300
1430
|
#
|
1301
1431
|
# @param [String] key
|
1302
1432
|
# @return [String]
|
1303
|
-
|
1433
|
+
# @param [Integer] count
|
1434
|
+
def spop(key, count = nil)
|
1304
1435
|
synchronize do |client|
|
1305
|
-
|
1436
|
+
if count.nil?
|
1437
|
+
client.call([:spop, key])
|
1438
|
+
else
|
1439
|
+
client.call([:spop, key, count])
|
1440
|
+
end
|
1306
1441
|
end
|
1307
1442
|
end
|
1308
1443
|
|
1309
1444
|
# Get one or more random members from a set.
|
1310
1445
|
#
|
1311
1446
|
# @param [String] key
|
1312
|
-
# @param [
|
1447
|
+
# @param [Integer] count
|
1313
1448
|
# @return [String]
|
1314
1449
|
def srandmember(key, count = nil)
|
1315
1450
|
synchronize do |client|
|
@@ -1329,7 +1464,7 @@ class Redis
|
|
1329
1464
|
# @return [Boolean]
|
1330
1465
|
def smove(source, destination, member)
|
1331
1466
|
synchronize do |client|
|
1332
|
-
client.call([:smove, source, destination, member], &
|
1467
|
+
client.call([:smove, source, destination, member], &Boolify)
|
1333
1468
|
end
|
1334
1469
|
end
|
1335
1470
|
|
@@ -1340,7 +1475,7 @@ class Redis
|
|
1340
1475
|
# @return [Boolean]
|
1341
1476
|
def sismember(key, member)
|
1342
1477
|
synchronize do |client|
|
1343
|
-
client.call([:sismember, key, member], &
|
1478
|
+
client.call([:sismember, key, member], &Boolify)
|
1344
1479
|
end
|
1345
1480
|
end
|
1346
1481
|
|
@@ -1360,7 +1495,7 @@ class Redis
|
|
1360
1495
|
# @return [Array<String>] members in the difference
|
1361
1496
|
def sdiff(*keys)
|
1362
1497
|
synchronize do |client|
|
1363
|
-
client.call([:sdiff
|
1498
|
+
client.call([:sdiff, *keys])
|
1364
1499
|
end
|
1365
1500
|
end
|
1366
1501
|
|
@@ -1368,10 +1503,10 @@ class Redis
|
|
1368
1503
|
#
|
1369
1504
|
# @param [String] destination destination key
|
1370
1505
|
# @param [String, Array<String>] keys keys pointing to sets to subtract
|
1371
|
-
# @return [
|
1506
|
+
# @return [Integer] number of elements in the resulting set
|
1372
1507
|
def sdiffstore(destination, *keys)
|
1373
1508
|
synchronize do |client|
|
1374
|
-
client.call([:sdiffstore, destination
|
1509
|
+
client.call([:sdiffstore, destination, *keys])
|
1375
1510
|
end
|
1376
1511
|
end
|
1377
1512
|
|
@@ -1381,7 +1516,7 @@ class Redis
|
|
1381
1516
|
# @return [Array<String>] members in the intersection
|
1382
1517
|
def sinter(*keys)
|
1383
1518
|
synchronize do |client|
|
1384
|
-
client.call([:sinter
|
1519
|
+
client.call([:sinter, *keys])
|
1385
1520
|
end
|
1386
1521
|
end
|
1387
1522
|
|
@@ -1389,10 +1524,10 @@ class Redis
|
|
1389
1524
|
#
|
1390
1525
|
# @param [String] destination destination key
|
1391
1526
|
# @param [String, Array<String>] keys keys pointing to sets to intersect
|
1392
|
-
# @return [
|
1527
|
+
# @return [Integer] number of elements in the resulting set
|
1393
1528
|
def sinterstore(destination, *keys)
|
1394
1529
|
synchronize do |client|
|
1395
|
-
client.call([:sinterstore, destination
|
1530
|
+
client.call([:sinterstore, destination, *keys])
|
1396
1531
|
end
|
1397
1532
|
end
|
1398
1533
|
|
@@ -1402,7 +1537,7 @@ class Redis
|
|
1402
1537
|
# @return [Array<String>] members in the union
|
1403
1538
|
def sunion(*keys)
|
1404
1539
|
synchronize do |client|
|
1405
|
-
client.call([:sunion
|
1540
|
+
client.call([:sunion, *keys])
|
1406
1541
|
end
|
1407
1542
|
end
|
1408
1543
|
|
@@ -1410,10 +1545,10 @@ class Redis
|
|
1410
1545
|
#
|
1411
1546
|
# @param [String] destination destination key
|
1412
1547
|
# @param [String, Array<String>] keys keys pointing to sets to unify
|
1413
|
-
# @return [
|
1548
|
+
# @return [Integer] number of elements in the resulting set
|
1414
1549
|
def sunionstore(destination, *keys)
|
1415
1550
|
synchronize do |client|
|
1416
|
-
client.call([:sunionstore, destination
|
1551
|
+
client.call([:sunionstore, destination, *keys])
|
1417
1552
|
end
|
1418
1553
|
end
|
1419
1554
|
|
@@ -1424,7 +1559,7 @@ class Redis
|
|
1424
1559
|
# # => 4
|
1425
1560
|
#
|
1426
1561
|
# @param [String] key
|
1427
|
-
# @return [
|
1562
|
+
# @return [Integer]
|
1428
1563
|
def zcard(key)
|
1429
1564
|
synchronize do |client|
|
1430
1565
|
client.call([:zcard, key])
|
@@ -1455,38 +1590,27 @@ class Redis
|
|
1455
1590
|
# - `:incr => true`: When this option is specified ZADD acts like
|
1456
1591
|
# ZINCRBY; only one score-element pair can be specified in this mode
|
1457
1592
|
#
|
1458
|
-
# @return [Boolean,
|
1593
|
+
# @return [Boolean, Integer, Float]
|
1459
1594
|
# - `Boolean` when a single pair is specified, holding whether or not it was
|
1460
1595
|
# **added** to the sorted set.
|
1461
|
-
# - `
|
1596
|
+
# - `Integer` when an array of pairs is specified, holding the number of
|
1462
1597
|
# pairs that were **added** to the sorted set.
|
1463
1598
|
# - `Float` when option :incr is specified, holding the score of the member
|
1464
1599
|
# after incrementing it.
|
1465
|
-
def zadd(key, *args
|
1466
|
-
|
1467
|
-
if
|
1468
|
-
|
1469
|
-
|
1470
|
-
|
1471
|
-
zadd_options << "NX" if nx
|
1472
|
-
|
1473
|
-
xx = options[:xx]
|
1474
|
-
zadd_options << "XX" if xx
|
1475
|
-
|
1476
|
-
ch = options[:ch]
|
1477
|
-
zadd_options << "CH" if ch
|
1478
|
-
|
1479
|
-
incr = options[:incr]
|
1480
|
-
zadd_options << "INCR" if incr
|
1481
|
-
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
|
1482
1606
|
|
1483
1607
|
synchronize do |client|
|
1484
1608
|
if args.size == 1 && args[0].is_a?(Array)
|
1485
1609
|
# Variadic: return float if INCR, integer if !INCR
|
1486
|
-
client.call(
|
1610
|
+
client.call(command + args[0], &(incr ? Floatify : nil))
|
1487
1611
|
elsif args.size == 2
|
1488
1612
|
# Single pair: return float if INCR, boolean if !INCR
|
1489
|
-
client.call(
|
1613
|
+
client.call(command + args, &(incr ? Floatify : Boolify))
|
1490
1614
|
else
|
1491
1615
|
raise ArgumentError, "wrong number of arguments"
|
1492
1616
|
end
|
@@ -1505,7 +1629,7 @@ class Redis
|
|
1505
1629
|
# @return [Float] score of the member after incrementing it
|
1506
1630
|
def zincrby(key, increment, member)
|
1507
1631
|
synchronize do |client|
|
1508
|
-
client.call([:zincrby, key, increment, member], &
|
1632
|
+
client.call([:zincrby, key, increment, member], &Floatify)
|
1509
1633
|
end
|
1510
1634
|
end
|
1511
1635
|
|
@@ -1521,10 +1645,10 @@ class Redis
|
|
1521
1645
|
# - a single member
|
1522
1646
|
# - an array of members
|
1523
1647
|
#
|
1524
|
-
# @return [Boolean,
|
1648
|
+
# @return [Boolean, Integer]
|
1525
1649
|
# - `Boolean` when a single member is specified, holding whether or not it
|
1526
1650
|
# was removed from the sorted set
|
1527
|
-
# - `
|
1651
|
+
# - `Integer` when an array of pairs is specified, holding the number of
|
1528
1652
|
# members that were removed to the sorted set
|
1529
1653
|
def zrem(key, member)
|
1530
1654
|
synchronize do |client|
|
@@ -1534,12 +1658,96 @@ class Redis
|
|
1534
1658
|
reply
|
1535
1659
|
else
|
1536
1660
|
# Single argument: return boolean
|
1537
|
-
|
1661
|
+
Boolify.call(reply)
|
1538
1662
|
end
|
1539
1663
|
end
|
1540
1664
|
end
|
1541
1665
|
end
|
1542
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
|
+
|
1543
1751
|
# Get the score associated with the given member in a sorted set.
|
1544
1752
|
#
|
1545
1753
|
# @example Get the score for member "a"
|
@@ -1551,7 +1759,7 @@ class Redis
|
|
1551
1759
|
# @return [Float] score of the member
|
1552
1760
|
def zscore(key, member)
|
1553
1761
|
synchronize do |client|
|
1554
|
-
client.call([:zscore, key, member], &
|
1762
|
+
client.call([:zscore, key, member], &Floatify)
|
1555
1763
|
end
|
1556
1764
|
end
|
1557
1765
|
|
@@ -1565,26 +1773,24 @@ class Redis
|
|
1565
1773
|
# # => [["a", 32.0], ["b", 64.0]]
|
1566
1774
|
#
|
1567
1775
|
# @param [String] key
|
1568
|
-
# @param [
|
1569
|
-
# @param [
|
1776
|
+
# @param [Integer] start start index
|
1777
|
+
# @param [Integer] stop stop index
|
1570
1778
|
# @param [Hash] options
|
1571
1779
|
# - `:with_scores => true`: include scores in output
|
1572
1780
|
#
|
1573
1781
|
# @return [Array<String>, Array<[String, Float]>]
|
1574
1782
|
# - when `:with_scores` is not specified, an array of members
|
1575
1783
|
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
1576
|
-
def zrange(key, start, stop,
|
1577
|
-
args = []
|
1578
|
-
|
1579
|
-
with_scores = options[:with_scores] || options[:withscores]
|
1784
|
+
def zrange(key, start, stop, withscores: false, with_scores: withscores)
|
1785
|
+
args = [:zrange, key, start, stop]
|
1580
1786
|
|
1581
1787
|
if with_scores
|
1582
1788
|
args << "WITHSCORES"
|
1583
|
-
block =
|
1789
|
+
block = FloatifyPairs
|
1584
1790
|
end
|
1585
1791
|
|
1586
1792
|
synchronize do |client|
|
1587
|
-
client.call(
|
1793
|
+
client.call(args, &block)
|
1588
1794
|
end
|
1589
1795
|
end
|
1590
1796
|
|
@@ -1599,18 +1805,16 @@ class Redis
|
|
1599
1805
|
# # => [["b", 64.0], ["a", 32.0]]
|
1600
1806
|
#
|
1601
1807
|
# @see #zrange
|
1602
|
-
def zrevrange(key, start, stop,
|
1603
|
-
args = []
|
1604
|
-
|
1605
|
-
with_scores = options[:with_scores] || options[:withscores]
|
1808
|
+
def zrevrange(key, start, stop, withscores: false, with_scores: withscores)
|
1809
|
+
args = [:zrevrange, key, start, stop]
|
1606
1810
|
|
1607
1811
|
if with_scores
|
1608
1812
|
args << "WITHSCORES"
|
1609
|
-
block =
|
1813
|
+
block = FloatifyPairs
|
1610
1814
|
end
|
1611
1815
|
|
1612
1816
|
synchronize do |client|
|
1613
|
-
client.call(
|
1817
|
+
client.call(args, &block)
|
1614
1818
|
end
|
1615
1819
|
end
|
1616
1820
|
|
@@ -1618,7 +1822,7 @@ class Redis
|
|
1618
1822
|
#
|
1619
1823
|
# @param [String] key
|
1620
1824
|
# @param [String] member
|
1621
|
-
# @return [
|
1825
|
+
# @return [Integer]
|
1622
1826
|
def zrank(key, member)
|
1623
1827
|
synchronize do |client|
|
1624
1828
|
client.call([:zrank, key, member])
|
@@ -1630,7 +1834,7 @@ class Redis
|
|
1630
1834
|
#
|
1631
1835
|
# @param [String] key
|
1632
1836
|
# @param [String] member
|
1633
|
-
# @return [
|
1837
|
+
# @return [Integer]
|
1634
1838
|
def zrevrank(key, member)
|
1635
1839
|
synchronize do |client|
|
1636
1840
|
client.call([:zrevrank, key, member])
|
@@ -1647,15 +1851,39 @@ class Redis
|
|
1647
1851
|
# # => 5
|
1648
1852
|
#
|
1649
1853
|
# @param [String] key
|
1650
|
-
# @param [
|
1651
|
-
# @param [
|
1652
|
-
# @return [
|
1854
|
+
# @param [Integer] start start index
|
1855
|
+
# @param [Integer] stop stop index
|
1856
|
+
# @return [Integer] number of members that were removed
|
1653
1857
|
def zremrangebyrank(key, start, stop)
|
1654
1858
|
synchronize do |client|
|
1655
1859
|
client.call([:zremrangebyrank, key, start, stop])
|
1656
1860
|
end
|
1657
1861
|
end
|
1658
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
|
+
|
1659
1887
|
# Return a range of members with the same score in a sorted set, by lexicographical ordering
|
1660
1888
|
#
|
1661
1889
|
# @example Retrieve members matching a
|
@@ -1677,14 +1905,16 @@ class Redis
|
|
1677
1905
|
# `count` members
|
1678
1906
|
#
|
1679
1907
|
# @return [Array<String>, Array<[String, Float]>]
|
1680
|
-
def zrangebylex(key, min, max,
|
1681
|
-
args = []
|
1908
|
+
def zrangebylex(key, min, max, limit: nil)
|
1909
|
+
args = [:zrangebylex, key, min, max]
|
1682
1910
|
|
1683
|
-
|
1684
|
-
|
1911
|
+
if limit
|
1912
|
+
args << "LIMIT"
|
1913
|
+
args.concat(limit)
|
1914
|
+
end
|
1685
1915
|
|
1686
1916
|
synchronize do |client|
|
1687
|
-
client.call(
|
1917
|
+
client.call(args)
|
1688
1918
|
end
|
1689
1919
|
end
|
1690
1920
|
|
@@ -1699,14 +1929,16 @@ class Redis
|
|
1699
1929
|
# # => ["abbygail", "abby"]
|
1700
1930
|
#
|
1701
1931
|
# @see #zrangebylex
|
1702
|
-
def zrevrangebylex(key, max, min,
|
1703
|
-
args = []
|
1932
|
+
def zrevrangebylex(key, max, min, limit: nil)
|
1933
|
+
args = [:zrevrangebylex, key, max, min]
|
1704
1934
|
|
1705
|
-
|
1706
|
-
|
1935
|
+
if limit
|
1936
|
+
args << "LIMIT"
|
1937
|
+
args.concat(limit)
|
1938
|
+
end
|
1707
1939
|
|
1708
1940
|
synchronize do |client|
|
1709
|
-
client.call(
|
1941
|
+
client.call(args)
|
1710
1942
|
end
|
1711
1943
|
end
|
1712
1944
|
|
@@ -1737,21 +1969,21 @@ class Redis
|
|
1737
1969
|
# @return [Array<String>, Array<[String, Float]>]
|
1738
1970
|
# - when `:with_scores` is not specified, an array of members
|
1739
1971
|
# - when `:with_scores` is specified, an array with `[member, score]` pairs
|
1740
|
-
def zrangebyscore(key, min, max,
|
1741
|
-
args = []
|
1742
|
-
|
1743
|
-
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]
|
1744
1974
|
|
1745
1975
|
if with_scores
|
1746
1976
|
args << "WITHSCORES"
|
1747
|
-
block =
|
1977
|
+
block = FloatifyPairs
|
1748
1978
|
end
|
1749
1979
|
|
1750
|
-
|
1751
|
-
|
1980
|
+
if limit
|
1981
|
+
args << "LIMIT"
|
1982
|
+
args.concat(limit)
|
1983
|
+
end
|
1752
1984
|
|
1753
1985
|
synchronize do |client|
|
1754
|
-
client.call(
|
1986
|
+
client.call(args, &block)
|
1755
1987
|
end
|
1756
1988
|
end
|
1757
1989
|
|
@@ -1769,21 +2001,21 @@ class Redis
|
|
1769
2001
|
# # => [["b", 64.0], ["a", 32.0]]
|
1770
2002
|
#
|
1771
2003
|
# @see #zrangebyscore
|
1772
|
-
def zrevrangebyscore(key, max, min,
|
1773
|
-
args = []
|
1774
|
-
|
1775
|
-
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]
|
1776
2006
|
|
1777
2007
|
if with_scores
|
1778
|
-
args <<
|
1779
|
-
block =
|
2008
|
+
args << "WITHSCORES"
|
2009
|
+
block = FloatifyPairs
|
1780
2010
|
end
|
1781
2011
|
|
1782
|
-
|
1783
|
-
|
2012
|
+
if limit
|
2013
|
+
args << "LIMIT"
|
2014
|
+
args.concat(limit)
|
2015
|
+
end
|
1784
2016
|
|
1785
2017
|
synchronize do |client|
|
1786
|
-
client.call(
|
2018
|
+
client.call(args, &block)
|
1787
2019
|
end
|
1788
2020
|
end
|
1789
2021
|
|
@@ -1803,7 +2035,7 @@ class Redis
|
|
1803
2035
|
# @param [String] max
|
1804
2036
|
# - inclusive maximum score is specified verbatim
|
1805
2037
|
# - exclusive maximum score is specified by prefixing `(`
|
1806
|
-
# @return [
|
2038
|
+
# @return [Integer] number of members that were removed
|
1807
2039
|
def zremrangebyscore(key, min, max)
|
1808
2040
|
synchronize do |client|
|
1809
2041
|
client.call([:zremrangebyscore, key, min, max])
|
@@ -1826,13 +2058,52 @@ class Redis
|
|
1826
2058
|
# @param [String] max
|
1827
2059
|
# - inclusive maximum score is specified verbatim
|
1828
2060
|
# - exclusive maximum score is specified by prefixing `(`
|
1829
|
-
# @return [
|
2061
|
+
# @return [Integer] number of members in within the specified range
|
1830
2062
|
def zcount(key, min, max)
|
1831
2063
|
synchronize do |client|
|
1832
2064
|
client.call([:zcount, key, min, max])
|
1833
2065
|
end
|
1834
2066
|
end
|
1835
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
|
+
|
1836
2107
|
# Intersect multiple sorted sets and store the resulting sorted set in a new
|
1837
2108
|
# key.
|
1838
2109
|
#
|
@@ -1846,18 +2117,19 @@ class Redis
|
|
1846
2117
|
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
1847
2118
|
# sorted sets
|
1848
2119
|
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
1849
|
-
# @return [
|
1850
|
-
def zinterstore(destination, keys,
|
1851
|
-
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]
|
1852
2123
|
|
1853
|
-
|
1854
|
-
|
2124
|
+
if weights
|
2125
|
+
args << "WEIGHTS"
|
2126
|
+
args.concat(weights)
|
2127
|
+
end
|
1855
2128
|
|
1856
|
-
aggregate
|
1857
|
-
args.concat(["AGGREGATE", aggregate]) if aggregate
|
2129
|
+
args << "AGGREGATE" << aggregate if aggregate
|
1858
2130
|
|
1859
2131
|
synchronize do |client|
|
1860
|
-
client.call(
|
2132
|
+
client.call(args)
|
1861
2133
|
end
|
1862
2134
|
end
|
1863
2135
|
|
@@ -1873,40 +2145,46 @@ class Redis
|
|
1873
2145
|
# - `:weights => [Float, Float, ...]`: weights to associate with source
|
1874
2146
|
# sorted sets
|
1875
2147
|
# - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
|
1876
|
-
# @return [
|
1877
|
-
def zunionstore(destination, keys,
|
1878
|
-
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]
|
1879
2151
|
|
1880
|
-
|
1881
|
-
|
2152
|
+
if weights
|
2153
|
+
args << "WEIGHTS"
|
2154
|
+
args.concat(weights)
|
2155
|
+
end
|
1882
2156
|
|
1883
|
-
aggregate
|
1884
|
-
args.concat(["AGGREGATE", aggregate]) if aggregate
|
2157
|
+
args << "AGGREGATE" << aggregate if aggregate
|
1885
2158
|
|
1886
2159
|
synchronize do |client|
|
1887
|
-
client.call(
|
2160
|
+
client.call(args)
|
1888
2161
|
end
|
1889
2162
|
end
|
1890
2163
|
|
1891
2164
|
# Get the number of fields in a hash.
|
1892
2165
|
#
|
1893
2166
|
# @param [String] key
|
1894
|
-
# @return [
|
2167
|
+
# @return [Integer] number of fields in the hash
|
1895
2168
|
def hlen(key)
|
1896
2169
|
synchronize do |client|
|
1897
2170
|
client.call([:hlen, key])
|
1898
2171
|
end
|
1899
2172
|
end
|
1900
2173
|
|
1901
|
-
# 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
|
1902
2179
|
#
|
1903
2180
|
# @param [String] key
|
1904
|
-
# @param [String]
|
1905
|
-
# @
|
1906
|
-
|
1907
|
-
|
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
|
+
|
1908
2186
|
synchronize do |client|
|
1909
|
-
client.call([:hset, key,
|
2187
|
+
client.call([:hset, key, *attrs])
|
1910
2188
|
end
|
1911
2189
|
end
|
1912
2190
|
|
@@ -1918,7 +2196,7 @@ class Redis
|
|
1918
2196
|
# @return [Boolean] whether or not the field was **added** to the hash
|
1919
2197
|
def hsetnx(key, field, value)
|
1920
2198
|
synchronize do |client|
|
1921
|
-
client.call([:hsetnx, key, field, value], &
|
2199
|
+
client.call([:hsetnx, key, field, value], &Boolify)
|
1922
2200
|
end
|
1923
2201
|
end
|
1924
2202
|
|
@@ -1995,7 +2273,7 @@ class Redis
|
|
1995
2273
|
# @see #hmget
|
1996
2274
|
def mapped_hmget(key, *fields)
|
1997
2275
|
hmget(key, *fields) do |reply|
|
1998
|
-
if reply.
|
2276
|
+
if reply.is_a?(Array)
|
1999
2277
|
Hash[fields.zip(reply)]
|
2000
2278
|
else
|
2001
2279
|
reply
|
@@ -2007,10 +2285,10 @@ class Redis
|
|
2007
2285
|
#
|
2008
2286
|
# @param [String] key
|
2009
2287
|
# @param [String, Array<String>] field
|
2010
|
-
# @return [
|
2011
|
-
def hdel(key,
|
2288
|
+
# @return [Integer] the number of fields that were removed from the hash
|
2289
|
+
def hdel(key, *fields)
|
2012
2290
|
synchronize do |client|
|
2013
|
-
client.call([:hdel, key,
|
2291
|
+
client.call([:hdel, key, *fields])
|
2014
2292
|
end
|
2015
2293
|
end
|
2016
2294
|
|
@@ -2021,7 +2299,7 @@ class Redis
|
|
2021
2299
|
# @return [Boolean] whether or not the field exists in the hash
|
2022
2300
|
def hexists(key, field)
|
2023
2301
|
synchronize do |client|
|
2024
|
-
client.call([:hexists, key, field], &
|
2302
|
+
client.call([:hexists, key, field], &Boolify)
|
2025
2303
|
end
|
2026
2304
|
end
|
2027
2305
|
|
@@ -2029,8 +2307,8 @@ class Redis
|
|
2029
2307
|
#
|
2030
2308
|
# @param [String] key
|
2031
2309
|
# @param [String] field
|
2032
|
-
# @param [
|
2033
|
-
# @return [
|
2310
|
+
# @param [Integer] increment
|
2311
|
+
# @return [Integer] value of the field after incrementing it
|
2034
2312
|
def hincrby(key, field, increment)
|
2035
2313
|
synchronize do |client|
|
2036
2314
|
client.call([:hincrby, key, field, increment])
|
@@ -2045,7 +2323,7 @@ class Redis
|
|
2045
2323
|
# @return [Float] value of the field after incrementing it
|
2046
2324
|
def hincrbyfloat(key, field, increment)
|
2047
2325
|
synchronize do |client|
|
2048
|
-
client.call([:hincrbyfloat, key, field, increment], &
|
2326
|
+
client.call([:hincrbyfloat, key, field, increment], &Floatify)
|
2049
2327
|
end
|
2050
2328
|
end
|
2051
2329
|
|
@@ -2075,7 +2353,7 @@ class Redis
|
|
2075
2353
|
# @return [Hash<String, String>]
|
2076
2354
|
def hgetall(key)
|
2077
2355
|
synchronize do |client|
|
2078
|
-
client.call([:hgetall, key], &
|
2356
|
+
client.call([:hgetall, key], &Hashify)
|
2079
2357
|
end
|
2080
2358
|
end
|
2081
2359
|
|
@@ -2088,36 +2366,54 @@ class Redis
|
|
2088
2366
|
|
2089
2367
|
def subscribed?
|
2090
2368
|
synchronize do |client|
|
2091
|
-
client.
|
2369
|
+
client.is_a? SubscribedClient
|
2092
2370
|
end
|
2093
2371
|
end
|
2094
2372
|
|
2095
2373
|
# Listen for messages published to the given channels.
|
2096
2374
|
def subscribe(*channels, &block)
|
2097
|
-
synchronize do |
|
2098
|
-
_subscription(:subscribe, channels, block)
|
2375
|
+
synchronize do |_client|
|
2376
|
+
_subscription(:subscribe, 0, channels, block)
|
2377
|
+
end
|
2378
|
+
end
|
2379
|
+
|
2380
|
+
# Listen for messages published to the given channels. Throw a timeout error
|
2381
|
+
# if there is no messages for a timeout period.
|
2382
|
+
def subscribe_with_timeout(timeout, *channels, &block)
|
2383
|
+
synchronize do |_client|
|
2384
|
+
_subscription(:subscribe_with_timeout, timeout, channels, block)
|
2099
2385
|
end
|
2100
2386
|
end
|
2101
2387
|
|
2102
2388
|
# Stop listening for messages posted to the given channels.
|
2103
2389
|
def unsubscribe(*channels)
|
2104
2390
|
synchronize do |client|
|
2105
|
-
raise
|
2391
|
+
raise "Can't unsubscribe if not subscribed." unless subscribed?
|
2392
|
+
|
2106
2393
|
client.unsubscribe(*channels)
|
2107
2394
|
end
|
2108
2395
|
end
|
2109
2396
|
|
2110
2397
|
# Listen for messages published to channels matching the given patterns.
|
2111
2398
|
def psubscribe(*channels, &block)
|
2112
|
-
synchronize do |
|
2113
|
-
_subscription(:psubscribe, channels, block)
|
2399
|
+
synchronize do |_client|
|
2400
|
+
_subscription(:psubscribe, 0, channels, block)
|
2401
|
+
end
|
2402
|
+
end
|
2403
|
+
|
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.
|
2406
|
+
def psubscribe_with_timeout(timeout, *channels, &block)
|
2407
|
+
synchronize do |_client|
|
2408
|
+
_subscription(:psubscribe_with_timeout, timeout, channels, block)
|
2114
2409
|
end
|
2115
2410
|
end
|
2116
2411
|
|
2117
2412
|
# Stop listening for messages posted to channels matching the given patterns.
|
2118
2413
|
def punsubscribe(*channels)
|
2119
2414
|
synchronize do |client|
|
2120
|
-
raise
|
2415
|
+
raise "Can't unsubscribe if not subscribed." unless subscribed?
|
2416
|
+
|
2121
2417
|
client.punsubscribe(*channels)
|
2122
2418
|
end
|
2123
2419
|
end
|
@@ -2162,7 +2458,7 @@ class Redis
|
|
2162
2458
|
# @see #multi
|
2163
2459
|
def watch(*keys)
|
2164
2460
|
synchronize do |client|
|
2165
|
-
res = client.call([:watch
|
2461
|
+
res = client.call([:watch, *keys])
|
2166
2462
|
|
2167
2463
|
if block_given?
|
2168
2464
|
begin
|
@@ -2192,13 +2488,13 @@ class Redis
|
|
2192
2488
|
end
|
2193
2489
|
|
2194
2490
|
def pipelined
|
2195
|
-
synchronize do |
|
2491
|
+
synchronize do |prior_client|
|
2196
2492
|
begin
|
2197
|
-
|
2493
|
+
@client = Pipeline.new(prior_client)
|
2198
2494
|
yield(self)
|
2199
|
-
|
2495
|
+
prior_client.call_pipeline(@client)
|
2200
2496
|
ensure
|
2201
|
-
@client =
|
2497
|
+
@client = prior_client
|
2202
2498
|
end
|
2203
2499
|
end
|
2204
2500
|
end
|
@@ -2234,17 +2530,16 @@ class Redis
|
|
2234
2530
|
# @see #watch
|
2235
2531
|
# @see #unwatch
|
2236
2532
|
def multi
|
2237
|
-
synchronize do |
|
2533
|
+
synchronize do |prior_client|
|
2238
2534
|
if !block_given?
|
2239
|
-
|
2535
|
+
prior_client.call([:multi])
|
2240
2536
|
else
|
2241
2537
|
begin
|
2242
|
-
|
2243
|
-
original, @client = @client, pipeline
|
2538
|
+
@client = Pipeline::Multi.new(prior_client)
|
2244
2539
|
yield(self)
|
2245
|
-
|
2540
|
+
prior_client.call_pipeline(@client)
|
2246
2541
|
ensure
|
2247
|
-
@client =
|
2542
|
+
@client = prior_client
|
2248
2543
|
end
|
2249
2544
|
end
|
2250
2545
|
end
|
@@ -2312,7 +2607,7 @@ class Redis
|
|
2312
2607
|
arg = args.first
|
2313
2608
|
|
2314
2609
|
client.call([:script, :exists, arg]) do |reply|
|
2315
|
-
reply = reply.map { |r|
|
2610
|
+
reply = reply.map { |r| Boolify.call(r) }
|
2316
2611
|
|
2317
2612
|
if arg.is_a?(Array)
|
2318
2613
|
reply
|
@@ -2391,18 +2686,13 @@ class Redis
|
|
2391
2686
|
_eval(:evalsha, args)
|
2392
2687
|
end
|
2393
2688
|
|
2394
|
-
def _scan(command, cursor, args,
|
2689
|
+
def _scan(command, cursor, args, match: nil, count: nil, type: nil, &block)
|
2395
2690
|
# SSCAN/ZSCAN/HSCAN already prepend the key to +args+.
|
2396
2691
|
|
2397
2692
|
args << cursor
|
2398
|
-
|
2399
|
-
|
2400
|
-
|
2401
|
-
end
|
2402
|
-
|
2403
|
-
if count = options[:count]
|
2404
|
-
args.concat(["COUNT", count])
|
2405
|
-
end
|
2693
|
+
args << "MATCH" << match if match
|
2694
|
+
args << "COUNT" << count if count
|
2695
|
+
args << "TYPE" << type if type
|
2406
2696
|
|
2407
2697
|
synchronize do |client|
|
2408
2698
|
client.call([command] + args, &block)
|
@@ -2417,15 +2707,19 @@ class Redis
|
|
2417
2707
|
# @example Retrieve a batch of keys matching a pattern
|
2418
2708
|
# redis.scan(4, :match => "key:1?")
|
2419
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"]]
|
2420
2713
|
#
|
2421
2714
|
# @param [String, Integer] cursor the cursor of the iteration
|
2422
2715
|
# @param [Hash] options
|
2423
2716
|
# - `:match => String`: only return keys matching the pattern
|
2424
2717
|
# - `:count => Integer`: return count keys at most per iteration
|
2718
|
+
# - `:type => String`: return keys only of the given type
|
2425
2719
|
#
|
2426
2720
|
# @return [String, Array<String>] the next cursor and all found keys
|
2427
|
-
def scan(cursor, options
|
2428
|
-
_scan(:scan, cursor, [], options)
|
2721
|
+
def scan(cursor, **options)
|
2722
|
+
_scan(:scan, cursor, [], **options)
|
2429
2723
|
end
|
2430
2724
|
|
2431
2725
|
# Scan the keyspace
|
@@ -2437,17 +2731,23 @@ class Redis
|
|
2437
2731
|
# redis.scan_each(:match => "key:1?") {|key| puts key}
|
2438
2732
|
# # => key:13
|
2439
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"
|
2440
2738
|
#
|
2441
2739
|
# @param [Hash] options
|
2442
2740
|
# - `:match => String`: only return keys matching the pattern
|
2443
2741
|
# - `:count => Integer`: return count keys at most per iteration
|
2742
|
+
# - `:type => String`: return keys only of the given type
|
2444
2743
|
#
|
2445
2744
|
# @return [Enumerator] an enumerator for all found keys
|
2446
|
-
def scan_each(options
|
2447
|
-
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
|
+
|
2448
2748
|
cursor = 0
|
2449
2749
|
loop do
|
2450
|
-
cursor, keys = scan(cursor, options)
|
2750
|
+
cursor, keys = scan(cursor, **options)
|
2451
2751
|
keys.each(&block)
|
2452
2752
|
break if cursor == "0"
|
2453
2753
|
end
|
@@ -2464,9 +2764,9 @@ class Redis
|
|
2464
2764
|
# - `:count => Integer`: return count keys at most per iteration
|
2465
2765
|
#
|
2466
2766
|
# @return [String, Array<[String, String]>] the next cursor and all found keys
|
2467
|
-
def hscan(key, cursor, options
|
2468
|
-
_scan(:hscan, cursor, [key], options) do |reply|
|
2469
|
-
[reply[0],
|
2767
|
+
def hscan(key, cursor, **options)
|
2768
|
+
_scan(:hscan, cursor, [key], **options) do |reply|
|
2769
|
+
[reply[0], reply[1].each_slice(2).to_a]
|
2470
2770
|
end
|
2471
2771
|
end
|
2472
2772
|
|
@@ -2481,11 +2781,12 @@ class Redis
|
|
2481
2781
|
# - `:count => Integer`: return count keys at most per iteration
|
2482
2782
|
#
|
2483
2783
|
# @return [Enumerator] an enumerator for all found keys
|
2484
|
-
def hscan_each(key, options
|
2485
|
-
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
|
+
|
2486
2787
|
cursor = 0
|
2487
2788
|
loop do
|
2488
|
-
cursor, values = hscan(key, cursor, options)
|
2789
|
+
cursor, values = hscan(key, cursor, **options)
|
2489
2790
|
values.each(&block)
|
2490
2791
|
break if cursor == "0"
|
2491
2792
|
end
|
@@ -2503,9 +2804,9 @@ class Redis
|
|
2503
2804
|
#
|
2504
2805
|
# @return [String, Array<[String, Float]>] the next cursor and all found
|
2505
2806
|
# members and scores
|
2506
|
-
def zscan(key, cursor, options
|
2507
|
-
_scan(:zscan, cursor, [key], options) do |reply|
|
2508
|
-
[reply[0],
|
2807
|
+
def zscan(key, cursor, **options)
|
2808
|
+
_scan(:zscan, cursor, [key], **options) do |reply|
|
2809
|
+
[reply[0], FloatifyPairs.call(reply[1])]
|
2509
2810
|
end
|
2510
2811
|
end
|
2511
2812
|
|
@@ -2520,11 +2821,12 @@ class Redis
|
|
2520
2821
|
# - `:count => Integer`: return count keys at most per iteration
|
2521
2822
|
#
|
2522
2823
|
# @return [Enumerator] an enumerator for all found scores and members
|
2523
|
-
def zscan_each(key, options
|
2524
|
-
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
|
+
|
2525
2827
|
cursor = 0
|
2526
2828
|
loop do
|
2527
|
-
cursor, values = zscan(key, cursor, options)
|
2829
|
+
cursor, values = zscan(key, cursor, **options)
|
2528
2830
|
values.each(&block)
|
2529
2831
|
break if cursor == "0"
|
2530
2832
|
end
|
@@ -2541,8 +2843,8 @@ class Redis
|
|
2541
2843
|
# - `:count => Integer`: return count keys at most per iteration
|
2542
2844
|
#
|
2543
2845
|
# @return [String, Array<String>] the next cursor and all found members
|
2544
|
-
def sscan(key, cursor, options
|
2545
|
-
_scan(:sscan, cursor, [key], options)
|
2846
|
+
def sscan(key, cursor, **options)
|
2847
|
+
_scan(:sscan, cursor, [key], **options)
|
2546
2848
|
end
|
2547
2849
|
|
2548
2850
|
# Scan a set
|
@@ -2556,11 +2858,12 @@ class Redis
|
|
2556
2858
|
# - `:count => Integer`: return count keys at most per iteration
|
2557
2859
|
#
|
2558
2860
|
# @return [Enumerator] an enumerator for all keys in the set
|
2559
|
-
def sscan_each(key, options
|
2560
|
-
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
|
+
|
2561
2864
|
cursor = 0
|
2562
2865
|
loop do
|
2563
|
-
cursor, keys = sscan(key, cursor, options)
|
2866
|
+
cursor, keys = sscan(key, cursor, **options)
|
2564
2867
|
keys.each(&block)
|
2565
2868
|
break if cursor == "0"
|
2566
2869
|
end
|
@@ -2573,7 +2876,7 @@ class Redis
|
|
2573
2876
|
# @return [Boolean] true if at least 1 HyperLogLog internal register was altered. false otherwise.
|
2574
2877
|
def pfadd(key, member)
|
2575
2878
|
synchronize do |client|
|
2576
|
-
client.call([:pfadd, key, member], &
|
2879
|
+
client.call([:pfadd, key, member], &Boolify)
|
2577
2880
|
end
|
2578
2881
|
end
|
2579
2882
|
|
@@ -2583,7 +2886,7 @@ class Redis
|
|
2583
2886
|
# union of the HyperLogLogs contained in the keys.
|
2584
2887
|
#
|
2585
2888
|
# @param [String, Array<String>] keys
|
2586
|
-
# @return [
|
2889
|
+
# @return [Integer]
|
2587
2890
|
def pfcount(*keys)
|
2588
2891
|
synchronize do |client|
|
2589
2892
|
client.call([:pfcount] + keys)
|
@@ -2598,10 +2901,449 @@ class Redis
|
|
2598
2901
|
# @return [Boolean]
|
2599
2902
|
def pfmerge(dest_key, *source_key)
|
2600
2903
|
synchronize do |client|
|
2601
|
-
client.call([:pfmerge, dest_key, *source_key], &
|
2904
|
+
client.call([:pfmerge, dest_key, *source_key], &BoolifySet)
|
2602
2905
|
end
|
2603
2906
|
end
|
2604
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
|
+
|
2605
3347
|
# Interact with the sentinel command (masters, master, slaves, failover)
|
2606
3348
|
#
|
2607
3349
|
# @param [String] subcommand e.g. `masters`, `master`, `slaves`
|
@@ -2615,11 +3357,11 @@ class Redis
|
|
2615
3357
|
when "get-master-addr-by-name"
|
2616
3358
|
reply
|
2617
3359
|
else
|
2618
|
-
if reply.
|
2619
|
-
if reply[0].
|
2620
|
-
reply.map(&
|
3360
|
+
if reply.is_a?(Array)
|
3361
|
+
if reply[0].is_a?(Array)
|
3362
|
+
reply.map(&Hashify)
|
2621
3363
|
else
|
2622
|
-
|
3364
|
+
Hashify.call(reply)
|
2623
3365
|
end
|
2624
3366
|
else
|
2625
3367
|
reply
|
@@ -2629,6 +3371,46 @@ class Redis
|
|
2629
3371
|
end
|
2630
3372
|
end
|
2631
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
|
+
|
2632
3414
|
def id
|
2633
3415
|
@original_client.id
|
2634
3416
|
end
|
@@ -2641,90 +3423,223 @@ class Redis
|
|
2641
3423
|
self.class.new(@options)
|
2642
3424
|
end
|
2643
3425
|
|
2644
|
-
def
|
3426
|
+
def connection
|
3427
|
+
return @original_client.connection_info if @cluster_mode
|
3428
|
+
|
3429
|
+
{
|
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
|
3435
|
+
}
|
3436
|
+
end
|
3437
|
+
|
3438
|
+
def method_missing(command, *args) # rubocop:disable Style/MissingRespondToMissing
|
2645
3439
|
synchronize do |client|
|
2646
3440
|
client.call([command] + args)
|
2647
3441
|
end
|
2648
3442
|
end
|
2649
3443
|
|
2650
|
-
private
|
3444
|
+
private
|
2651
3445
|
|
2652
3446
|
# Commands returning 1 for true and 0 for false may be executed in a pipeline
|
2653
3447
|
# where the method call will return nil. Propagate the nil instead of falsely
|
2654
3448
|
# returning false.
|
2655
|
-
|
2656
|
-
|
2657
|
-
|
2658
|
-
|
2659
|
-
|
3449
|
+
Boolify = lambda { |value|
|
3450
|
+
case value
|
3451
|
+
when 1
|
3452
|
+
true
|
3453
|
+
when 0
|
3454
|
+
false
|
3455
|
+
else
|
3456
|
+
value
|
3457
|
+
end
|
3458
|
+
}
|
2660
3459
|
|
2661
|
-
|
2662
|
-
|
2663
|
-
|
2664
|
-
|
2665
|
-
|
2666
|
-
|
2667
|
-
|
2668
|
-
|
2669
|
-
|
3460
|
+
BoolifySet = lambda { |value|
|
3461
|
+
case value
|
3462
|
+
when "OK"
|
3463
|
+
true
|
3464
|
+
when nil
|
3465
|
+
false
|
3466
|
+
else
|
3467
|
+
value
|
3468
|
+
end
|
3469
|
+
}
|
2670
3470
|
|
2671
|
-
|
2672
|
-
|
2673
|
-
|
2674
|
-
|
2675
|
-
|
2676
|
-
|
2677
|
-
|
2678
|
-
}
|
2679
|
-
end
|
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
|
+
}
|
2680
3478
|
|
2681
|
-
|
2682
|
-
|
2683
|
-
|
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
|
+
}
|
2684
3491
|
|
2685
|
-
|
2686
|
-
|
2687
|
-
else
|
2688
|
-
Float(str)
|
2689
|
-
end
|
2690
|
-
}
|
2691
|
-
end
|
3492
|
+
FloatifyPairs = lambda { |value|
|
3493
|
+
return value unless value.respond_to?(:each_slice)
|
2692
3494
|
|
2693
|
-
|
2694
|
-
|
2695
|
-
|
3495
|
+
value.each_slice(2).map do |member, score|
|
3496
|
+
[member, Floatify.call(score)]
|
3497
|
+
end
|
3498
|
+
}
|
2696
3499
|
|
2697
|
-
|
2698
|
-
|
2699
|
-
|
2700
|
-
|
2701
|
-
|
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
|
+
}
|
2702
3506
|
|
2703
|
-
|
2704
|
-
|
2705
|
-
|
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
|
+
}
|
2706
3515
|
|
2707
|
-
|
2708
|
-
|
2709
|
-
|
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] }
|
3529
|
+
}
|
3530
|
+
}
|
3531
|
+
|
3532
|
+
HashifyStreamAutoclaimJustId = lambda { |reply|
|
3533
|
+
{
|
3534
|
+
'next' => reply[0],
|
3535
|
+
'entries' => reply[1]
|
2710
3536
|
}
|
3537
|
+
}
|
3538
|
+
|
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
|
3545
|
+
}
|
3546
|
+
}
|
3547
|
+
|
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('-'))
|
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
|
2711
3603
|
end
|
2712
3604
|
|
2713
|
-
def _subscription(method, channels, block)
|
3605
|
+
def _subscription(method, timeout, channels, block)
|
2714
3606
|
return @client.call([method] + channels) if subscribed?
|
2715
3607
|
|
2716
3608
|
begin
|
2717
3609
|
original, @client = @client, SubscribedClient.new(@client)
|
2718
|
-
|
3610
|
+
if timeout > 0
|
3611
|
+
@client.send(method, timeout, *channels, &block)
|
3612
|
+
else
|
3613
|
+
@client.send(method, *channels, &block)
|
3614
|
+
end
|
2719
3615
|
ensure
|
2720
3616
|
@client = original
|
2721
3617
|
end
|
2722
3618
|
end
|
2723
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
|
2724
3638
|
end
|
2725
3639
|
|
2726
|
-
|
2727
|
-
|
2728
|
-
|
2729
|
-
|
2730
|
-
|
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"
|