redis 4.2.5 → 4.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +113 -0
  3. data/README.md +41 -21
  4. data/lib/redis/client.rb +52 -27
  5. data/lib/redis/cluster/command.rb +4 -6
  6. data/lib/redis/cluster/command_loader.rb +8 -9
  7. data/lib/redis/cluster/node.rb +14 -1
  8. data/lib/redis/cluster/node_loader.rb +8 -11
  9. data/lib/redis/cluster/option.rb +14 -4
  10. data/lib/redis/cluster/slot_loader.rb +9 -12
  11. data/lib/redis/cluster.rb +33 -13
  12. data/lib/redis/commands/bitmaps.rb +63 -0
  13. data/lib/redis/commands/cluster.rb +45 -0
  14. data/lib/redis/commands/connection.rb +58 -0
  15. data/lib/redis/commands/geo.rb +84 -0
  16. data/lib/redis/commands/hashes.rb +251 -0
  17. data/lib/redis/commands/hyper_log_log.rb +37 -0
  18. data/lib/redis/commands/keys.rb +455 -0
  19. data/lib/redis/commands/lists.rb +290 -0
  20. data/lib/redis/commands/pubsub.rb +72 -0
  21. data/lib/redis/commands/scripting.rb +114 -0
  22. data/lib/redis/commands/server.rb +188 -0
  23. data/lib/redis/commands/sets.rb +223 -0
  24. data/lib/redis/commands/sorted_sets.rb +812 -0
  25. data/lib/redis/commands/streams.rb +382 -0
  26. data/lib/redis/commands/strings.rb +313 -0
  27. data/lib/redis/commands/transactions.rb +139 -0
  28. data/lib/redis/commands.rb +240 -0
  29. data/lib/redis/connection/command_helper.rb +2 -0
  30. data/lib/redis/connection/hiredis.rb +3 -2
  31. data/lib/redis/connection/ruby.rb +19 -9
  32. data/lib/redis/connection/synchrony.rb +10 -8
  33. data/lib/redis/connection.rb +1 -1
  34. data/lib/redis/distributed.rb +124 -29
  35. data/lib/redis/errors.rb +9 -0
  36. data/lib/redis/pipeline.rb +128 -3
  37. data/lib/redis/version.rb +1 -1
  38. data/lib/redis.rb +139 -3377
  39. metadata +22 -5
data/lib/redis.rb CHANGED
@@ -1,32 +1,54 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "monitor"
4
- require_relative "redis/errors"
4
+ require "redis/errors"
5
+ require "redis/commands"
5
6
 
6
7
  class Redis
8
+ BASE_PATH = __dir__
9
+ @exists_returns_integer = true
10
+ @sadd_returns_boolean = true
11
+
12
+ Deprecated = Class.new(StandardError)
13
+
7
14
  class << self
8
15
  attr_reader :exists_returns_integer
16
+ attr_accessor :silence_deprecations, :raise_deprecations, :sadd_returns_boolean
9
17
 
10
18
  def exists_returns_integer=(value)
11
19
  unless value
12
- message = "`Redis#exists(key)` will return an Integer by default in redis-rb 4.3. The option to explicitly " \
20
+ deprecate!(
21
+ "`Redis#exists(key)` will return an Integer by default in redis-rb 4.3. The option to explicitly " \
13
22
  "disable this behaviour via `Redis.exists_returns_integer` will be removed in 5.0. You should use " \
14
23
  "`exists?` instead."
15
-
16
- ::Kernel.warn(message)
24
+ )
17
25
  end
18
26
 
19
27
  @exists_returns_integer = value
20
28
  end
21
29
 
22
- attr_writer :current
23
- end
30
+ def deprecate!(message)
31
+ unless silence_deprecations
32
+ if raise_deprecations
33
+ raise Deprecated, message
34
+ else
35
+ ::Kernel.warn(message)
36
+ end
37
+ end
38
+ end
24
39
 
25
- def self.current
26
- @current ||= Redis.new
40
+ def current
41
+ deprecate!("`Redis.current` is deprecated and will be removed in 5.0. (called from: #{caller(1, 1).first})")
42
+ @current ||= Redis.new
43
+ end
44
+
45
+ def current=(redis)
46
+ deprecate!("`Redis.current=` is deprecated and will be removed in 5.0. (called from: #{caller(1, 1).first})")
47
+ @current = redis
48
+ end
27
49
  end
28
50
 
29
- include MonitorMixin
51
+ include Commands
30
52
 
31
53
  # Create a new client instance
32
54
  #
@@ -39,6 +61,7 @@ class Redis
39
61
  # @option options [String] :path path to server socket (overrides host and port)
40
62
  # @option options [Float] :timeout (5.0) timeout in seconds
41
63
  # @option options [Float] :connect_timeout (same as timeout) timeout for initial connect in seconds
64
+ # @option options [String] :username Username to authenticate against server
42
65
  # @option options [String] :password Password to authenticate against server
43
66
  # @option options [Integer] :db (0) Database to select after initial connect
44
67
  # @option options [Symbol] :driver Driver to use, currently supported: `:ruby`, `:hiredis`, `:synchrony`
@@ -52,6 +75,8 @@ class Redis
52
75
  # @option options [Symbol] :role (:master) Role to fetch via Sentinel, either `:master` or `:slave`
53
76
  # @option options [Array<String, Hash{Symbol => String, Integer}>] :cluster List of cluster nodes to contact
54
77
  # @option options [Boolean] :replica Whether to use readonly replica nodes in Redis Cluster or not
78
+ # @option options [String] :fixed_hostname Specify a FQDN if cluster mode enabled and
79
+ # client has to connect nodes via single endpoint with SSL/TLS
55
80
  # @option options [Class] :connector Class of custom connector
56
81
  #
57
82
  # @return [Redis] a new client instance
@@ -61,12 +86,7 @@ class Redis
61
86
  client = @cluster_mode ? Cluster : Client
62
87
  @original_client = @client = client.new(options)
63
88
  @queue = Hash.new { |h, k| h[k] = [] }
64
-
65
- super() # Monitor#initialize
66
- end
67
-
68
- def synchronize
69
- mon_synchronize { yield(@client) }
89
+ @monitor = Monitor.new
70
90
  end
71
91
 
72
92
  # Run code with the client reconnecting
@@ -92,37 +112,37 @@ class Redis
92
112
  end
93
113
  alias disconnect! close
94
114
 
95
- # Sends a command to Redis and returns its reply.
96
- #
97
- # Replies are converted to Ruby objects according to the RESP protocol, so
98
- # you can expect a Ruby array, integer or nil when Redis sends one. Higher
99
- # level transformations, such as converting an array of pairs into a Ruby
100
- # hash, are up to consumers.
101
- #
102
- # Redis error replies are raised as Ruby exceptions.
103
- def call(*command)
104
- synchronize do |client|
105
- client.call(command)
106
- end
115
+ def with
116
+ yield self
107
117
  end
108
118
 
109
- # Queues a command for pipelining.
119
+ # @deprecated Queues a command for pipelining.
110
120
  #
111
121
  # Commands in the queue are executed with the Redis#commit method.
112
122
  #
113
123
  # See http://redis.io/topics/pipelining for more details.
114
124
  #
115
125
  def queue(*command)
126
+ ::Redis.deprecate!(
127
+ "Redis#queue is deprecated and will be removed in Redis 5.0.0. Use Redis#pipelined instead." \
128
+ "(called from: #{caller(1, 1).first})"
129
+ )
130
+
116
131
  synchronize do
117
132
  @queue[Thread.current.object_id] << command
118
133
  end
119
134
  end
120
135
 
121
- # Sends all commands in the queue.
136
+ # @deprecated Sends all commands in the queue.
122
137
  #
123
138
  # See http://redis.io/topics/pipelining for more details.
124
139
  #
125
140
  def commit
141
+ ::Redis.deprecate!(
142
+ "Redis#commit is deprecated and will be removed in Redis 5.0.0. Use Redis#pipelined instead. " \
143
+ "(called from: #{Kernel.caller(1, 1).first})"
144
+ )
145
+
126
146
  synchronize do |client|
127
147
  begin
128
148
  pipeline = Pipeline.new(client)
@@ -141,3399 +161,141 @@ class Redis
141
161
  @client
142
162
  end
143
163
 
144
- # Authenticate to the server.
145
- #
146
- # @param [String] password must match the password specified in the
147
- # `requirepass` directive in the configuration file
148
- # @return [String] `OK`
149
- def auth(password)
150
- synchronize do |client|
151
- client.call([:auth, password])
152
- end
153
- end
154
-
155
- # Change the selected database for the current connection.
156
- #
157
- # @param [Integer] db zero-based index of the DB to use (0 to 15)
158
- # @return [String] `OK`
159
- def select(db)
160
- synchronize do |client|
161
- client.db = db
162
- client.call([:select, db])
163
- end
164
- end
165
-
166
- # Ping the server.
167
- #
168
- # @param [optional, String] message
169
- # @return [String] `PONG`
170
- def ping(message = nil)
171
- synchronize do |client|
172
- client.call([:ping, message].compact)
173
- end
174
- end
175
-
176
- # Echo the given string.
177
- #
178
- # @param [String] value
179
- # @return [String]
180
- def echo(value)
181
- synchronize do |client|
182
- client.call([:echo, value])
164
+ def pipelined(&block)
165
+ deprecation_displayed = false
166
+ if block&.arity == 0
167
+ Pipeline.deprecation_warning("pipelined", Kernel.caller_locations(1, 5))
168
+ deprecation_displayed = true
183
169
  end
184
- end
185
170
 
186
- # Close the connection.
187
- #
188
- # @return [String] `OK`
189
- def quit
190
- synchronize do |client|
171
+ synchronize do |prior_client|
191
172
  begin
192
- client.call([:quit])
193
- rescue ConnectionError
173
+ pipeline = Pipeline.new(prior_client)
174
+ @client = deprecation_displayed ? pipeline : DeprecatedPipeline.new(pipeline)
175
+ pipelined_connection = PipelinedConnection.new(pipeline)
176
+ yield pipelined_connection
177
+ prior_client.call_pipeline(pipeline)
194
178
  ensure
195
- client.disconnect
179
+ @client = prior_client
196
180
  end
197
181
  end
198
182
  end
199
183
 
200
- # Asynchronously rewrite the append-only file.
201
- #
202
- # @return [String] `OK`
203
- def bgrewriteaof
204
- synchronize do |client|
205
- client.call([:bgrewriteaof])
206
- end
207
- end
208
-
209
- # Asynchronously save the dataset to disk.
210
- #
211
- # @return [String] `OK`
212
- def bgsave
213
- synchronize do |client|
214
- client.call([:bgsave])
215
- end
216
- end
217
-
218
- # Get or set server configuration parameters.
184
+ # Mark the start of a transaction block.
219
185
  #
220
- # @param [Symbol] action e.g. `:get`, `:set`, `:resetstat`
221
- # @return [String, Hash] string reply, or hash when retrieving more than one
222
- # property with `CONFIG GET`
223
- def config(action, *args)
224
- synchronize do |client|
225
- client.call([:config, action] + args) do |reply|
226
- if reply.is_a?(Array) && action == :get
227
- Hashify.call(reply)
228
- else
229
- reply
230
- end
231
- end
232
- end
233
- end
234
-
235
- # Manage client connections.
186
+ # Passing a block is optional.
236
187
  #
237
- # @param [String, Symbol] subcommand e.g. `kill`, `list`, `getname`, `setname`
238
- # @return [String, Hash] depends on subcommand
239
- def client(subcommand = nil, *args)
240
- synchronize do |client|
241
- client.call([:client, subcommand] + args) do |reply|
242
- if subcommand.to_s == "list"
243
- reply.lines.map do |line|
244
- entries = line.chomp.split(/[ =]/)
245
- Hash[entries.each_slice(2).to_a]
246
- end
247
- else
248
- reply
249
- end
250
- end
251
- end
252
- end
253
-
254
- # Return the number of keys in the selected database.
188
+ # @example With a block
189
+ # redis.multi do |multi|
190
+ # multi.set("key", "value")
191
+ # multi.incr("counter")
192
+ # end # => ["OK", 6]
255
193
  #
256
- # @return [Integer]
257
- def dbsize
258
- synchronize do |client|
259
- client.call([:dbsize])
260
- end
261
- end
262
-
263
- def debug(*args)
264
- synchronize do |client|
265
- client.call([:debug] + args)
266
- end
267
- end
268
-
269
- # Remove all keys from all databases.
194
+ # @example Without a block
195
+ # redis.multi
196
+ # # => "OK"
197
+ # redis.set("key", "value")
198
+ # # => "QUEUED"
199
+ # redis.incr("counter")
200
+ # # => "QUEUED"
201
+ # redis.exec
202
+ # # => ["OK", 6]
270
203
  #
271
- # @param [Hash] options
272
- # - `:async => Boolean`: async flush (default: false)
273
- # @return [String] `OK`
274
- def flushall(options = nil)
275
- synchronize do |client|
276
- if options && options[:async]
277
- client.call(%i[flushall async])
278
- else
279
- client.call([:flushall])
280
- end
281
- end
282
- end
283
-
284
- # Remove all keys from the current database.
204
+ # @yield [multi] the commands that are called inside this block are cached
205
+ # and written to the server upon returning from it
206
+ # @yieldparam [Redis] multi `self`
285
207
  #
286
- # @param [Hash] options
287
- # - `:async => Boolean`: async flush (default: false)
288
- # @return [String] `OK`
289
- def flushdb(options = nil)
290
- synchronize do |client|
291
- if options && options[:async]
292
- client.call(%i[flushdb async])
293
- else
294
- client.call([:flushdb])
295
- end
296
- end
297
- end
298
-
299
- # Get information and statistics about the server.
208
+ # @return [String, Array<...>]
209
+ # - when a block is not given, `OK`
210
+ # - when a block is given, an array with replies
300
211
  #
301
- # @param [String, Symbol] cmd e.g. "commandstats"
302
- # @return [Hash<String, String>]
303
- def info(cmd = nil)
304
- synchronize do |client|
305
- client.call([:info, cmd].compact) do |reply|
306
- if reply.is_a?(String)
307
- reply = HashifyInfo.call(reply)
308
-
309
- if cmd && cmd.to_s == "commandstats"
310
- # Extract nested hashes for INFO COMMANDSTATS
311
- reply = Hash[reply.map do |k, v|
312
- v = v.split(",").map { |e| e.split("=") }
313
- [k[/^cmdstat_(.*)$/, 1], Hash[v]]
314
- end]
315
- end
316
- end
317
-
318
- reply
212
+ # @see #watch
213
+ # @see #unwatch
214
+ def multi(&block)
215
+ if block_given?
216
+ deprecation_displayed = false
217
+ if block&.arity == 0
218
+ Pipeline.deprecation_warning("multi", Kernel.caller_locations(1, 5))
219
+ deprecation_displayed = true
319
220
  end
320
- end
321
- end
322
-
323
- # Get the UNIX time stamp of the last successful save to disk.
324
- #
325
- # @return [Integer]
326
- def lastsave
327
- synchronize do |client|
328
- client.call([:lastsave])
329
- end
330
- end
331
-
332
- # Listen for all requests received by the server in real time.
333
- #
334
- # There is no way to interrupt this command.
335
- #
336
- # @yield a block to be called for every line of output
337
- # @yieldparam [String] line timestamp and command that was executed
338
- def monitor(&block)
339
- synchronize do |client|
340
- client.call_loop([:monitor], &block)
341
- end
342
- end
343
-
344
- # Synchronously save the dataset to disk.
345
- #
346
- # @return [String]
347
- def save
348
- synchronize do |client|
349
- client.call([:save])
350
- end
351
- end
352
221
 
353
- # Synchronously save the dataset to disk and then shut down the server.
354
- def shutdown
355
- synchronize do |client|
356
- client.with_reconnect(false) do
222
+ synchronize do |prior_client|
357
223
  begin
358
- client.call([:shutdown])
359
- rescue ConnectionError
360
- # This means Redis has probably exited.
361
- nil
224
+ pipeline = Pipeline::Multi.new(prior_client)
225
+ @client = deprecation_displayed ? pipeline : DeprecatedMulti.new(pipeline)
226
+ pipelined_connection = PipelinedConnection.new(pipeline)
227
+ yield pipelined_connection
228
+ prior_client.call_pipeline(pipeline)
229
+ ensure
230
+ @client = prior_client
362
231
  end
363
232
  end
233
+ else
234
+ send_command([:multi])
364
235
  end
365
236
  end
366
237
 
367
- # Make the server a slave of another instance, or promote it as master.
368
- def slaveof(host, port)
369
- synchronize do |client|
370
- client.call([:slaveof, host, port])
371
- end
372
- end
373
-
374
- # Interact with the slowlog (get, len, reset)
375
- #
376
- # @param [String] subcommand e.g. `get`, `len`, `reset`
377
- # @param [Integer] length maximum number of entries to return
378
- # @return [Array<String>, Integer, String] depends on subcommand
379
- def slowlog(subcommand, length = nil)
380
- synchronize do |client|
381
- args = [:slowlog, subcommand]
382
- args << length if length
383
- client.call args
384
- end
385
- end
386
-
387
- # Internal command used for replication.
388
- def sync
389
- synchronize do |client|
390
- client.call([:sync])
391
- end
392
- end
393
-
394
- # Return the server time.
395
- #
396
- # @example
397
- # r.time # => [ 1333093196, 606806 ]
398
- #
399
- # @return [Array<Integer>] tuple of seconds since UNIX epoch and
400
- # microseconds in the current second
401
- def time
402
- synchronize do |client|
403
- client.call([:time]) do |reply|
404
- reply&.map(&:to_i)
405
- end
406
- end
407
- end
408
-
409
- # Remove the expiration from a key.
410
- #
411
- # @param [String] key
412
- # @return [Boolean] whether the timeout was removed or not
413
- def persist(key)
414
- synchronize do |client|
415
- client.call([:persist, key], &Boolify)
416
- end
417
- end
418
-
419
- # Set a key's time to live in seconds.
420
- #
421
- # @param [String] key
422
- # @param [Integer] seconds time to live
423
- # @return [Boolean] whether the timeout was set or not
424
- def expire(key, seconds)
425
- synchronize do |client|
426
- client.call([:expire, key, seconds], &Boolify)
427
- end
428
- end
429
-
430
- # Set the expiration for a key as a UNIX timestamp.
431
- #
432
- # @param [String] key
433
- # @param [Integer] unix_time expiry time specified as a UNIX timestamp
434
- # @return [Boolean] whether the timeout was set or not
435
- def expireat(key, unix_time)
436
- synchronize do |client|
437
- client.call([:expireat, key, unix_time], &Boolify)
438
- end
439
- end
440
-
441
- # Get the time to live (in seconds) for a key.
442
- #
443
- # @param [String] key
444
- # @return [Integer] remaining time to live in seconds.
445
- #
446
- # In Redis 2.6 or older the command returns -1 if the key does not exist or if
447
- # the key exist but has no associated expire.
448
- #
449
- # Starting with Redis 2.8 the return value in case of error changed:
450
- #
451
- # - The command returns -2 if the key does not exist.
452
- # - The command returns -1 if the key exists but has no associated expire.
453
- def ttl(key)
454
- synchronize do |client|
455
- client.call([:ttl, key])
456
- end
457
- end
458
-
459
- # Set a key's time to live in milliseconds.
460
- #
461
- # @param [String] key
462
- # @param [Integer] milliseconds time to live
463
- # @return [Boolean] whether the timeout was set or not
464
- def pexpire(key, milliseconds)
465
- synchronize do |client|
466
- client.call([:pexpire, key, milliseconds], &Boolify)
467
- end
468
- end
469
-
470
- # Set the expiration for a key as number of milliseconds from UNIX Epoch.
471
- #
472
- # @param [String] key
473
- # @param [Integer] ms_unix_time expiry time specified as number of milliseconds from UNIX Epoch.
474
- # @return [Boolean] whether the timeout was set or not
475
- def pexpireat(key, ms_unix_time)
476
- synchronize do |client|
477
- client.call([:pexpireat, key, ms_unix_time], &Boolify)
478
- end
479
- end
480
-
481
- # Get the time to live (in milliseconds) for a key.
482
- #
483
- # @param [String] key
484
- # @return [Integer] remaining time to live in milliseconds
485
- # In Redis 2.6 or older the command returns -1 if the key does not exist or if
486
- # the key exist but has no associated expire.
487
- #
488
- # Starting with Redis 2.8 the return value in case of error changed:
489
- #
490
- # - The command returns -2 if the key does not exist.
491
- # - The command returns -1 if the key exists but has no associated expire.
492
- def pttl(key)
493
- synchronize do |client|
494
- client.call([:pttl, key])
495
- end
238
+ def id
239
+ @original_client.id
496
240
  end
497
241
 
498
- # Return a serialized version of the value stored at a key.
499
- #
500
- # @param [String] key
501
- # @return [String] serialized_value
502
- def dump(key)
503
- synchronize do |client|
504
- client.call([:dump, key])
505
- end
242
+ def inspect
243
+ "#<Redis client v#{Redis::VERSION} for #{id}>"
506
244
  end
507
245
 
508
- # Create a key using the serialized value, previously obtained using DUMP.
509
- #
510
- # @param [String] key
511
- # @param [String] ttl
512
- # @param [String] serialized_value
513
- # @param [Hash] options
514
- # - `:replace => Boolean`: if false, raises an error if key already exists
515
- # @raise [Redis::CommandError]
516
- # @return [String] `"OK"`
517
- def restore(key, ttl, serialized_value, replace: nil)
518
- args = [:restore, key, ttl, serialized_value]
519
- args << 'REPLACE' if replace
520
-
521
- synchronize do |client|
522
- client.call(args)
523
- end
246
+ def dup
247
+ self.class.new(@options)
524
248
  end
525
249
 
526
- # Transfer a key from the connected instance to another instance.
527
- #
528
- # @param [String, Array<String>] key
529
- # @param [Hash] options
530
- # - `:host => String`: host of instance to migrate to
531
- # - `:port => Integer`: port of instance to migrate to
532
- # - `:db => Integer`: database to migrate to (default: same as source)
533
- # - `:timeout => Integer`: timeout (default: same as connection timeout)
534
- # - `:copy => Boolean`: Do not remove the key from the local instance.
535
- # - `:replace => Boolean`: Replace existing key on the remote instance.
536
- # @return [String] `"OK"`
537
- def migrate(key, options)
538
- args = [:migrate]
539
- args << (options[:host] || raise(':host not specified'))
540
- args << (options[:port] || raise(':port not specified'))
541
- args << (key.is_a?(String) ? key : '')
542
- args << (options[:db] || @client.db).to_i
543
- args << (options[:timeout] || @client.timeout).to_i
544
- args << 'COPY' if options[:copy]
545
- args << 'REPLACE' if options[:replace]
546
- args += ['KEYS', *key] if key.is_a?(Array)
250
+ def connection
251
+ return @original_client.connection_info if @cluster_mode
547
252
 
548
- synchronize { |client| client.call(args) }
253
+ {
254
+ host: @original_client.host,
255
+ port: @original_client.port,
256
+ db: @original_client.db,
257
+ id: @original_client.id,
258
+ location: @original_client.location
259
+ }
549
260
  end
550
261
 
551
- # Delete one or more keys.
552
- #
553
- # @param [String, Array<String>] keys
554
- # @return [Integer] number of keys that were deleted
555
- def del(*keys)
556
- synchronize do |client|
557
- client.call([:del] + keys)
558
- end
559
- end
262
+ private
560
263
 
561
- # Unlink one or more keys.
562
- #
563
- # @param [String, Array<String>] keys
564
- # @return [Integer] number of keys that were unlinked
565
- def unlink(*keys)
566
- synchronize do |client|
567
- client.call([:unlink] + keys)
568
- end
264
+ def synchronize
265
+ @monitor.synchronize { yield(@client) }
569
266
  end
570
267
 
571
- # Determine how many of the keys exists.
572
- #
573
- # @param [String, Array<String>] keys
574
- # @return [Integer]
575
- def exists(*keys)
576
- if !Redis.exists_returns_integer && keys.size == 1
577
- if Redis.exists_returns_integer.nil?
578
- message = "`Redis#exists(key)` will return an Integer in redis-rb 4.3. `exists?` returns a boolean, you " \
579
- "should use it instead. To opt-in to the new behavior now you can set Redis.exists_returns_integer = " \
580
- "true. To disable this message and keep the current (boolean) behaviour of 'exists' you can set " \
581
- "`Redis.exists_returns_integer = false`, but this option will be removed in 5.0. " \
582
- "(#{::Kernel.caller(1, 1).first})\n"
583
-
584
- ::Kernel.warn(message)
585
- end
586
-
587
- exists?(*keys)
588
- else
589
- _exists(*keys)
268
+ def send_command(command, &block)
269
+ @monitor.synchronize do
270
+ @client.call(command, &block)
590
271
  end
591
272
  end
592
273
 
593
- def _exists(*keys)
594
- synchronize do |client|
595
- client.call([:exists, *keys])
274
+ def send_blocking_command(command, timeout, &block)
275
+ @monitor.synchronize do
276
+ @client.call_with_timeout(command, timeout, &block)
596
277
  end
597
278
  end
598
279
 
599
- # Determine if any of the keys exists.
600
- #
601
- # @param [String, Array<String>] keys
602
- # @return [Boolean]
603
- def exists?(*keys)
604
- synchronize do |client|
605
- client.call([:exists, *keys]) do |value|
606
- value > 0
607
- end
608
- end
609
- end
280
+ def _subscription(method, timeout, channels, block)
281
+ return @client.call([method] + channels) if subscribed?
610
282
 
611
- # Find all keys matching the given pattern.
612
- #
613
- # @param [String] pattern
614
- # @return [Array<String>]
615
- def keys(pattern = "*")
616
- synchronize do |client|
617
- client.call([:keys, pattern]) do |reply|
618
- if reply.is_a?(String)
619
- reply.split(" ")
620
- else
621
- reply
622
- end
623
- end
624
- end
625
- end
626
-
627
- # Move a key to another database.
628
- #
629
- # @example Move a key to another database
630
- # redis.set "foo", "bar"
631
- # # => "OK"
632
- # redis.move "foo", 2
633
- # # => true
634
- # redis.exists "foo"
635
- # # => false
636
- # redis.select 2
637
- # # => "OK"
638
- # redis.exists "foo"
639
- # # => true
640
- # redis.get "foo"
641
- # # => "bar"
642
- #
643
- # @param [String] key
644
- # @param [Integer] db
645
- # @return [Boolean] whether the key was moved or not
646
- def move(key, db)
647
- synchronize do |client|
648
- client.call([:move, key, db], &Boolify)
649
- end
650
- end
651
-
652
- def object(*args)
653
- synchronize do |client|
654
- client.call([:object] + args)
655
- end
656
- end
657
-
658
- # Return a random key from the keyspace.
659
- #
660
- # @return [String]
661
- def randomkey
662
- synchronize do |client|
663
- client.call([:randomkey])
664
- end
665
- end
666
-
667
- # Rename a key. If the new key already exists it is overwritten.
668
- #
669
- # @param [String] old_name
670
- # @param [String] new_name
671
- # @return [String] `OK`
672
- def rename(old_name, new_name)
673
- synchronize do |client|
674
- client.call([:rename, old_name, new_name])
675
- end
676
- end
677
-
678
- # Rename a key, only if the new key does not exist.
679
- #
680
- # @param [String] old_name
681
- # @param [String] new_name
682
- # @return [Boolean] whether the key was renamed or not
683
- def renamenx(old_name, new_name)
684
- synchronize do |client|
685
- client.call([:renamenx, old_name, new_name], &Boolify)
686
- end
687
- end
688
-
689
- # Sort the elements in a list, set or sorted set.
690
- #
691
- # @example Retrieve the first 2 elements from an alphabetically sorted "list"
692
- # redis.sort("list", :order => "alpha", :limit => [0, 2])
693
- # # => ["a", "b"]
694
- # @example Store an alphabetically descending list in "target"
695
- # redis.sort("list", :order => "desc alpha", :store => "target")
696
- # # => 26
697
- #
698
- # @param [String] key
699
- # @param [Hash] options
700
- # - `:by => String`: use external key to sort elements by
701
- # - `:limit => [offset, count]`: skip `offset` elements, return a maximum
702
- # of `count` elements
703
- # - `:get => [String, Array<String>]`: single key or array of keys to
704
- # retrieve per element in the result
705
- # - `:order => String`: combination of `ASC`, `DESC` and optionally `ALPHA`
706
- # - `:store => String`: key to store the result at
707
- #
708
- # @return [Array<String>, Array<Array<String>>, Integer]
709
- # - when `:get` is not specified, or holds a single element, an array of elements
710
- # - when `:get` is specified, and holds more than one element, an array of
711
- # elements where every element is an array with the result for every
712
- # element specified in `:get`
713
- # - when `:store` is specified, the number of elements in the stored result
714
- def sort(key, by: nil, limit: nil, get: nil, order: nil, store: nil)
715
- args = [:sort, key]
716
- args << "BY" << by if by
717
-
718
- if limit
719
- args << "LIMIT"
720
- args.concat(limit)
721
- end
722
-
723
- get = Array(get)
724
- get.each do |item|
725
- args << "GET" << item
726
- end
727
-
728
- args.concat(order.split(" ")) if order
729
- args << "STORE" << store if store
730
-
731
- synchronize do |client|
732
- client.call(args) do |reply|
733
- if get.size > 1 && !store
734
- reply.each_slice(get.size).to_a if reply
735
- else
736
- reply
737
- end
738
- end
739
- end
740
- end
741
-
742
- # Determine the type stored at key.
743
- #
744
- # @param [String] key
745
- # @return [String] `string`, `list`, `set`, `zset`, `hash` or `none`
746
- def type(key)
747
- synchronize do |client|
748
- client.call([:type, key])
749
- end
750
- end
751
-
752
- # Decrement the integer value of a key by one.
753
- #
754
- # @example
755
- # redis.decr("value")
756
- # # => 4
757
- #
758
- # @param [String] key
759
- # @return [Integer] value after decrementing it
760
- def decr(key)
761
- synchronize do |client|
762
- client.call([:decr, key])
763
- end
764
- end
765
-
766
- # Decrement the integer value of a key by the given number.
767
- #
768
- # @example
769
- # redis.decrby("value", 5)
770
- # # => 0
771
- #
772
- # @param [String] key
773
- # @param [Integer] decrement
774
- # @return [Integer] value after decrementing it
775
- def decrby(key, decrement)
776
- synchronize do |client|
777
- client.call([:decrby, key, decrement])
778
- end
779
- end
780
-
781
- # Increment the integer value of a key by one.
782
- #
783
- # @example
784
- # redis.incr("value")
785
- # # => 6
786
- #
787
- # @param [String] key
788
- # @return [Integer] value after incrementing it
789
- def incr(key)
790
- synchronize do |client|
791
- client.call([:incr, key])
792
- end
793
- end
794
-
795
- # Increment the integer value of a key by the given integer number.
796
- #
797
- # @example
798
- # redis.incrby("value", 5)
799
- # # => 10
800
- #
801
- # @param [String] key
802
- # @param [Integer] increment
803
- # @return [Integer] value after incrementing it
804
- def incrby(key, increment)
805
- synchronize do |client|
806
- client.call([:incrby, key, increment])
807
- end
808
- end
809
-
810
- # Increment the numeric value of a key by the given float number.
811
- #
812
- # @example
813
- # redis.incrbyfloat("value", 1.23)
814
- # # => 1.23
815
- #
816
- # @param [String] key
817
- # @param [Float] increment
818
- # @return [Float] value after incrementing it
819
- def incrbyfloat(key, increment)
820
- synchronize do |client|
821
- client.call([:incrbyfloat, key, increment], &Floatify)
822
- end
823
- end
824
-
825
- # Set the string value of a key.
826
- #
827
- # @param [String] key
828
- # @param [String] value
829
- # @param [Hash] options
830
- # - `:ex => Integer`: Set the specified expire time, in seconds.
831
- # - `:px => Integer`: Set the specified expire time, in milliseconds.
832
- # - `:nx => true`: Only set the key if it does not already exist.
833
- # - `:xx => true`: Only set the key if it already exist.
834
- # - `:keepttl => true`: Retain the time to live associated with the key.
835
- # @return [String, Boolean] `"OK"` or true, false if `:nx => true` or `:xx => true`
836
- def set(key, value, ex: nil, px: nil, nx: nil, xx: nil, keepttl: nil)
837
- args = [:set, key, value.to_s]
838
- args << "EX" << ex if ex
839
- args << "PX" << px if px
840
- args << "NX" if nx
841
- args << "XX" if xx
842
- args << "KEEPTTL" if keepttl
843
-
844
- synchronize do |client|
845
- if nx || xx
846
- client.call(args, &BoolifySet)
847
- else
848
- client.call(args)
849
- end
850
- end
851
- end
852
-
853
- # Set the time to live in seconds of a key.
854
- #
855
- # @param [String] key
856
- # @param [Integer] ttl
857
- # @param [String] value
858
- # @return [String] `"OK"`
859
- def setex(key, ttl, value)
860
- synchronize do |client|
861
- client.call([:setex, key, ttl, value.to_s])
862
- end
863
- end
864
-
865
- # Set the time to live in milliseconds of a key.
866
- #
867
- # @param [String] key
868
- # @param [Integer] ttl
869
- # @param [String] value
870
- # @return [String] `"OK"`
871
- def psetex(key, ttl, value)
872
- synchronize do |client|
873
- client.call([:psetex, key, ttl, value.to_s])
874
- end
875
- end
876
-
877
- # Set the value of a key, only if the key does not exist.
878
- #
879
- # @param [String] key
880
- # @param [String] value
881
- # @return [Boolean] whether the key was set or not
882
- def setnx(key, value)
883
- synchronize do |client|
884
- client.call([:setnx, key, value.to_s], &Boolify)
885
- end
886
- end
887
-
888
- # Set one or more values.
889
- #
890
- # @example
891
- # redis.mset("key1", "v1", "key2", "v2")
892
- # # => "OK"
893
- #
894
- # @param [Array<String>] args array of keys and values
895
- # @return [String] `"OK"`
896
- #
897
- # @see #mapped_mset
898
- def mset(*args)
899
- synchronize do |client|
900
- client.call([:mset] + args)
901
- end
902
- end
903
-
904
- # Set one or more values.
905
- #
906
- # @example
907
- # redis.mapped_mset({ "f1" => "v1", "f2" => "v2" })
908
- # # => "OK"
909
- #
910
- # @param [Hash] hash keys mapping to values
911
- # @return [String] `"OK"`
912
- #
913
- # @see #mset
914
- def mapped_mset(hash)
915
- mset(hash.to_a.flatten)
916
- end
917
-
918
- # Set one or more values, only if none of the keys exist.
919
- #
920
- # @example
921
- # redis.msetnx("key1", "v1", "key2", "v2")
922
- # # => true
923
- #
924
- # @param [Array<String>] args array of keys and values
925
- # @return [Boolean] whether or not all values were set
926
- #
927
- # @see #mapped_msetnx
928
- def msetnx(*args)
929
- synchronize do |client|
930
- client.call([:msetnx, *args], &Boolify)
931
- end
932
- end
933
-
934
- # Set one or more values, only if none of the keys exist.
935
- #
936
- # @example
937
- # redis.mapped_msetnx({ "key1" => "v1", "key2" => "v2" })
938
- # # => true
939
- #
940
- # @param [Hash] hash keys mapping to values
941
- # @return [Boolean] whether or not all values were set
942
- #
943
- # @see #msetnx
944
- def mapped_msetnx(hash)
945
- msetnx(hash.to_a.flatten)
946
- end
947
-
948
- # Get the value of a key.
949
- #
950
- # @param [String] key
951
- # @return [String]
952
- def get(key)
953
- synchronize do |client|
954
- client.call([:get, key])
955
- end
956
- end
957
-
958
- # Get the values of all the given keys.
959
- #
960
- # @example
961
- # redis.mget("key1", "key2")
962
- # # => ["v1", "v2"]
963
- #
964
- # @param [Array<String>] keys
965
- # @return [Array<String>] an array of values for the specified keys
966
- #
967
- # @see #mapped_mget
968
- def mget(*keys, &blk)
969
- synchronize do |client|
970
- client.call([:mget, *keys], &blk)
971
- end
972
- end
973
-
974
- # Get the values of all the given keys.
975
- #
976
- # @example
977
- # redis.mapped_mget("key1", "key2")
978
- # # => { "key1" => "v1", "key2" => "v2" }
979
- #
980
- # @param [Array<String>] keys array of keys
981
- # @return [Hash] a hash mapping the specified keys to their values
982
- #
983
- # @see #mget
984
- def mapped_mget(*keys)
985
- mget(*keys) do |reply|
986
- if reply.is_a?(Array)
987
- Hash[keys.zip(reply)]
988
- else
989
- reply
990
- end
991
- end
992
- end
993
-
994
- # Overwrite part of a string at key starting at the specified offset.
995
- #
996
- # @param [String] key
997
- # @param [Integer] offset byte offset
998
- # @param [String] value
999
- # @return [Integer] length of the string after it was modified
1000
- def setrange(key, offset, value)
1001
- synchronize do |client|
1002
- client.call([:setrange, key, offset, value.to_s])
1003
- end
1004
- end
1005
-
1006
- # Get a substring of the string stored at a key.
1007
- #
1008
- # @param [String] key
1009
- # @param [Integer] start zero-based start offset
1010
- # @param [Integer] stop zero-based end offset. Use -1 for representing
1011
- # the end of the string
1012
- # @return [Integer] `0` or `1`
1013
- def getrange(key, start, stop)
1014
- synchronize do |client|
1015
- client.call([:getrange, key, start, stop])
1016
- end
1017
- end
1018
-
1019
- # Sets or clears the bit at offset in the string value stored at key.
1020
- #
1021
- # @param [String] key
1022
- # @param [Integer] offset bit offset
1023
- # @param [Integer] value bit value `0` or `1`
1024
- # @return [Integer] the original bit value stored at `offset`
1025
- def setbit(key, offset, value)
1026
- synchronize do |client|
1027
- client.call([:setbit, key, offset, value])
1028
- end
1029
- end
1030
-
1031
- # Returns the bit value at offset in the string value stored at key.
1032
- #
1033
- # @param [String] key
1034
- # @param [Integer] offset bit offset
1035
- # @return [Integer] `0` or `1`
1036
- def getbit(key, offset)
1037
- synchronize do |client|
1038
- client.call([:getbit, key, offset])
1039
- end
1040
- end
1041
-
1042
- # Append a value to a key.
1043
- #
1044
- # @param [String] key
1045
- # @param [String] value value to append
1046
- # @return [Integer] length of the string after appending
1047
- def append(key, value)
1048
- synchronize do |client|
1049
- client.call([:append, key, value])
1050
- end
1051
- end
1052
-
1053
- # Count the number of set bits in a range of the string value stored at key.
1054
- #
1055
- # @param [String] key
1056
- # @param [Integer] start start index
1057
- # @param [Integer] stop stop index
1058
- # @return [Integer] the number of bits set to 1
1059
- def bitcount(key, start = 0, stop = -1)
1060
- synchronize do |client|
1061
- client.call([:bitcount, key, start, stop])
1062
- end
1063
- end
1064
-
1065
- # Perform a bitwise operation between strings and store the resulting string in a key.
1066
- #
1067
- # @param [String] operation e.g. `and`, `or`, `xor`, `not`
1068
- # @param [String] destkey destination key
1069
- # @param [String, Array<String>] keys one or more source keys to perform `operation`
1070
- # @return [Integer] the length of the string stored in `destkey`
1071
- def bitop(operation, destkey, *keys)
1072
- synchronize do |client|
1073
- client.call([:bitop, operation, destkey, *keys])
1074
- end
1075
- end
1076
-
1077
- # Return the position of the first bit set to 1 or 0 in a string.
1078
- #
1079
- # @param [String] key
1080
- # @param [Integer] bit whether to look for the first 1 or 0 bit
1081
- # @param [Integer] start start index
1082
- # @param [Integer] stop stop index
1083
- # @return [Integer] the position of the first 1/0 bit.
1084
- # -1 if looking for 1 and it is not found or start and stop are given.
1085
- def bitpos(key, bit, start = nil, stop = nil)
1086
- raise(ArgumentError, 'stop parameter specified without start parameter') if stop && !start
1087
-
1088
- synchronize do |client|
1089
- command = [:bitpos, key, bit]
1090
- command << start if start
1091
- command << stop if stop
1092
- client.call(command)
1093
- end
1094
- end
1095
-
1096
- # Set the string value of a key and return its old value.
1097
- #
1098
- # @param [String] key
1099
- # @param [String] value value to replace the current value with
1100
- # @return [String] the old value stored in the key, or `nil` if the key
1101
- # did not exist
1102
- def getset(key, value)
1103
- synchronize do |client|
1104
- client.call([:getset, key, value.to_s])
1105
- end
1106
- end
1107
-
1108
- # Get the length of the value stored in a key.
1109
- #
1110
- # @param [String] key
1111
- # @return [Integer] the length of the value stored in the key, or 0
1112
- # if the key does not exist
1113
- def strlen(key)
1114
- synchronize do |client|
1115
- client.call([:strlen, key])
1116
- end
1117
- end
1118
-
1119
- # Get the length of a list.
1120
- #
1121
- # @param [String] key
1122
- # @return [Integer]
1123
- def llen(key)
1124
- synchronize do |client|
1125
- client.call([:llen, key])
1126
- end
1127
- end
1128
-
1129
- # Prepend one or more values to a list, creating the list if it doesn't exist
1130
- #
1131
- # @param [String] key
1132
- # @param [String, Array<String>] value string value, or array of string values to push
1133
- # @return [Integer] the length of the list after the push operation
1134
- def lpush(key, value)
1135
- synchronize do |client|
1136
- client.call([:lpush, key, value])
1137
- end
1138
- end
1139
-
1140
- # Prepend a value to a list, only if the list exists.
1141
- #
1142
- # @param [String] key
1143
- # @param [String] value
1144
- # @return [Integer] the length of the list after the push operation
1145
- def lpushx(key, value)
1146
- synchronize do |client|
1147
- client.call([:lpushx, key, value])
1148
- end
1149
- end
1150
-
1151
- # Append one or more values to a list, creating the list if it doesn't exist
1152
- #
1153
- # @param [String] key
1154
- # @param [String, Array<String>] value string value, or array of string values to push
1155
- # @return [Integer] the length of the list after the push operation
1156
- def rpush(key, value)
1157
- synchronize do |client|
1158
- client.call([:rpush, key, value])
1159
- end
1160
- end
1161
-
1162
- # Append a value to a list, only if the list exists.
1163
- #
1164
- # @param [String] key
1165
- # @param [String] value
1166
- # @return [Integer] the length of the list after the push operation
1167
- def rpushx(key, value)
1168
- synchronize do |client|
1169
- client.call([:rpushx, key, value])
1170
- end
1171
- end
1172
-
1173
- # Remove and get the first element in a list.
1174
- #
1175
- # @param [String] key
1176
- # @return [String]
1177
- def lpop(key)
1178
- synchronize do |client|
1179
- client.call([:lpop, key])
1180
- end
1181
- end
1182
-
1183
- # Remove and get the last element in a list.
1184
- #
1185
- # @param [String] key
1186
- # @return [String]
1187
- def rpop(key)
1188
- synchronize do |client|
1189
- client.call([:rpop, key])
1190
- end
1191
- end
1192
-
1193
- # Remove the last element in a list, append it to another list and return it.
1194
- #
1195
- # @param [String] source source key
1196
- # @param [String] destination destination key
1197
- # @return [nil, String] the element, or nil when the source key does not exist
1198
- def rpoplpush(source, destination)
1199
- synchronize do |client|
1200
- client.call([:rpoplpush, source, destination])
1201
- end
1202
- end
1203
-
1204
- def _bpop(cmd, args, &blk)
1205
- timeout = if args.last.is_a?(Hash)
1206
- options = args.pop
1207
- options[:timeout]
1208
- elsif args.last.respond_to?(:to_int)
1209
- # Issue deprecation notice in obnoxious mode...
1210
- args.pop.to_int
1211
- end
1212
-
1213
- timeout ||= 0
1214
-
1215
- if args.size > 1
1216
- # Issue deprecation notice in obnoxious mode...
1217
- end
1218
-
1219
- keys = args.flatten
1220
-
1221
- synchronize do |client|
1222
- command = [cmd, keys, timeout]
1223
- timeout += client.timeout if timeout > 0
1224
- client.call_with_timeout(command, timeout, &blk)
1225
- end
1226
- end
1227
-
1228
- # Remove and get the first element in a list, or block until one is available.
1229
- #
1230
- # @example With timeout
1231
- # list, element = redis.blpop("list", :timeout => 5)
1232
- # # => nil on timeout
1233
- # # => ["list", "element"] on success
1234
- # @example Without timeout
1235
- # list, element = redis.blpop("list")
1236
- # # => ["list", "element"]
1237
- # @example Blocking pop on multiple lists
1238
- # list, element = redis.blpop(["list", "another_list"])
1239
- # # => ["list", "element"]
1240
- #
1241
- # @param [String, Array<String>] keys one or more keys to perform the
1242
- # blocking pop on
1243
- # @param [Hash] options
1244
- # - `:timeout => Integer`: timeout in seconds, defaults to no timeout
1245
- #
1246
- # @return [nil, [String, String]]
1247
- # - `nil` when the operation timed out
1248
- # - tuple of the list that was popped from and element was popped otherwise
1249
- def blpop(*args)
1250
- _bpop(:blpop, args)
1251
- end
1252
-
1253
- # Remove and get the last element in a list, or block until one is available.
1254
- #
1255
- # @param [String, Array<String>] keys one or more keys to perform the
1256
- # blocking pop on
1257
- # @param [Hash] options
1258
- # - `:timeout => Integer`: timeout in seconds, defaults to no timeout
1259
- #
1260
- # @return [nil, [String, String]]
1261
- # - `nil` when the operation timed out
1262
- # - tuple of the list that was popped from and element was popped otherwise
1263
- #
1264
- # @see #blpop
1265
- def brpop(*args)
1266
- _bpop(:brpop, args)
1267
- end
1268
-
1269
- # Pop a value from a list, push it to another list and return it; or block
1270
- # until one is available.
1271
- #
1272
- # @param [String] source source key
1273
- # @param [String] destination destination key
1274
- # @param [Hash] options
1275
- # - `:timeout => Integer`: timeout in seconds, defaults to no timeout
1276
- #
1277
- # @return [nil, String]
1278
- # - `nil` when the operation timed out
1279
- # - the element was popped and pushed otherwise
1280
- def brpoplpush(source, destination, deprecated_timeout = 0, timeout: deprecated_timeout)
1281
- synchronize do |client|
1282
- command = [:brpoplpush, source, destination, timeout]
1283
- timeout += client.timeout if timeout > 0
1284
- client.call_with_timeout(command, timeout)
1285
- end
1286
- end
1287
-
1288
- # Get an element from a list by its index.
1289
- #
1290
- # @param [String] key
1291
- # @param [Integer] index
1292
- # @return [String]
1293
- def lindex(key, index)
1294
- synchronize do |client|
1295
- client.call([:lindex, key, index])
1296
- end
1297
- end
1298
-
1299
- # Insert an element before or after another element in a list.
1300
- #
1301
- # @param [String] key
1302
- # @param [String, Symbol] where `BEFORE` or `AFTER`
1303
- # @param [String] pivot reference element
1304
- # @param [String] value
1305
- # @return [Integer] length of the list after the insert operation, or `-1`
1306
- # when the element `pivot` was not found
1307
- def linsert(key, where, pivot, value)
1308
- synchronize do |client|
1309
- client.call([:linsert, key, where, pivot, value])
1310
- end
1311
- end
1312
-
1313
- # Get a range of elements from a list.
1314
- #
1315
- # @param [String] key
1316
- # @param [Integer] start start index
1317
- # @param [Integer] stop stop index
1318
- # @return [Array<String>]
1319
- def lrange(key, start, stop)
1320
- synchronize do |client|
1321
- client.call([:lrange, key, start, stop])
1322
- end
1323
- end
1324
-
1325
- # Remove elements from a list.
1326
- #
1327
- # @param [String] key
1328
- # @param [Integer] count number of elements to remove. Use a positive
1329
- # value to remove the first `count` occurrences of `value`. A negative
1330
- # value to remove the last `count` occurrences of `value`. Or zero, to
1331
- # remove all occurrences of `value` from the list.
1332
- # @param [String] value
1333
- # @return [Integer] the number of removed elements
1334
- def lrem(key, count, value)
1335
- synchronize do |client|
1336
- client.call([:lrem, key, count, value])
1337
- end
1338
- end
1339
-
1340
- # Set the value of an element in a list by its index.
1341
- #
1342
- # @param [String] key
1343
- # @param [Integer] index
1344
- # @param [String] value
1345
- # @return [String] `OK`
1346
- def lset(key, index, value)
1347
- synchronize do |client|
1348
- client.call([:lset, key, index, value])
1349
- end
1350
- end
1351
-
1352
- # Trim a list to the specified range.
1353
- #
1354
- # @param [String] key
1355
- # @param [Integer] start start index
1356
- # @param [Integer] stop stop index
1357
- # @return [String] `OK`
1358
- def ltrim(key, start, stop)
1359
- synchronize do |client|
1360
- client.call([:ltrim, key, start, stop])
1361
- end
1362
- end
1363
-
1364
- # Get the number of members in a set.
1365
- #
1366
- # @param [String] key
1367
- # @return [Integer]
1368
- def scard(key)
1369
- synchronize do |client|
1370
- client.call([:scard, key])
1371
- end
1372
- end
1373
-
1374
- # Add one or more members to a set.
1375
- #
1376
- # @param [String] key
1377
- # @param [String, Array<String>] member one member, or array of members
1378
- # @return [Boolean, Integer] `Boolean` when a single member is specified,
1379
- # holding whether or not adding the member succeeded, or `Integer` when an
1380
- # array of members is specified, holding the number of members that were
1381
- # successfully added
1382
- def sadd(key, member)
1383
- synchronize do |client|
1384
- client.call([:sadd, key, member]) do |reply|
1385
- if member.is_a? Array
1386
- # Variadic: return integer
1387
- reply
1388
- else
1389
- # Single argument: return boolean
1390
- Boolify.call(reply)
1391
- end
1392
- end
1393
- end
1394
- end
1395
-
1396
- # Remove one or more members from a set.
1397
- #
1398
- # @param [String] key
1399
- # @param [String, Array<String>] member one member, or array of members
1400
- # @return [Boolean, Integer] `Boolean` when a single member is specified,
1401
- # holding whether or not removing the member succeeded, or `Integer` when an
1402
- # array of members is specified, holding the number of members that were
1403
- # successfully removed
1404
- def srem(key, member)
1405
- synchronize do |client|
1406
- client.call([:srem, key, member]) do |reply|
1407
- if member.is_a? Array
1408
- # Variadic: return integer
1409
- reply
1410
- else
1411
- # Single argument: return boolean
1412
- Boolify.call(reply)
1413
- end
1414
- end
1415
- end
1416
- end
1417
-
1418
- # Remove and return one or more random member from a set.
1419
- #
1420
- # @param [String] key
1421
- # @return [String]
1422
- # @param [Integer] count
1423
- def spop(key, count = nil)
1424
- synchronize do |client|
1425
- if count.nil?
1426
- client.call([:spop, key])
1427
- else
1428
- client.call([:spop, key, count])
1429
- end
1430
- end
1431
- end
1432
-
1433
- # Get one or more random members from a set.
1434
- #
1435
- # @param [String] key
1436
- # @param [Integer] count
1437
- # @return [String]
1438
- def srandmember(key, count = nil)
1439
- synchronize do |client|
1440
- if count.nil?
1441
- client.call([:srandmember, key])
1442
- else
1443
- client.call([:srandmember, key, count])
1444
- end
1445
- end
1446
- end
1447
-
1448
- # Move a member from one set to another.
1449
- #
1450
- # @param [String] source source key
1451
- # @param [String] destination destination key
1452
- # @param [String] member member to move from `source` to `destination`
1453
- # @return [Boolean]
1454
- def smove(source, destination, member)
1455
- synchronize do |client|
1456
- client.call([:smove, source, destination, member], &Boolify)
1457
- end
1458
- end
1459
-
1460
- # Determine if a given value is a member of a set.
1461
- #
1462
- # @param [String] key
1463
- # @param [String] member
1464
- # @return [Boolean]
1465
- def sismember(key, member)
1466
- synchronize do |client|
1467
- client.call([:sismember, key, member], &Boolify)
1468
- end
1469
- end
1470
-
1471
- # Get all the members in a set.
1472
- #
1473
- # @param [String] key
1474
- # @return [Array<String>]
1475
- def smembers(key)
1476
- synchronize do |client|
1477
- client.call([:smembers, key])
1478
- end
1479
- end
1480
-
1481
- # Subtract multiple sets.
1482
- #
1483
- # @param [String, Array<String>] keys keys pointing to sets to subtract
1484
- # @return [Array<String>] members in the difference
1485
- def sdiff(*keys)
1486
- synchronize do |client|
1487
- client.call([:sdiff, *keys])
1488
- end
1489
- end
1490
-
1491
- # Subtract multiple sets and store the resulting set in a key.
1492
- #
1493
- # @param [String] destination destination key
1494
- # @param [String, Array<String>] keys keys pointing to sets to subtract
1495
- # @return [Integer] number of elements in the resulting set
1496
- def sdiffstore(destination, *keys)
1497
- synchronize do |client|
1498
- client.call([:sdiffstore, destination, *keys])
1499
- end
1500
- end
1501
-
1502
- # Intersect multiple sets.
1503
- #
1504
- # @param [String, Array<String>] keys keys pointing to sets to intersect
1505
- # @return [Array<String>] members in the intersection
1506
- def sinter(*keys)
1507
- synchronize do |client|
1508
- client.call([:sinter, *keys])
1509
- end
1510
- end
1511
-
1512
- # Intersect multiple sets and store the resulting set in a key.
1513
- #
1514
- # @param [String] destination destination key
1515
- # @param [String, Array<String>] keys keys pointing to sets to intersect
1516
- # @return [Integer] number of elements in the resulting set
1517
- def sinterstore(destination, *keys)
1518
- synchronize do |client|
1519
- client.call([:sinterstore, destination, *keys])
1520
- end
1521
- end
1522
-
1523
- # Add multiple sets.
1524
- #
1525
- # @param [String, Array<String>] keys keys pointing to sets to unify
1526
- # @return [Array<String>] members in the union
1527
- def sunion(*keys)
1528
- synchronize do |client|
1529
- client.call([:sunion, *keys])
1530
- end
1531
- end
1532
-
1533
- # Add multiple sets and store the resulting set in a key.
1534
- #
1535
- # @param [String] destination destination key
1536
- # @param [String, Array<String>] keys keys pointing to sets to unify
1537
- # @return [Integer] number of elements in the resulting set
1538
- def sunionstore(destination, *keys)
1539
- synchronize do |client|
1540
- client.call([:sunionstore, destination, *keys])
1541
- end
1542
- end
1543
-
1544
- # Get the number of members in a sorted set.
1545
- #
1546
- # @example
1547
- # redis.zcard("zset")
1548
- # # => 4
1549
- #
1550
- # @param [String] key
1551
- # @return [Integer]
1552
- def zcard(key)
1553
- synchronize do |client|
1554
- client.call([:zcard, key])
1555
- end
1556
- end
1557
-
1558
- # Add one or more members to a sorted set, or update the score for members
1559
- # that already exist.
1560
- #
1561
- # @example Add a single `[score, member]` pair to a sorted set
1562
- # redis.zadd("zset", 32.0, "member")
1563
- # @example Add an array of `[score, member]` pairs to a sorted set
1564
- # redis.zadd("zset", [[32.0, "a"], [64.0, "b"]])
1565
- #
1566
- # @param [String] key
1567
- # @param [[Float, String], Array<[Float, String]>] args
1568
- # - a single `[score, member]` pair
1569
- # - an array of `[score, member]` pairs
1570
- # @param [Hash] options
1571
- # - `:xx => true`: Only update elements that already exist (never
1572
- # add elements)
1573
- # - `:nx => true`: Don't update already existing elements (always
1574
- # add new elements)
1575
- # - `:ch => true`: Modify the return value from the number of new
1576
- # elements added, to the total number of elements changed (CH is an
1577
- # abbreviation of changed); changed elements are new elements added
1578
- # and elements already existing for which the score was updated
1579
- # - `:incr => true`: When this option is specified ZADD acts like
1580
- # ZINCRBY; only one score-element pair can be specified in this mode
1581
- #
1582
- # @return [Boolean, Integer, Float]
1583
- # - `Boolean` when a single pair is specified, holding whether or not it was
1584
- # **added** to the sorted set.
1585
- # - `Integer` when an array of pairs is specified, holding the number of
1586
- # pairs that were **added** to the sorted set.
1587
- # - `Float` when option :incr is specified, holding the score of the member
1588
- # after incrementing it.
1589
- def zadd(key, *args, nx: nil, xx: nil, ch: nil, incr: nil)
1590
- command = [:zadd, key]
1591
- command << "NX" if nx
1592
- command << "XX" if xx
1593
- command << "CH" if ch
1594
- command << "INCR" if incr
1595
-
1596
- synchronize do |client|
1597
- if args.size == 1 && args[0].is_a?(Array)
1598
- # Variadic: return float if INCR, integer if !INCR
1599
- client.call(command + args[0], &(incr ? Floatify : nil))
1600
- elsif args.size == 2
1601
- # Single pair: return float if INCR, boolean if !INCR
1602
- client.call(command + args, &(incr ? Floatify : Boolify))
1603
- else
1604
- raise ArgumentError, "wrong number of arguments"
1605
- end
1606
- end
1607
- end
1608
-
1609
- # Increment the score of a member in a sorted set.
1610
- #
1611
- # @example
1612
- # redis.zincrby("zset", 32.0, "a")
1613
- # # => 64.0
1614
- #
1615
- # @param [String] key
1616
- # @param [Float] increment
1617
- # @param [String] member
1618
- # @return [Float] score of the member after incrementing it
1619
- def zincrby(key, increment, member)
1620
- synchronize do |client|
1621
- client.call([:zincrby, key, increment, member], &Floatify)
1622
- end
1623
- end
1624
-
1625
- # Remove one or more members from a sorted set.
1626
- #
1627
- # @example Remove a single member from a sorted set
1628
- # redis.zrem("zset", "a")
1629
- # @example Remove an array of members from a sorted set
1630
- # redis.zrem("zset", ["a", "b"])
1631
- #
1632
- # @param [String] key
1633
- # @param [String, Array<String>] member
1634
- # - a single member
1635
- # - an array of members
1636
- #
1637
- # @return [Boolean, Integer]
1638
- # - `Boolean` when a single member is specified, holding whether or not it
1639
- # was removed from the sorted set
1640
- # - `Integer` when an array of pairs is specified, holding the number of
1641
- # members that were removed to the sorted set
1642
- def zrem(key, member)
1643
- synchronize do |client|
1644
- client.call([:zrem, key, member]) do |reply|
1645
- if member.is_a? Array
1646
- # Variadic: return integer
1647
- reply
1648
- else
1649
- # Single argument: return boolean
1650
- Boolify.call(reply)
1651
- end
1652
- end
1653
- end
1654
- end
1655
-
1656
- # Removes and returns up to count members with the highest scores in the sorted set stored at key.
1657
- #
1658
- # @example Popping a member
1659
- # redis.zpopmax('zset')
1660
- # #=> ['b', 2.0]
1661
- # @example With count option
1662
- # redis.zpopmax('zset', 2)
1663
- # #=> [['b', 2.0], ['a', 1.0]]
1664
- #
1665
- # @params key [String] a key of the sorted set
1666
- # @params count [Integer] a number of members
1667
- #
1668
- # @return [Array<String, Float>] element and score pair if count is not specified
1669
- # @return [Array<Array<String, Float>>] list of popped elements and scores
1670
- def zpopmax(key, count = nil)
1671
- synchronize do |client|
1672
- members = client.call([:zpopmax, key, count].compact, &FloatifyPairs)
1673
- count.to_i > 1 ? members : members.first
1674
- end
1675
- end
1676
-
1677
- # Removes and returns up to count members with the lowest scores in the sorted set stored at key.
1678
- #
1679
- # @example Popping a member
1680
- # redis.zpopmin('zset')
1681
- # #=> ['a', 1.0]
1682
- # @example With count option
1683
- # redis.zpopmin('zset', 2)
1684
- # #=> [['a', 1.0], ['b', 2.0]]
1685
- #
1686
- # @params key [String] a key of the sorted set
1687
- # @params count [Integer] a number of members
1688
- #
1689
- # @return [Array<String, Float>] element and score pair if count is not specified
1690
- # @return [Array<Array<String, Float>>] list of popped elements and scores
1691
- def zpopmin(key, count = nil)
1692
- synchronize do |client|
1693
- members = client.call([:zpopmin, key, count].compact, &FloatifyPairs)
1694
- count.to_i > 1 ? members : members.first
1695
- end
1696
- end
1697
-
1698
- # Removes and returns up to count members with the highest scores in the sorted set stored at keys,
1699
- # or block until one is available.
1700
- #
1701
- # @example Popping a member from a sorted set
1702
- # redis.bzpopmax('zset', 1)
1703
- # #=> ['zset', 'b', 2.0]
1704
- # @example Popping a member from multiple sorted sets
1705
- # redis.bzpopmax('zset1', 'zset2', 1)
1706
- # #=> ['zset1', 'b', 2.0]
1707
- #
1708
- # @params keys [Array<String>] one or multiple keys of the sorted sets
1709
- # @params timeout [Integer] the maximum number of seconds to block
1710
- #
1711
- # @return [Array<String, String, Float>] a touple of key, member and score
1712
- # @return [nil] when no element could be popped and the timeout expired
1713
- def bzpopmax(*args)
1714
- _bpop(:bzpopmax, args) do |reply|
1715
- reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
1716
- end
1717
- end
1718
-
1719
- # Removes and returns up to count members with the lowest scores in the sorted set stored at keys,
1720
- # or block until one is available.
1721
- #
1722
- # @example Popping a member from a sorted set
1723
- # redis.bzpopmin('zset', 1)
1724
- # #=> ['zset', 'a', 1.0]
1725
- # @example Popping a member from multiple sorted sets
1726
- # redis.bzpopmin('zset1', 'zset2', 1)
1727
- # #=> ['zset1', 'a', 1.0]
1728
- #
1729
- # @params keys [Array<String>] one or multiple keys of the sorted sets
1730
- # @params timeout [Integer] the maximum number of seconds to block
1731
- #
1732
- # @return [Array<String, String, Float>] a touple of key, member and score
1733
- # @return [nil] when no element could be popped and the timeout expired
1734
- def bzpopmin(*args)
1735
- _bpop(:bzpopmin, args) do |reply|
1736
- reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
1737
- end
1738
- end
1739
-
1740
- # Get the score associated with the given member in a sorted set.
1741
- #
1742
- # @example Get the score for member "a"
1743
- # redis.zscore("zset", "a")
1744
- # # => 32.0
1745
- #
1746
- # @param [String] key
1747
- # @param [String] member
1748
- # @return [Float] score of the member
1749
- def zscore(key, member)
1750
- synchronize do |client|
1751
- client.call([:zscore, key, member], &Floatify)
1752
- end
1753
- end
1754
-
1755
- # Return a range of members in a sorted set, by index.
1756
- #
1757
- # @example Retrieve all members from a sorted set
1758
- # redis.zrange("zset", 0, -1)
1759
- # # => ["a", "b"]
1760
- # @example Retrieve all members and their scores from a sorted set
1761
- # redis.zrange("zset", 0, -1, :with_scores => true)
1762
- # # => [["a", 32.0], ["b", 64.0]]
1763
- #
1764
- # @param [String] key
1765
- # @param [Integer] start start index
1766
- # @param [Integer] stop stop index
1767
- # @param [Hash] options
1768
- # - `:with_scores => true`: include scores in output
1769
- #
1770
- # @return [Array<String>, Array<[String, Float]>]
1771
- # - when `:with_scores` is not specified, an array of members
1772
- # - when `:with_scores` is specified, an array with `[member, score]` pairs
1773
- def zrange(key, start, stop, withscores: false, with_scores: withscores)
1774
- args = [:zrange, key, start, stop]
1775
-
1776
- if with_scores
1777
- args << "WITHSCORES"
1778
- block = FloatifyPairs
1779
- end
1780
-
1781
- synchronize do |client|
1782
- client.call(args, &block)
1783
- end
1784
- end
1785
-
1786
- # Return a range of members in a sorted set, by index, with scores ordered
1787
- # from high to low.
1788
- #
1789
- # @example Retrieve all members from a sorted set
1790
- # redis.zrevrange("zset", 0, -1)
1791
- # # => ["b", "a"]
1792
- # @example Retrieve all members and their scores from a sorted set
1793
- # redis.zrevrange("zset", 0, -1, :with_scores => true)
1794
- # # => [["b", 64.0], ["a", 32.0]]
1795
- #
1796
- # @see #zrange
1797
- def zrevrange(key, start, stop, withscores: false, with_scores: withscores)
1798
- args = [:zrevrange, key, start, stop]
1799
-
1800
- if with_scores
1801
- args << "WITHSCORES"
1802
- block = FloatifyPairs
1803
- end
1804
-
1805
- synchronize do |client|
1806
- client.call(args, &block)
1807
- end
1808
- end
1809
-
1810
- # Determine the index of a member in a sorted set.
1811
- #
1812
- # @param [String] key
1813
- # @param [String] member
1814
- # @return [Integer]
1815
- def zrank(key, member)
1816
- synchronize do |client|
1817
- client.call([:zrank, key, member])
1818
- end
1819
- end
1820
-
1821
- # Determine the index of a member in a sorted set, with scores ordered from
1822
- # high to low.
1823
- #
1824
- # @param [String] key
1825
- # @param [String] member
1826
- # @return [Integer]
1827
- def zrevrank(key, member)
1828
- synchronize do |client|
1829
- client.call([:zrevrank, key, member])
1830
- end
1831
- end
1832
-
1833
- # Remove all members in a sorted set within the given indexes.
1834
- #
1835
- # @example Remove first 5 members
1836
- # redis.zremrangebyrank("zset", 0, 4)
1837
- # # => 5
1838
- # @example Remove last 5 members
1839
- # redis.zremrangebyrank("zset", -5, -1)
1840
- # # => 5
1841
- #
1842
- # @param [String] key
1843
- # @param [Integer] start start index
1844
- # @param [Integer] stop stop index
1845
- # @return [Integer] number of members that were removed
1846
- def zremrangebyrank(key, start, stop)
1847
- synchronize do |client|
1848
- client.call([:zremrangebyrank, key, start, stop])
1849
- end
1850
- end
1851
-
1852
- # Count the members, with the same score in a sorted set, within the given lexicographical range.
1853
- #
1854
- # @example Count members matching a
1855
- # redis.zlexcount("zset", "[a", "[a\xff")
1856
- # # => 1
1857
- # @example Count members matching a-z
1858
- # redis.zlexcount("zset", "[a", "[z\xff")
1859
- # # => 26
1860
- #
1861
- # @param [String] key
1862
- # @param [String] min
1863
- # - inclusive minimum is specified by prefixing `(`
1864
- # - exclusive minimum is specified by prefixing `[`
1865
- # @param [String] max
1866
- # - inclusive maximum is specified by prefixing `(`
1867
- # - exclusive maximum is specified by prefixing `[`
1868
- #
1869
- # @return [Integer] number of members within the specified lexicographical range
1870
- def zlexcount(key, min, max)
1871
- synchronize do |client|
1872
- client.call([:zlexcount, key, min, max])
1873
- end
1874
- end
1875
-
1876
- # Return a range of members with the same score in a sorted set, by lexicographical ordering
1877
- #
1878
- # @example Retrieve members matching a
1879
- # redis.zrangebylex("zset", "[a", "[a\xff")
1880
- # # => ["aaren", "aarika", "abagael", "abby"]
1881
- # @example Retrieve the first 2 members matching a
1882
- # redis.zrangebylex("zset", "[a", "[a\xff", :limit => [0, 2])
1883
- # # => ["aaren", "aarika"]
1884
- #
1885
- # @param [String] key
1886
- # @param [String] min
1887
- # - inclusive minimum is specified by prefixing `(`
1888
- # - exclusive minimum is specified by prefixing `[`
1889
- # @param [String] max
1890
- # - inclusive maximum is specified by prefixing `(`
1891
- # - exclusive maximum is specified by prefixing `[`
1892
- # @param [Hash] options
1893
- # - `:limit => [offset, count]`: skip `offset` members, return a maximum of
1894
- # `count` members
1895
- #
1896
- # @return [Array<String>, Array<[String, Float]>]
1897
- def zrangebylex(key, min, max, limit: nil)
1898
- args = [:zrangebylex, key, min, max]
1899
-
1900
- if limit
1901
- args << "LIMIT"
1902
- args.concat(limit)
1903
- end
1904
-
1905
- synchronize do |client|
1906
- client.call(args)
1907
- end
1908
- end
1909
-
1910
- # Return a range of members with the same score in a sorted set, by reversed lexicographical ordering.
1911
- # Apart from the reversed ordering, #zrevrangebylex is similar to #zrangebylex.
1912
- #
1913
- # @example Retrieve members matching a
1914
- # redis.zrevrangebylex("zset", "[a", "[a\xff")
1915
- # # => ["abbygail", "abby", "abagael", "aaren"]
1916
- # @example Retrieve the last 2 members matching a
1917
- # redis.zrevrangebylex("zset", "[a", "[a\xff", :limit => [0, 2])
1918
- # # => ["abbygail", "abby"]
1919
- #
1920
- # @see #zrangebylex
1921
- def zrevrangebylex(key, max, min, limit: nil)
1922
- args = [:zrevrangebylex, key, max, min]
1923
-
1924
- if limit
1925
- args << "LIMIT"
1926
- args.concat(limit)
1927
- end
1928
-
1929
- synchronize do |client|
1930
- client.call(args)
1931
- end
1932
- end
1933
-
1934
- # Return a range of members in a sorted set, by score.
1935
- #
1936
- # @example Retrieve members with score `>= 5` and `< 100`
1937
- # redis.zrangebyscore("zset", "5", "(100")
1938
- # # => ["a", "b"]
1939
- # @example Retrieve the first 2 members with score `>= 0`
1940
- # redis.zrangebyscore("zset", "0", "+inf", :limit => [0, 2])
1941
- # # => ["a", "b"]
1942
- # @example Retrieve members and their scores with scores `> 5`
1943
- # redis.zrangebyscore("zset", "(5", "+inf", :with_scores => true)
1944
- # # => [["a", 32.0], ["b", 64.0]]
1945
- #
1946
- # @param [String] key
1947
- # @param [String] min
1948
- # - inclusive minimum score is specified verbatim
1949
- # - exclusive minimum score is specified by prefixing `(`
1950
- # @param [String] max
1951
- # - inclusive maximum score is specified verbatim
1952
- # - exclusive maximum score is specified by prefixing `(`
1953
- # @param [Hash] options
1954
- # - `:with_scores => true`: include scores in output
1955
- # - `:limit => [offset, count]`: skip `offset` members, return a maximum of
1956
- # `count` members
1957
- #
1958
- # @return [Array<String>, Array<[String, Float]>]
1959
- # - when `:with_scores` is not specified, an array of members
1960
- # - when `:with_scores` is specified, an array with `[member, score]` pairs
1961
- def zrangebyscore(key, min, max, withscores: false, with_scores: withscores, limit: nil)
1962
- args = [:zrangebyscore, key, min, max]
1963
-
1964
- if with_scores
1965
- args << "WITHSCORES"
1966
- block = FloatifyPairs
1967
- end
1968
-
1969
- if limit
1970
- args << "LIMIT"
1971
- args.concat(limit)
1972
- end
1973
-
1974
- synchronize do |client|
1975
- client.call(args, &block)
1976
- end
1977
- end
1978
-
1979
- # Return a range of members in a sorted set, by score, with scores ordered
1980
- # from high to low.
1981
- #
1982
- # @example Retrieve members with score `< 100` and `>= 5`
1983
- # redis.zrevrangebyscore("zset", "(100", "5")
1984
- # # => ["b", "a"]
1985
- # @example Retrieve the first 2 members with score `<= 0`
1986
- # redis.zrevrangebyscore("zset", "0", "-inf", :limit => [0, 2])
1987
- # # => ["b", "a"]
1988
- # @example Retrieve members and their scores with scores `> 5`
1989
- # redis.zrevrangebyscore("zset", "+inf", "(5", :with_scores => true)
1990
- # # => [["b", 64.0], ["a", 32.0]]
1991
- #
1992
- # @see #zrangebyscore
1993
- def zrevrangebyscore(key, max, min, withscores: false, with_scores: withscores, limit: nil)
1994
- args = [:zrevrangebyscore, key, max, min]
1995
-
1996
- if with_scores
1997
- args << "WITHSCORES"
1998
- block = FloatifyPairs
1999
- end
2000
-
2001
- if limit
2002
- args << "LIMIT"
2003
- args.concat(limit)
2004
- end
2005
-
2006
- synchronize do |client|
2007
- client.call(args, &block)
2008
- end
2009
- end
2010
-
2011
- # Remove all members in a sorted set within the given scores.
2012
- #
2013
- # @example Remove members with score `>= 5` and `< 100`
2014
- # redis.zremrangebyscore("zset", "5", "(100")
2015
- # # => 2
2016
- # @example Remove members with scores `> 5`
2017
- # redis.zremrangebyscore("zset", "(5", "+inf")
2018
- # # => 2
2019
- #
2020
- # @param [String] key
2021
- # @param [String] min
2022
- # - inclusive minimum score is specified verbatim
2023
- # - exclusive minimum score is specified by prefixing `(`
2024
- # @param [String] max
2025
- # - inclusive maximum score is specified verbatim
2026
- # - exclusive maximum score is specified by prefixing `(`
2027
- # @return [Integer] number of members that were removed
2028
- def zremrangebyscore(key, min, max)
2029
- synchronize do |client|
2030
- client.call([:zremrangebyscore, key, min, max])
2031
- end
2032
- end
2033
-
2034
- # Count the members in a sorted set with scores within the given values.
2035
- #
2036
- # @example Count members with score `>= 5` and `< 100`
2037
- # redis.zcount("zset", "5", "(100")
2038
- # # => 2
2039
- # @example Count members with scores `> 5`
2040
- # redis.zcount("zset", "(5", "+inf")
2041
- # # => 2
2042
- #
2043
- # @param [String] key
2044
- # @param [String] min
2045
- # - inclusive minimum score is specified verbatim
2046
- # - exclusive minimum score is specified by prefixing `(`
2047
- # @param [String] max
2048
- # - inclusive maximum score is specified verbatim
2049
- # - exclusive maximum score is specified by prefixing `(`
2050
- # @return [Integer] number of members in within the specified range
2051
- def zcount(key, min, max)
2052
- synchronize do |client|
2053
- client.call([:zcount, key, min, max])
2054
- end
2055
- end
2056
-
2057
- # Intersect multiple sorted sets and store the resulting sorted set in a new
2058
- # key.
2059
- #
2060
- # @example Compute the intersection of `2*zsetA` with `1*zsetB`, summing their scores
2061
- # redis.zinterstore("zsetC", ["zsetA", "zsetB"], :weights => [2.0, 1.0], :aggregate => "sum")
2062
- # # => 4
2063
- #
2064
- # @param [String] destination destination key
2065
- # @param [Array<String>] keys source keys
2066
- # @param [Hash] options
2067
- # - `:weights => [Float, Float, ...]`: weights to associate with source
2068
- # sorted sets
2069
- # - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
2070
- # @return [Integer] number of elements in the resulting sorted set
2071
- def zinterstore(destination, keys, weights: nil, aggregate: nil)
2072
- args = [:zinterstore, destination, keys.size, *keys]
2073
-
2074
- if weights
2075
- args << "WEIGHTS"
2076
- args.concat(weights)
2077
- end
2078
-
2079
- args << "AGGREGATE" << aggregate if aggregate
2080
-
2081
- synchronize do |client|
2082
- client.call(args)
2083
- end
2084
- end
2085
-
2086
- # Add multiple sorted sets and store the resulting sorted set in a new key.
2087
- #
2088
- # @example Compute the union of `2*zsetA` with `1*zsetB`, summing their scores
2089
- # redis.zunionstore("zsetC", ["zsetA", "zsetB"], :weights => [2.0, 1.0], :aggregate => "sum")
2090
- # # => 8
2091
- #
2092
- # @param [String] destination destination key
2093
- # @param [Array<String>] keys source keys
2094
- # @param [Hash] options
2095
- # - `:weights => [Float, Float, ...]`: weights to associate with source
2096
- # sorted sets
2097
- # - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
2098
- # @return [Integer] number of elements in the resulting sorted set
2099
- def zunionstore(destination, keys, weights: nil, aggregate: nil)
2100
- args = [:zunionstore, destination, keys.size, *keys]
2101
-
2102
- if weights
2103
- args << "WEIGHTS"
2104
- args.concat(weights)
2105
- end
2106
-
2107
- args << "AGGREGATE" << aggregate if aggregate
2108
-
2109
- synchronize do |client|
2110
- client.call(args)
2111
- end
2112
- end
2113
-
2114
- # Get the number of fields in a hash.
2115
- #
2116
- # @param [String] key
2117
- # @return [Integer] number of fields in the hash
2118
- def hlen(key)
2119
- synchronize do |client|
2120
- client.call([:hlen, key])
2121
- end
2122
- end
2123
-
2124
- # Set one or more hash values.
2125
- #
2126
- # @example
2127
- # redis.hset("hash", "f1", "v1", "f2", "v2") # => 2
2128
- # redis.hset("hash", { "f1" => "v1", "f2" => "v2" }) # => 2
2129
- #
2130
- # @param [String] key
2131
- # @param [Array<String> | Hash<String, String>] attrs array or hash of fields and values
2132
- # @return [Integer] The number of fields that were added to the hash
2133
- def hset(key, *attrs)
2134
- attrs = attrs.first.flatten if attrs.size == 1 && attrs.first.is_a?(Hash)
2135
-
2136
- synchronize do |client|
2137
- client.call([:hset, key, *attrs])
2138
- end
2139
- end
2140
-
2141
- # Set the value of a hash field, only if the field does not exist.
2142
- #
2143
- # @param [String] key
2144
- # @param [String] field
2145
- # @param [String] value
2146
- # @return [Boolean] whether or not the field was **added** to the hash
2147
- def hsetnx(key, field, value)
2148
- synchronize do |client|
2149
- client.call([:hsetnx, key, field, value], &Boolify)
2150
- end
2151
- end
2152
-
2153
- # Set one or more hash values.
2154
- #
2155
- # @example
2156
- # redis.hmset("hash", "f1", "v1", "f2", "v2")
2157
- # # => "OK"
2158
- #
2159
- # @param [String] key
2160
- # @param [Array<String>] attrs array of fields and values
2161
- # @return [String] `"OK"`
2162
- #
2163
- # @see #mapped_hmset
2164
- def hmset(key, *attrs)
2165
- synchronize do |client|
2166
- client.call([:hmset, key] + attrs)
2167
- end
2168
- end
2169
-
2170
- # Set one or more hash values.
2171
- #
2172
- # @example
2173
- # redis.mapped_hmset("hash", { "f1" => "v1", "f2" => "v2" })
2174
- # # => "OK"
2175
- #
2176
- # @param [String] key
2177
- # @param [Hash] hash a non-empty hash with fields mapping to values
2178
- # @return [String] `"OK"`
2179
- #
2180
- # @see #hmset
2181
- def mapped_hmset(key, hash)
2182
- hmset(key, hash.to_a.flatten)
2183
- end
2184
-
2185
- # Get the value of a hash field.
2186
- #
2187
- # @param [String] key
2188
- # @param [String] field
2189
- # @return [String]
2190
- def hget(key, field)
2191
- synchronize do |client|
2192
- client.call([:hget, key, field])
2193
- end
2194
- end
2195
-
2196
- # Get the values of all the given hash fields.
2197
- #
2198
- # @example
2199
- # redis.hmget("hash", "f1", "f2")
2200
- # # => ["v1", "v2"]
2201
- #
2202
- # @param [String] key
2203
- # @param [Array<String>] fields array of fields
2204
- # @return [Array<String>] an array of values for the specified fields
2205
- #
2206
- # @see #mapped_hmget
2207
- def hmget(key, *fields, &blk)
2208
- synchronize do |client|
2209
- client.call([:hmget, key] + fields, &blk)
2210
- end
2211
- end
2212
-
2213
- # Get the values of all the given hash fields.
2214
- #
2215
- # @example
2216
- # redis.mapped_hmget("hash", "f1", "f2")
2217
- # # => { "f1" => "v1", "f2" => "v2" }
2218
- #
2219
- # @param [String] key
2220
- # @param [Array<String>] fields array of fields
2221
- # @return [Hash] a hash mapping the specified fields to their values
2222
- #
2223
- # @see #hmget
2224
- def mapped_hmget(key, *fields)
2225
- hmget(key, *fields) do |reply|
2226
- if reply.is_a?(Array)
2227
- Hash[fields.zip(reply)]
2228
- else
2229
- reply
2230
- end
2231
- end
2232
- end
2233
-
2234
- # Delete one or more hash fields.
2235
- #
2236
- # @param [String] key
2237
- # @param [String, Array<String>] field
2238
- # @return [Integer] the number of fields that were removed from the hash
2239
- def hdel(key, *fields)
2240
- synchronize do |client|
2241
- client.call([:hdel, key, *fields])
2242
- end
2243
- end
2244
-
2245
- # Determine if a hash field exists.
2246
- #
2247
- # @param [String] key
2248
- # @param [String] field
2249
- # @return [Boolean] whether or not the field exists in the hash
2250
- def hexists(key, field)
2251
- synchronize do |client|
2252
- client.call([:hexists, key, field], &Boolify)
2253
- end
2254
- end
2255
-
2256
- # Increment the integer value of a hash field by the given integer number.
2257
- #
2258
- # @param [String] key
2259
- # @param [String] field
2260
- # @param [Integer] increment
2261
- # @return [Integer] value of the field after incrementing it
2262
- def hincrby(key, field, increment)
2263
- synchronize do |client|
2264
- client.call([:hincrby, key, field, increment])
2265
- end
2266
- end
2267
-
2268
- # Increment the numeric value of a hash field by the given float number.
2269
- #
2270
- # @param [String] key
2271
- # @param [String] field
2272
- # @param [Float] increment
2273
- # @return [Float] value of the field after incrementing it
2274
- def hincrbyfloat(key, field, increment)
2275
- synchronize do |client|
2276
- client.call([:hincrbyfloat, key, field, increment], &Floatify)
2277
- end
2278
- end
2279
-
2280
- # Get all the fields in a hash.
2281
- #
2282
- # @param [String] key
2283
- # @return [Array<String>]
2284
- def hkeys(key)
2285
- synchronize do |client|
2286
- client.call([:hkeys, key])
2287
- end
2288
- end
2289
-
2290
- # Get all the values in a hash.
2291
- #
2292
- # @param [String] key
2293
- # @return [Array<String>]
2294
- def hvals(key)
2295
- synchronize do |client|
2296
- client.call([:hvals, key])
2297
- end
2298
- end
2299
-
2300
- # Get all the fields and values in a hash.
2301
- #
2302
- # @param [String] key
2303
- # @return [Hash<String, String>]
2304
- def hgetall(key)
2305
- synchronize do |client|
2306
- client.call([:hgetall, key], &Hashify)
2307
- end
2308
- end
2309
-
2310
- # Post a message to a channel.
2311
- def publish(channel, message)
2312
- synchronize do |client|
2313
- client.call([:publish, channel, message])
2314
- end
2315
- end
2316
-
2317
- def subscribed?
2318
- synchronize do |client|
2319
- client.is_a? SubscribedClient
2320
- end
2321
- end
2322
-
2323
- # Listen for messages published to the given channels.
2324
- def subscribe(*channels, &block)
2325
- synchronize do |_client|
2326
- _subscription(:subscribe, 0, channels, block)
2327
- end
2328
- end
2329
-
2330
- # Listen for messages published to the given channels. Throw a timeout error
2331
- # if there is no messages for a timeout period.
2332
- def subscribe_with_timeout(timeout, *channels, &block)
2333
- synchronize do |_client|
2334
- _subscription(:subscribe_with_timeout, timeout, channels, block)
2335
- end
2336
- end
2337
-
2338
- # Stop listening for messages posted to the given channels.
2339
- def unsubscribe(*channels)
2340
- synchronize do |client|
2341
- raise "Can't unsubscribe if not subscribed." unless subscribed?
2342
-
2343
- client.unsubscribe(*channels)
2344
- end
2345
- end
2346
-
2347
- # Listen for messages published to channels matching the given patterns.
2348
- def psubscribe(*channels, &block)
2349
- synchronize do |_client|
2350
- _subscription(:psubscribe, 0, channels, block)
2351
- end
2352
- end
2353
-
2354
- # Listen for messages published to channels matching the given patterns.
2355
- # Throw a timeout error if there is no messages for a timeout period.
2356
- def psubscribe_with_timeout(timeout, *channels, &block)
2357
- synchronize do |_client|
2358
- _subscription(:psubscribe_with_timeout, timeout, channels, block)
2359
- end
2360
- end
2361
-
2362
- # Stop listening for messages posted to channels matching the given patterns.
2363
- def punsubscribe(*channels)
2364
- synchronize do |client|
2365
- raise "Can't unsubscribe if not subscribed." unless subscribed?
2366
-
2367
- client.punsubscribe(*channels)
2368
- end
2369
- end
2370
-
2371
- # Inspect the state of the Pub/Sub subsystem.
2372
- # Possible subcommands: channels, numsub, numpat.
2373
- def pubsub(subcommand, *args)
2374
- synchronize do |client|
2375
- client.call([:pubsub, subcommand] + args)
2376
- end
2377
- end
2378
-
2379
- # Watch the given keys to determine execution of the MULTI/EXEC block.
2380
- #
2381
- # Using a block is optional, but is necessary for thread-safety.
2382
- #
2383
- # An `#unwatch` is automatically issued if an exception is raised within the
2384
- # block that is a subclass of StandardError and is not a ConnectionError.
2385
- #
2386
- # @example With a block
2387
- # redis.watch("key") do
2388
- # if redis.get("key") == "some value"
2389
- # redis.multi do |multi|
2390
- # multi.set("key", "other value")
2391
- # multi.incr("counter")
2392
- # end
2393
- # else
2394
- # redis.unwatch
2395
- # end
2396
- # end
2397
- # # => ["OK", 6]
2398
- #
2399
- # @example Without a block
2400
- # redis.watch("key")
2401
- # # => "OK"
2402
- #
2403
- # @param [String, Array<String>] keys one or more keys to watch
2404
- # @return [Object] if using a block, returns the return value of the block
2405
- # @return [String] if not using a block, returns `OK`
2406
- #
2407
- # @see #unwatch
2408
- # @see #multi
2409
- def watch(*keys)
2410
- synchronize do |client|
2411
- res = client.call([:watch, *keys])
2412
-
2413
- if block_given?
2414
- begin
2415
- yield(self)
2416
- rescue ConnectionError
2417
- raise
2418
- rescue StandardError
2419
- unwatch
2420
- raise
2421
- end
2422
- else
2423
- res
2424
- end
2425
- end
2426
- end
2427
-
2428
- # Forget about all watched keys.
2429
- #
2430
- # @return [String] `OK`
2431
- #
2432
- # @see #watch
2433
- # @see #multi
2434
- def unwatch
2435
- synchronize do |client|
2436
- client.call([:unwatch])
2437
- end
2438
- end
2439
-
2440
- def pipelined
2441
- synchronize do |prior_client|
2442
- begin
2443
- @client = Pipeline.new(prior_client)
2444
- yield(self)
2445
- prior_client.call_pipeline(@client)
2446
- ensure
2447
- @client = prior_client
2448
- end
2449
- end
2450
- end
2451
-
2452
- # Mark the start of a transaction block.
2453
- #
2454
- # Passing a block is optional.
2455
- #
2456
- # @example With a block
2457
- # redis.multi do |multi|
2458
- # multi.set("key", "value")
2459
- # multi.incr("counter")
2460
- # end # => ["OK", 6]
2461
- #
2462
- # @example Without a block
2463
- # redis.multi
2464
- # # => "OK"
2465
- # redis.set("key", "value")
2466
- # # => "QUEUED"
2467
- # redis.incr("counter")
2468
- # # => "QUEUED"
2469
- # redis.exec
2470
- # # => ["OK", 6]
2471
- #
2472
- # @yield [multi] the commands that are called inside this block are cached
2473
- # and written to the server upon returning from it
2474
- # @yieldparam [Redis] multi `self`
2475
- #
2476
- # @return [String, Array<...>]
2477
- # - when a block is not given, `OK`
2478
- # - when a block is given, an array with replies
2479
- #
2480
- # @see #watch
2481
- # @see #unwatch
2482
- def multi
2483
- synchronize do |prior_client|
2484
- if !block_given?
2485
- prior_client.call([:multi])
2486
- else
2487
- begin
2488
- @client = Pipeline::Multi.new(prior_client)
2489
- yield(self)
2490
- prior_client.call_pipeline(@client)
2491
- ensure
2492
- @client = prior_client
2493
- end
2494
- end
2495
- end
2496
- end
2497
-
2498
- # Execute all commands issued after MULTI.
2499
- #
2500
- # Only call this method when `#multi` was called **without** a block.
2501
- #
2502
- # @return [nil, Array<...>]
2503
- # - when commands were not executed, `nil`
2504
- # - when commands were executed, an array with their replies
2505
- #
2506
- # @see #multi
2507
- # @see #discard
2508
- def exec
2509
- synchronize do |client|
2510
- client.call([:exec])
2511
- end
2512
- end
2513
-
2514
- # Discard all commands issued after MULTI.
2515
- #
2516
- # Only call this method when `#multi` was called **without** a block.
2517
- #
2518
- # @return [String] `"OK"`
2519
- #
2520
- # @see #multi
2521
- # @see #exec
2522
- def discard
2523
- synchronize do |client|
2524
- client.call([:discard])
2525
- end
2526
- end
2527
-
2528
- # Control remote script registry.
2529
- #
2530
- # @example Load a script
2531
- # sha = redis.script(:load, "return 1")
2532
- # # => <sha of this script>
2533
- # @example Check if a script exists
2534
- # redis.script(:exists, sha)
2535
- # # => true
2536
- # @example Check if multiple scripts exist
2537
- # redis.script(:exists, [sha, other_sha])
2538
- # # => [true, false]
2539
- # @example Flush the script registry
2540
- # redis.script(:flush)
2541
- # # => "OK"
2542
- # @example Kill a running script
2543
- # redis.script(:kill)
2544
- # # => "OK"
2545
- #
2546
- # @param [String] subcommand e.g. `exists`, `flush`, `load`, `kill`
2547
- # @param [Array<String>] args depends on subcommand
2548
- # @return [String, Boolean, Array<Boolean>, ...] depends on subcommand
2549
- #
2550
- # @see #eval
2551
- # @see #evalsha
2552
- def script(subcommand, *args)
2553
- subcommand = subcommand.to_s.downcase
2554
-
2555
- if subcommand == "exists"
2556
- synchronize do |client|
2557
- arg = args.first
2558
-
2559
- client.call([:script, :exists, arg]) do |reply|
2560
- reply = reply.map { |r| Boolify.call(r) }
2561
-
2562
- if arg.is_a?(Array)
2563
- reply
2564
- else
2565
- reply.first
2566
- end
2567
- end
2568
- end
2569
- else
2570
- synchronize do |client|
2571
- client.call([:script, subcommand] + args)
2572
- end
2573
- end
2574
- end
2575
-
2576
- def _eval(cmd, args)
2577
- script = args.shift
2578
- options = args.pop if args.last.is_a?(Hash)
2579
- options ||= {}
2580
-
2581
- keys = args.shift || options[:keys] || []
2582
- argv = args.shift || options[:argv] || []
2583
-
2584
- synchronize do |client|
2585
- client.call([cmd, script, keys.length] + keys + argv)
2586
- end
2587
- end
2588
-
2589
- # Evaluate Lua script.
2590
- #
2591
- # @example EVAL without KEYS nor ARGV
2592
- # redis.eval("return 1")
2593
- # # => 1
2594
- # @example EVAL with KEYS and ARGV as array arguments
2595
- # redis.eval("return { KEYS, ARGV }", ["k1", "k2"], ["a1", "a2"])
2596
- # # => [["k1", "k2"], ["a1", "a2"]]
2597
- # @example EVAL with KEYS and ARGV in a hash argument
2598
- # redis.eval("return { KEYS, ARGV }", :keys => ["k1", "k2"], :argv => ["a1", "a2"])
2599
- # # => [["k1", "k2"], ["a1", "a2"]]
2600
- #
2601
- # @param [Array<String>] keys optional array with keys to pass to the script
2602
- # @param [Array<String>] argv optional array with arguments to pass to the script
2603
- # @param [Hash] options
2604
- # - `:keys => Array<String>`: optional array with keys to pass to the script
2605
- # - `:argv => Array<String>`: optional array with arguments to pass to the script
2606
- # @return depends on the script
2607
- #
2608
- # @see #script
2609
- # @see #evalsha
2610
- def eval(*args)
2611
- _eval(:eval, args)
2612
- end
2613
-
2614
- # Evaluate Lua script by its SHA.
2615
- #
2616
- # @example EVALSHA without KEYS nor ARGV
2617
- # redis.evalsha(sha)
2618
- # # => <depends on script>
2619
- # @example EVALSHA with KEYS and ARGV as array arguments
2620
- # redis.evalsha(sha, ["k1", "k2"], ["a1", "a2"])
2621
- # # => <depends on script>
2622
- # @example EVALSHA with KEYS and ARGV in a hash argument
2623
- # redis.evalsha(sha, :keys => ["k1", "k2"], :argv => ["a1", "a2"])
2624
- # # => <depends on script>
2625
- #
2626
- # @param [Array<String>] keys optional array with keys to pass to the script
2627
- # @param [Array<String>] argv optional array with arguments to pass to the script
2628
- # @param [Hash] options
2629
- # - `:keys => Array<String>`: optional array with keys to pass to the script
2630
- # - `:argv => Array<String>`: optional array with arguments to pass to the script
2631
- # @return depends on the script
2632
- #
2633
- # @see #script
2634
- # @see #eval
2635
- def evalsha(*args)
2636
- _eval(:evalsha, args)
2637
- end
2638
-
2639
- def _scan(command, cursor, args, match: nil, count: nil, &block)
2640
- # SSCAN/ZSCAN/HSCAN already prepend the key to +args+.
2641
-
2642
- args << cursor
2643
- args << "MATCH" << match if match
2644
- args << "COUNT" << count if count
2645
-
2646
- synchronize do |client|
2647
- client.call([command] + args, &block)
2648
- end
2649
- end
2650
-
2651
- # Scan the keyspace
2652
- #
2653
- # @example Retrieve the first batch of keys
2654
- # redis.scan(0)
2655
- # # => ["4", ["key:21", "key:47", "key:42"]]
2656
- # @example Retrieve a batch of keys matching a pattern
2657
- # redis.scan(4, :match => "key:1?")
2658
- # # => ["92", ["key:13", "key:18"]]
2659
- #
2660
- # @param [String, Integer] cursor the cursor of the iteration
2661
- # @param [Hash] options
2662
- # - `:match => String`: only return keys matching the pattern
2663
- # - `:count => Integer`: return count keys at most per iteration
2664
- #
2665
- # @return [String, Array<String>] the next cursor and all found keys
2666
- def scan(cursor, **options)
2667
- _scan(:scan, cursor, [], **options)
2668
- end
2669
-
2670
- # Scan the keyspace
2671
- #
2672
- # @example Retrieve all of the keys (with possible duplicates)
2673
- # redis.scan_each.to_a
2674
- # # => ["key:21", "key:47", "key:42"]
2675
- # @example Execute block for each key matching a pattern
2676
- # redis.scan_each(:match => "key:1?") {|key| puts key}
2677
- # # => key:13
2678
- # # => key:18
2679
- #
2680
- # @param [Hash] options
2681
- # - `:match => String`: only return keys matching the pattern
2682
- # - `:count => Integer`: return count keys at most per iteration
2683
- #
2684
- # @return [Enumerator] an enumerator for all found keys
2685
- def scan_each(**options, &block)
2686
- return to_enum(:scan_each, **options) unless block_given?
2687
-
2688
- cursor = 0
2689
- loop do
2690
- cursor, keys = scan(cursor, **options)
2691
- keys.each(&block)
2692
- break if cursor == "0"
2693
- end
2694
- end
2695
-
2696
- # Scan a hash
2697
- #
2698
- # @example Retrieve the first batch of key/value pairs in a hash
2699
- # redis.hscan("hash", 0)
2700
- #
2701
- # @param [String, Integer] cursor the cursor of the iteration
2702
- # @param [Hash] options
2703
- # - `:match => String`: only return keys matching the pattern
2704
- # - `:count => Integer`: return count keys at most per iteration
2705
- #
2706
- # @return [String, Array<[String, String]>] the next cursor and all found keys
2707
- def hscan(key, cursor, **options)
2708
- _scan(:hscan, cursor, [key], **options) do |reply|
2709
- [reply[0], reply[1].each_slice(2).to_a]
2710
- end
2711
- end
2712
-
2713
- # Scan a hash
2714
- #
2715
- # @example Retrieve all of the key/value pairs in a hash
2716
- # redis.hscan_each("hash").to_a
2717
- # # => [["key70", "70"], ["key80", "80"]]
2718
- #
2719
- # @param [Hash] options
2720
- # - `:match => String`: only return keys matching the pattern
2721
- # - `:count => Integer`: return count keys at most per iteration
2722
- #
2723
- # @return [Enumerator] an enumerator for all found keys
2724
- def hscan_each(key, **options, &block)
2725
- return to_enum(:hscan_each, key, **options) unless block_given?
2726
-
2727
- cursor = 0
2728
- loop do
2729
- cursor, values = hscan(key, cursor, **options)
2730
- values.each(&block)
2731
- break if cursor == "0"
2732
- end
2733
- end
2734
-
2735
- # Scan a sorted set
2736
- #
2737
- # @example Retrieve the first batch of key/value pairs in a hash
2738
- # redis.zscan("zset", 0)
2739
- #
2740
- # @param [String, Integer] cursor the cursor of the iteration
2741
- # @param [Hash] options
2742
- # - `:match => String`: only return keys matching the pattern
2743
- # - `:count => Integer`: return count keys at most per iteration
2744
- #
2745
- # @return [String, Array<[String, Float]>] the next cursor and all found
2746
- # members and scores
2747
- def zscan(key, cursor, **options)
2748
- _scan(:zscan, cursor, [key], **options) do |reply|
2749
- [reply[0], FloatifyPairs.call(reply[1])]
2750
- end
2751
- end
2752
-
2753
- # Scan a sorted set
2754
- #
2755
- # @example Retrieve all of the members/scores in a sorted set
2756
- # redis.zscan_each("zset").to_a
2757
- # # => [["key70", "70"], ["key80", "80"]]
2758
- #
2759
- # @param [Hash] options
2760
- # - `:match => String`: only return keys matching the pattern
2761
- # - `:count => Integer`: return count keys at most per iteration
2762
- #
2763
- # @return [Enumerator] an enumerator for all found scores and members
2764
- def zscan_each(key, **options, &block)
2765
- return to_enum(:zscan_each, key, **options) unless block_given?
2766
-
2767
- cursor = 0
2768
- loop do
2769
- cursor, values = zscan(key, cursor, **options)
2770
- values.each(&block)
2771
- break if cursor == "0"
2772
- end
2773
- end
2774
-
2775
- # Scan a set
2776
- #
2777
- # @example Retrieve the first batch of keys in a set
2778
- # redis.sscan("set", 0)
2779
- #
2780
- # @param [String, Integer] cursor the cursor of the iteration
2781
- # @param [Hash] options
2782
- # - `:match => String`: only return keys matching the pattern
2783
- # - `:count => Integer`: return count keys at most per iteration
2784
- #
2785
- # @return [String, Array<String>] the next cursor and all found members
2786
- def sscan(key, cursor, **options)
2787
- _scan(:sscan, cursor, [key], **options)
2788
- end
2789
-
2790
- # Scan a set
2791
- #
2792
- # @example Retrieve all of the keys in a set
2793
- # redis.sscan_each("set").to_a
2794
- # # => ["key1", "key2", "key3"]
2795
- #
2796
- # @param [Hash] options
2797
- # - `:match => String`: only return keys matching the pattern
2798
- # - `:count => Integer`: return count keys at most per iteration
2799
- #
2800
- # @return [Enumerator] an enumerator for all keys in the set
2801
- def sscan_each(key, **options, &block)
2802
- return to_enum(:sscan_each, key, **options) unless block_given?
2803
-
2804
- cursor = 0
2805
- loop do
2806
- cursor, keys = sscan(key, cursor, **options)
2807
- keys.each(&block)
2808
- break if cursor == "0"
2809
- end
2810
- end
2811
-
2812
- # Add one or more members to a HyperLogLog structure.
2813
- #
2814
- # @param [String] key
2815
- # @param [String, Array<String>] member one member, or array of members
2816
- # @return [Boolean] true if at least 1 HyperLogLog internal register was altered. false otherwise.
2817
- def pfadd(key, member)
2818
- synchronize do |client|
2819
- client.call([:pfadd, key, member], &Boolify)
2820
- end
2821
- end
2822
-
2823
- # Get the approximate cardinality of members added to HyperLogLog structure.
2824
- #
2825
- # If called with multiple keys, returns the approximate cardinality of the
2826
- # union of the HyperLogLogs contained in the keys.
2827
- #
2828
- # @param [String, Array<String>] keys
2829
- # @return [Integer]
2830
- def pfcount(*keys)
2831
- synchronize do |client|
2832
- client.call([:pfcount] + keys)
2833
- end
2834
- end
2835
-
2836
- # Merge multiple HyperLogLog values into an unique value that will approximate the cardinality of the union of
2837
- # the observed Sets of the source HyperLogLog structures.
2838
- #
2839
- # @param [String] dest_key destination key
2840
- # @param [String, Array<String>] source_key source key, or array of keys
2841
- # @return [Boolean]
2842
- def pfmerge(dest_key, *source_key)
2843
- synchronize do |client|
2844
- client.call([:pfmerge, dest_key, *source_key], &BoolifySet)
2845
- end
2846
- end
2847
-
2848
- # Adds the specified geospatial items (latitude, longitude, name) to the specified key
2849
- #
2850
- # @param [String] key
2851
- # @param [Array] member arguemnts for member or members: longitude, latitude, name
2852
- # @return [Integer] number of elements added to the sorted set
2853
- def geoadd(key, *member)
2854
- synchronize do |client|
2855
- client.call([:geoadd, key, *member])
2856
- end
2857
- end
2858
-
2859
- # Returns geohash string representing position for specified members of the specified key.
2860
- #
2861
- # @param [String] key
2862
- # @param [String, Array<String>] member one member or array of members
2863
- # @return [Array<String, nil>] returns array containg geohash string if member is present, nil otherwise
2864
- def geohash(key, member)
2865
- synchronize do |client|
2866
- client.call([:geohash, key, member])
2867
- end
2868
- end
2869
-
2870
- # Query a sorted set representing a geospatial index to fetch members matching a
2871
- # given maximum distance from a point
2872
- #
2873
- # @param [Array] args key, longitude, latitude, radius, unit(m|km|ft|mi)
2874
- # @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest
2875
- # or the farthest to the nearest relative to the center
2876
- # @param [Integer] count limit the results to the first N matching items
2877
- # @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
2878
- # @return [Array<String>] may be changed with `options`
2879
-
2880
- def georadius(*args, **geoptions)
2881
- geoarguments = _geoarguments(*args, **geoptions)
2882
-
2883
- synchronize do |client|
2884
- client.call([:georadius, *geoarguments])
2885
- end
2886
- end
2887
-
2888
- # Query a sorted set representing a geospatial index to fetch members matching a
2889
- # given maximum distance from an already existing member
2890
- #
2891
- # @param [Array] args key, member, radius, unit(m|km|ft|mi)
2892
- # @param ['asc', 'desc'] sort sort returned items from the nearest to the farthest or the farthest
2893
- # to the nearest relative to the center
2894
- # @param [Integer] count limit the results to the first N matching items
2895
- # @param ['WITHDIST', 'WITHCOORD', 'WITHHASH'] options to return additional information
2896
- # @return [Array<String>] may be changed with `options`
2897
-
2898
- def georadiusbymember(*args, **geoptions)
2899
- geoarguments = _geoarguments(*args, **geoptions)
2900
-
2901
- synchronize do |client|
2902
- client.call([:georadiusbymember, *geoarguments])
2903
- end
2904
- end
2905
-
2906
- # Returns longitude and latitude of members of a geospatial index
2907
- #
2908
- # @param [String] key
2909
- # @param [String, Array<String>] member one member or array of members
2910
- # @return [Array<Array<String>, nil>] returns array of elements, where each
2911
- # element is either array of longitude and latitude or nil
2912
- def geopos(key, member)
2913
- synchronize do |client|
2914
- client.call([:geopos, key, member])
2915
- end
2916
- end
2917
-
2918
- # Returns the distance between two members of a geospatial index
2919
- #
2920
- # @param [String ]key
2921
- # @param [Array<String>] members
2922
- # @param ['m', 'km', 'mi', 'ft'] unit
2923
- # @return [String, nil] returns distance in spefied unit if both members present, nil otherwise.
2924
- def geodist(key, member1, member2, unit = 'm')
2925
- synchronize do |client|
2926
- client.call([:geodist, key, member1, member2, unit])
2927
- end
2928
- end
2929
-
2930
- # Returns the stream information each subcommand.
2931
- #
2932
- # @example stream
2933
- # redis.xinfo(:stream, 'mystream')
2934
- # @example groups
2935
- # redis.xinfo(:groups, 'mystream')
2936
- # @example consumers
2937
- # redis.xinfo(:consumers, 'mystream', 'mygroup')
2938
- #
2939
- # @param subcommand [String] e.g. `stream` `groups` `consumers`
2940
- # @param key [String] the stream key
2941
- # @param group [String] the consumer group name, required if subcommand is `consumers`
2942
- #
2943
- # @return [Hash] information of the stream if subcommand is `stream`
2944
- # @return [Array<Hash>] information of the consumer groups if subcommand is `groups`
2945
- # @return [Array<Hash>] information of the consumers if subcommand is `consumers`
2946
- def xinfo(subcommand, key, group = nil)
2947
- args = [:xinfo, subcommand, key, group].compact
2948
- synchronize do |client|
2949
- client.call(args) do |reply|
2950
- case subcommand.to_s.downcase
2951
- when 'stream' then Hashify.call(reply)
2952
- when 'groups', 'consumers' then reply.map { |arr| Hashify.call(arr) }
2953
- else reply
2954
- end
2955
- end
2956
- end
2957
- end
2958
-
2959
- # Add new entry to the stream.
2960
- #
2961
- # @example Without options
2962
- # redis.xadd('mystream', f1: 'v1', f2: 'v2')
2963
- # @example With options
2964
- # redis.xadd('mystream', { f1: 'v1', f2: 'v2' }, id: '0-0', maxlen: 1000, approximate: true)
2965
- #
2966
- # @param key [String] the stream key
2967
- # @param entry [Hash] one or multiple field-value pairs
2968
- # @param opts [Hash] several options for `XADD` command
2969
- #
2970
- # @option opts [String] :id the entry id, default value is `*`, it means auto generation
2971
- # @option opts [Integer] :maxlen max length of entries
2972
- # @option opts [Boolean] :approximate whether to add `~` modifier of maxlen or not
2973
- #
2974
- # @return [String] the entry id
2975
- def xadd(key, entry, approximate: nil, maxlen: nil, id: '*')
2976
- args = [:xadd, key]
2977
- if maxlen
2978
- args << "MAXLEN"
2979
- args << "~" if approximate
2980
- args << maxlen
2981
- end
2982
- args << id
2983
- args.concat(entry.to_a.flatten)
2984
- synchronize { |client| client.call(args) }
2985
- end
2986
-
2987
- # Trims older entries of the stream if needed.
2988
- #
2989
- # @example Without options
2990
- # redis.xtrim('mystream', 1000)
2991
- # @example With options
2992
- # redis.xtrim('mystream', 1000, approximate: true)
2993
- #
2994
- # @param key [String] the stream key
2995
- # @param mexlen [Integer] max length of entries
2996
- # @param approximate [Boolean] whether to add `~` modifier of maxlen or not
2997
- #
2998
- # @return [Integer] the number of entries actually deleted
2999
- def xtrim(key, maxlen, approximate: false)
3000
- args = [:xtrim, key, 'MAXLEN', (approximate ? '~' : nil), maxlen].compact
3001
- synchronize { |client| client.call(args) }
3002
- end
3003
-
3004
- # Delete entries by entry ids.
3005
- #
3006
- # @example With splatted entry ids
3007
- # redis.xdel('mystream', '0-1', '0-2')
3008
- # @example With arrayed entry ids
3009
- # redis.xdel('mystream', ['0-1', '0-2'])
3010
- #
3011
- # @param key [String] the stream key
3012
- # @param ids [Array<String>] one or multiple entry ids
3013
- #
3014
- # @return [Integer] the number of entries actually deleted
3015
- def xdel(key, *ids)
3016
- args = [:xdel, key].concat(ids.flatten)
3017
- synchronize { |client| client.call(args) }
3018
- end
3019
-
3020
- # Fetches entries of the stream in ascending order.
3021
- #
3022
- # @example Without options
3023
- # redis.xrange('mystream')
3024
- # @example With a specific start
3025
- # redis.xrange('mystream', '0-1')
3026
- # @example With a specific start and end
3027
- # redis.xrange('mystream', '0-1', '0-3')
3028
- # @example With count options
3029
- # redis.xrange('mystream', count: 10)
3030
- #
3031
- # @param key [String] the stream key
3032
- # @param start [String] first entry id of range, default value is `-`
3033
- # @param end [String] last entry id of range, default value is `+`
3034
- # @param count [Integer] the number of entries as limit
3035
- #
3036
- # @return [Array<Array<String, Hash>>] the ids and entries pairs
3037
- def xrange(key, start = '-', range_end = '+', count: nil)
3038
- args = [:xrange, key, start, range_end]
3039
- args.concat(['COUNT', count]) if count
3040
- synchronize { |client| client.call(args, &HashifyStreamEntries) }
3041
- end
3042
-
3043
- # Fetches entries of the stream in descending order.
3044
- #
3045
- # @example Without options
3046
- # redis.xrevrange('mystream')
3047
- # @example With a specific end
3048
- # redis.xrevrange('mystream', '0-3')
3049
- # @example With a specific end and start
3050
- # redis.xrevrange('mystream', '0-3', '0-1')
3051
- # @example With count options
3052
- # redis.xrevrange('mystream', count: 10)
3053
- #
3054
- # @param key [String] the stream key
3055
- # @param end [String] first entry id of range, default value is `+`
3056
- # @param start [String] last entry id of range, default value is `-`
3057
- # @params count [Integer] the number of entries as limit
3058
- #
3059
- # @return [Array<Array<String, Hash>>] the ids and entries pairs
3060
- def xrevrange(key, range_end = '+', start = '-', count: nil)
3061
- args = [:xrevrange, key, range_end, start]
3062
- args.concat(['COUNT', count]) if count
3063
- synchronize { |client| client.call(args, &HashifyStreamEntries) }
3064
- end
3065
-
3066
- # Returns the number of entries inside a stream.
3067
- #
3068
- # @example With key
3069
- # redis.xlen('mystream')
3070
- #
3071
- # @param key [String] the stream key
3072
- #
3073
- # @return [Integer] the number of entries
3074
- def xlen(key)
3075
- synchronize { |client| client.call([:xlen, key]) }
3076
- end
3077
-
3078
- # Fetches entries from one or multiple streams. Optionally blocking.
3079
- #
3080
- # @example With a key
3081
- # redis.xread('mystream', '0-0')
3082
- # @example With multiple keys
3083
- # redis.xread(%w[mystream1 mystream2], %w[0-0 0-0])
3084
- # @example With count option
3085
- # redis.xread('mystream', '0-0', count: 2)
3086
- # @example With block option
3087
- # redis.xread('mystream', '$', block: 1000)
3088
- #
3089
- # @param keys [Array<String>] one or multiple stream keys
3090
- # @param ids [Array<String>] one or multiple entry ids
3091
- # @param count [Integer] the number of entries as limit per stream
3092
- # @param block [Integer] the number of milliseconds as blocking timeout
3093
- #
3094
- # @return [Hash{String => Hash{String => Hash}}] the entries
3095
- def xread(keys, ids, count: nil, block: nil)
3096
- args = [:xread]
3097
- args << 'COUNT' << count if count
3098
- args << 'BLOCK' << block.to_i if block
3099
- _xread(args, keys, ids, block)
3100
- end
3101
-
3102
- # Manages the consumer group of the stream.
3103
- #
3104
- # @example With `create` subcommand
3105
- # redis.xgroup(:create, 'mystream', 'mygroup', '$')
3106
- # @example With `setid` subcommand
3107
- # redis.xgroup(:setid, 'mystream', 'mygroup', '$')
3108
- # @example With `destroy` subcommand
3109
- # redis.xgroup(:destroy, 'mystream', 'mygroup')
3110
- # @example With `delconsumer` subcommand
3111
- # redis.xgroup(:delconsumer, 'mystream', 'mygroup', 'consumer1')
3112
- #
3113
- # @param subcommand [String] `create` `setid` `destroy` `delconsumer`
3114
- # @param key [String] the stream key
3115
- # @param group [String] the consumer group name
3116
- # @param id_or_consumer [String]
3117
- # * the entry id or `$`, required if subcommand is `create` or `setid`
3118
- # * the consumer name, required if subcommand is `delconsumer`
3119
- # @param mkstream [Boolean] whether to create an empty stream automatically or not
3120
- #
3121
- # @return [String] `OK` if subcommand is `create` or `setid`
3122
- # @return [Integer] effected count if subcommand is `destroy` or `delconsumer`
3123
- def xgroup(subcommand, key, group, id_or_consumer = nil, mkstream: false)
3124
- args = [:xgroup, subcommand, key, group, id_or_consumer, (mkstream ? 'MKSTREAM' : nil)].compact
3125
- synchronize { |client| client.call(args) }
3126
- end
3127
-
3128
- # Fetches a subset of the entries from one or multiple streams related with the consumer group.
3129
- # Optionally blocking.
3130
- #
3131
- # @example With a key
3132
- # redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>')
3133
- # @example With multiple keys
3134
- # redis.xreadgroup('mygroup', 'consumer1', %w[mystream1 mystream2], %w[> >])
3135
- # @example With count option
3136
- # redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', count: 2)
3137
- # @example With block option
3138
- # redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', block: 1000)
3139
- # @example With noack option
3140
- # redis.xreadgroup('mygroup', 'consumer1', 'mystream', '>', noack: true)
3141
- #
3142
- # @param group [String] the consumer group name
3143
- # @param consumer [String] the consumer name
3144
- # @param keys [Array<String>] one or multiple stream keys
3145
- # @param ids [Array<String>] one or multiple entry ids
3146
- # @param opts [Hash] several options for `XREADGROUP` command
3147
- #
3148
- # @option opts [Integer] :count the number of entries as limit
3149
- # @option opts [Integer] :block the number of milliseconds as blocking timeout
3150
- # @option opts [Boolean] :noack whether message loss is acceptable or not
3151
- #
3152
- # @return [Hash{String => Hash{String => Hash}}] the entries
3153
- def xreadgroup(group, consumer, keys, ids, count: nil, block: nil, noack: nil)
3154
- args = [:xreadgroup, 'GROUP', group, consumer]
3155
- args << 'COUNT' << count if count
3156
- args << 'BLOCK' << block.to_i if block
3157
- args << 'NOACK' if noack
3158
- _xread(args, keys, ids, block)
3159
- end
3160
-
3161
- # Removes one or multiple entries from the pending entries list of a stream consumer group.
3162
- #
3163
- # @example With a entry id
3164
- # redis.xack('mystream', 'mygroup', '1526569495631-0')
3165
- # @example With splatted entry ids
3166
- # redis.xack('mystream', 'mygroup', '0-1', '0-2')
3167
- # @example With arrayed entry ids
3168
- # redis.xack('mystream', 'mygroup', %w[0-1 0-2])
3169
- #
3170
- # @param key [String] the stream key
3171
- # @param group [String] the consumer group name
3172
- # @param ids [Array<String>] one or multiple entry ids
3173
- #
3174
- # @return [Integer] the number of entries successfully acknowledged
3175
- def xack(key, group, *ids)
3176
- args = [:xack, key, group].concat(ids.flatten)
3177
- synchronize { |client| client.call(args) }
3178
- end
3179
-
3180
- # Changes the ownership of a pending entry
3181
- #
3182
- # @example With splatted entry ids
3183
- # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-1', '0-2')
3184
- # @example With arrayed entry ids
3185
- # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2])
3186
- # @example With idle option
3187
- # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], idle: 1000)
3188
- # @example With time option
3189
- # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], time: 1542866959000)
3190
- # @example With retrycount option
3191
- # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], retrycount: 10)
3192
- # @example With force option
3193
- # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], force: true)
3194
- # @example With justid option
3195
- # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, %w[0-1 0-2], justid: true)
3196
- #
3197
- # @param key [String] the stream key
3198
- # @param group [String] the consumer group name
3199
- # @param consumer [String] the consumer name
3200
- # @param min_idle_time [Integer] the number of milliseconds
3201
- # @param ids [Array<String>] one or multiple entry ids
3202
- # @param opts [Hash] several options for `XCLAIM` command
3203
- #
3204
- # @option opts [Integer] :idle the number of milliseconds as last time it was delivered of the entry
3205
- # @option opts [Integer] :time the number of milliseconds as a specific Unix Epoch time
3206
- # @option opts [Integer] :retrycount the number of retry counter
3207
- # @option opts [Boolean] :force whether to create the pending entry to the pending entries list or not
3208
- # @option opts [Boolean] :justid whether to fetch just an array of entry ids or not
3209
- #
3210
- # @return [Hash{String => Hash}] the entries successfully claimed
3211
- # @return [Array<String>] the entry ids successfully claimed if justid option is `true`
3212
- def xclaim(key, group, consumer, min_idle_time, *ids, **opts)
3213
- args = [:xclaim, key, group, consumer, min_idle_time].concat(ids.flatten)
3214
- args.concat(['IDLE', opts[:idle].to_i]) if opts[:idle]
3215
- args.concat(['TIME', opts[:time].to_i]) if opts[:time]
3216
- args.concat(['RETRYCOUNT', opts[:retrycount]]) if opts[:retrycount]
3217
- args << 'FORCE' if opts[:force]
3218
- args << 'JUSTID' if opts[:justid]
3219
- blk = opts[:justid] ? Noop : HashifyStreamEntries
3220
- synchronize { |client| client.call(args, &blk) }
3221
- end
3222
-
3223
- # Fetches not acknowledging pending entries
3224
- #
3225
- # @example With key and group
3226
- # redis.xpending('mystream', 'mygroup')
3227
- # @example With range options
3228
- # redis.xpending('mystream', 'mygroup', '-', '+', 10)
3229
- # @example With range and consumer options
3230
- # redis.xpending('mystream', 'mygroup', '-', '+', 10, 'consumer1')
3231
- #
3232
- # @param key [String] the stream key
3233
- # @param group [String] the consumer group name
3234
- # @param start [String] start first entry id of range
3235
- # @param end [String] end last entry id of range
3236
- # @param count [Integer] count the number of entries as limit
3237
- # @param consumer [String] the consumer name
3238
- #
3239
- # @return [Hash] the summary of pending entries
3240
- # @return [Array<Hash>] the pending entries details if options were specified
3241
- def xpending(key, group, *args)
3242
- command_args = [:xpending, key, group]
3243
- case args.size
3244
- when 0, 3, 4
3245
- command_args.concat(args)
3246
- else
3247
- raise ArgumentError, "wrong number of arguments (given #{args.size + 2}, expected 2, 5 or 6)"
3248
- end
3249
-
3250
- summary_needed = args.empty?
3251
- blk = summary_needed ? HashifyStreamPendings : HashifyStreamPendingDetails
3252
- synchronize { |client| client.call(command_args, &blk) }
3253
- end
3254
-
3255
- # Interact with the sentinel command (masters, master, slaves, failover)
3256
- #
3257
- # @param [String] subcommand e.g. `masters`, `master`, `slaves`
3258
- # @param [Array<String>] args depends on subcommand
3259
- # @return [Array<String>, Hash<String, String>, String] depends on subcommand
3260
- def sentinel(subcommand, *args)
3261
- subcommand = subcommand.to_s.downcase
3262
- synchronize do |client|
3263
- client.call([:sentinel, subcommand] + args) do |reply|
3264
- case subcommand
3265
- when "get-master-addr-by-name"
3266
- reply
3267
- else
3268
- if reply.is_a?(Array)
3269
- if reply[0].is_a?(Array)
3270
- reply.map(&Hashify)
3271
- else
3272
- Hashify.call(reply)
3273
- end
3274
- else
3275
- reply
3276
- end
3277
- end
3278
- end
3279
- end
3280
- end
3281
-
3282
- # Sends `CLUSTER *` command to random node and returns its reply.
3283
- #
3284
- # @see https://redis.io/commands#cluster Reference of cluster command
3285
- #
3286
- # @param subcommand [String, Symbol] the subcommand of cluster command
3287
- # e.g. `:slots`, `:nodes`, `:slaves`, `:info`
3288
- #
3289
- # @return [Object] depends on the subcommand
3290
- def cluster(subcommand, *args)
3291
- subcommand = subcommand.to_s.downcase
3292
- block = case subcommand
3293
- when 'slots'
3294
- HashifyClusterSlots
3295
- when 'nodes'
3296
- HashifyClusterNodes
3297
- when 'slaves'
3298
- HashifyClusterSlaves
3299
- when 'info'
3300
- HashifyInfo
3301
- else
3302
- Noop
3303
- end
3304
-
3305
- # @see https://github.com/antirez/redis/blob/unstable/src/redis-trib.rb#L127 raw reply expected
3306
- block = Noop unless @cluster_mode
3307
-
3308
- synchronize do |client|
3309
- client.call([:cluster, subcommand] + args, &block)
3310
- end
3311
- end
3312
-
3313
- # Sends `ASKING` command to random node and returns its reply.
3314
- #
3315
- # @see https://redis.io/topics/cluster-spec#ask-redirection ASK redirection
3316
- #
3317
- # @return [String] `'OK'`
3318
- def asking
3319
- synchronize { |client| client.call(%i[asking]) }
3320
- end
3321
-
3322
- def id
3323
- @original_client.id
3324
- end
3325
-
3326
- def inspect
3327
- "#<Redis client v#{Redis::VERSION} for #{id}>"
3328
- end
3329
-
3330
- def dup
3331
- self.class.new(@options)
3332
- end
3333
-
3334
- def connection
3335
- return @original_client.connection_info if @cluster_mode
3336
-
3337
- {
3338
- host: @original_client.host,
3339
- port: @original_client.port,
3340
- db: @original_client.db,
3341
- id: @original_client.id,
3342
- location: @original_client.location
3343
- }
3344
- end
3345
-
3346
- def method_missing(command, *args) # rubocop:disable Style/MissingRespondToMissing
3347
- synchronize do |client|
3348
- client.call([command] + args)
3349
- end
3350
- end
3351
-
3352
- private
3353
-
3354
- # Commands returning 1 for true and 0 for false may be executed in a pipeline
3355
- # where the method call will return nil. Propagate the nil instead of falsely
3356
- # returning false.
3357
- Boolify = lambda { |value|
3358
- case value
3359
- when 1
3360
- true
3361
- when 0
3362
- false
3363
- else
3364
- value
3365
- end
3366
- }
3367
-
3368
- BoolifySet = lambda { |value|
3369
- case value
3370
- when "OK"
3371
- true
3372
- when nil
3373
- false
3374
- else
3375
- value
3376
- end
3377
- }
3378
-
3379
- Hashify = lambda { |value|
3380
- if value.respond_to?(:each_slice)
3381
- value.each_slice(2).to_h
3382
- else
3383
- value
3384
- end
3385
- }
3386
-
3387
- Floatify = lambda { |value|
3388
- case value
3389
- when "inf"
3390
- Float::INFINITY
3391
- when "-inf"
3392
- -Float::INFINITY
3393
- when String
3394
- Float(value)
3395
- else
3396
- value
3397
- end
3398
- }
3399
-
3400
- FloatifyPairs = lambda { |value|
3401
- return value unless value.respond_to?(:each_slice)
3402
-
3403
- value.each_slice(2).map do |member, score|
3404
- [member, Floatify.call(score)]
3405
- end
3406
- }
3407
-
3408
- HashifyInfo = lambda { |reply|
3409
- lines = reply.split("\r\n").grep_v(/^(#|$)/)
3410
- lines.map! { |line| line.split(':', 2) }
3411
- lines.compact!
3412
- lines.to_h
3413
- }
3414
-
3415
- HashifyStreams = lambda { |reply|
3416
- case reply
3417
- when nil
3418
- {}
3419
- else
3420
- reply.map { |key, entries| [key, HashifyStreamEntries.call(entries)] }.to_h
3421
- end
3422
- }
3423
-
3424
- EMPTY_STREAM_RESPONSE = [nil].freeze
3425
- private_constant :EMPTY_STREAM_RESPONSE
3426
-
3427
- HashifyStreamEntries = lambda { |reply|
3428
- reply.compact.map do |entry_id, values|
3429
- [entry_id, values.each_slice(2).to_h]
3430
- end
3431
- }
3432
-
3433
- HashifyStreamPendings = lambda { |reply|
3434
- {
3435
- 'size' => reply[0],
3436
- 'min_entry_id' => reply[1],
3437
- 'max_entry_id' => reply[2],
3438
- 'consumers' => reply[3].nil? ? {} : reply[3].to_h
3439
- }
3440
- }
3441
-
3442
- HashifyStreamPendingDetails = lambda { |reply|
3443
- reply.map do |arr|
3444
- {
3445
- 'entry_id' => arr[0],
3446
- 'consumer' => arr[1],
3447
- 'elapsed' => arr[2],
3448
- 'count' => arr[3]
3449
- }
3450
- end
3451
- }
3452
-
3453
- HashifyClusterNodeInfo = lambda { |str|
3454
- arr = str.split(' ')
3455
- {
3456
- 'node_id' => arr[0],
3457
- 'ip_port' => arr[1],
3458
- 'flags' => arr[2].split(','),
3459
- 'master_node_id' => arr[3],
3460
- 'ping_sent' => arr[4],
3461
- 'pong_recv' => arr[5],
3462
- 'config_epoch' => arr[6],
3463
- 'link_state' => arr[7],
3464
- 'slots' => arr[8].nil? ? nil : Range.new(*arr[8].split('-'))
3465
- }
3466
- }
3467
-
3468
- HashifyClusterSlots = lambda { |reply|
3469
- reply.map do |arr|
3470
- first_slot, last_slot = arr[0..1]
3471
- master = { 'ip' => arr[2][0], 'port' => arr[2][1], 'node_id' => arr[2][2] }
3472
- replicas = arr[3..-1].map { |r| { 'ip' => r[0], 'port' => r[1], 'node_id' => r[2] } }
3473
- {
3474
- 'start_slot' => first_slot,
3475
- 'end_slot' => last_slot,
3476
- 'master' => master,
3477
- 'replicas' => replicas
3478
- }
3479
- end
3480
- }
3481
-
3482
- HashifyClusterNodes = lambda { |reply|
3483
- reply.split(/[\r\n]+/).map { |str| HashifyClusterNodeInfo.call(str) }
3484
- }
3485
-
3486
- HashifyClusterSlaves = lambda { |reply|
3487
- reply.map { |str| HashifyClusterNodeInfo.call(str) }
3488
- }
3489
-
3490
- Noop = ->(reply) { reply }
3491
-
3492
- def _geoarguments(*args, options: nil, sort: nil, count: nil)
3493
- args.push sort if sort
3494
- args.push 'count', count if count
3495
- args.push options if options
3496
- args
3497
- end
3498
-
3499
- def _subscription(method, timeout, channels, block)
3500
- return @client.call([method] + channels) if subscribed?
3501
-
3502
- begin
3503
- original, @client = @client, SubscribedClient.new(@client)
3504
- if timeout > 0
3505
- @client.send(method, timeout, *channels, &block)
3506
- else
3507
- @client.send(method, *channels, &block)
283
+ begin
284
+ original, @client = @client, SubscribedClient.new(@client)
285
+ if timeout > 0
286
+ @client.send(method, timeout, *channels, &block)
287
+ else
288
+ @client.send(method, *channels, &block)
3508
289
  end
3509
290
  ensure
3510
291
  @client = original
3511
292
  end
3512
293
  end
3513
-
3514
- def _xread(args, keys, ids, blocking_timeout_msec)
3515
- keys = keys.is_a?(Array) ? keys : [keys]
3516
- ids = ids.is_a?(Array) ? ids : [ids]
3517
- args << 'STREAMS'
3518
- args.concat(keys)
3519
- args.concat(ids)
3520
-
3521
- synchronize do |client|
3522
- if blocking_timeout_msec.nil?
3523
- client.call(args, &HashifyStreams)
3524
- elsif blocking_timeout_msec.to_f.zero?
3525
- client.call_without_timeout(args, &HashifyStreams)
3526
- else
3527
- timeout = client.timeout.to_f + blocking_timeout_msec.to_f / 1000.0
3528
- client.call_with_timeout(args, timeout, &HashifyStreams)
3529
- end
3530
- end
3531
- end
3532
294
  end
3533
295
 
3534
- require_relative "redis/version"
3535
- require_relative "redis/connection"
3536
- require_relative "redis/client"
3537
- require_relative "redis/cluster"
3538
- require_relative "redis/pipeline"
3539
- require_relative "redis/subscribe"
296
+ require "redis/version"
297
+ require "redis/connection"
298
+ require "redis/client"
299
+ require "redis/cluster"
300
+ require "redis/pipeline"
301
+ require "redis/subscribe"