redis 3.2.0 → 4.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +278 -15
  3. data/README.md +260 -76
  4. data/lib/redis/client.rb +239 -115
  5. data/lib/redis/cluster/command.rb +79 -0
  6. data/lib/redis/cluster/command_loader.rb +33 -0
  7. data/lib/redis/cluster/key_slot_converter.rb +72 -0
  8. data/lib/redis/cluster/node.rb +120 -0
  9. data/lib/redis/cluster/node_key.rb +31 -0
  10. data/lib/redis/cluster/node_loader.rb +37 -0
  11. data/lib/redis/cluster/option.rb +93 -0
  12. data/lib/redis/cluster/slot.rb +86 -0
  13. data/lib/redis/cluster/slot_loader.rb +49 -0
  14. data/lib/redis/cluster.rb +315 -0
  15. data/lib/redis/commands/bitmaps.rb +63 -0
  16. data/lib/redis/commands/cluster.rb +45 -0
  17. data/lib/redis/commands/connection.rb +58 -0
  18. data/lib/redis/commands/geo.rb +84 -0
  19. data/lib/redis/commands/hashes.rb +251 -0
  20. data/lib/redis/commands/hyper_log_log.rb +37 -0
  21. data/lib/redis/commands/keys.rb +411 -0
  22. data/lib/redis/commands/lists.rb +289 -0
  23. data/lib/redis/commands/pubsub.rb +72 -0
  24. data/lib/redis/commands/scripting.rb +114 -0
  25. data/lib/redis/commands/server.rb +188 -0
  26. data/lib/redis/commands/sets.rb +207 -0
  27. data/lib/redis/commands/sorted_sets.rb +804 -0
  28. data/lib/redis/commands/streams.rb +382 -0
  29. data/lib/redis/commands/strings.rb +313 -0
  30. data/lib/redis/commands/transactions.rb +92 -0
  31. data/lib/redis/commands.rb +242 -0
  32. data/lib/redis/connection/command_helper.rb +7 -10
  33. data/lib/redis/connection/hiredis.rb +11 -6
  34. data/lib/redis/connection/registry.rb +2 -1
  35. data/lib/redis/connection/ruby.rb +173 -64
  36. data/lib/redis/connection/synchrony.rb +32 -8
  37. data/lib/redis/connection.rb +3 -1
  38. data/lib/redis/distributed.rb +233 -74
  39. data/lib/redis/errors.rb +48 -0
  40. data/lib/redis/hash_ring.rb +30 -72
  41. data/lib/redis/pipeline.rb +145 -12
  42. data/lib/redis/subscribe.rb +20 -13
  43. data/lib/redis/version.rb +3 -1
  44. data/lib/redis.rb +171 -2476
  45. metadata +71 -165
  46. data/.gitignore +0 -15
  47. data/.travis/Gemfile +0 -11
  48. data/.travis.yml +0 -54
  49. data/.yardopts +0 -3
  50. data/Gemfile +0 -4
  51. data/Rakefile +0 -68
  52. data/benchmarking/logging.rb +0 -71
  53. data/benchmarking/pipeline.rb +0 -51
  54. data/benchmarking/speed.rb +0 -21
  55. data/benchmarking/suite.rb +0 -24
  56. data/benchmarking/worker.rb +0 -71
  57. data/examples/basic.rb +0 -15
  58. data/examples/consistency.rb +0 -114
  59. data/examples/dist_redis.rb +0 -43
  60. data/examples/incr-decr.rb +0 -17
  61. data/examples/list.rb +0 -26
  62. data/examples/pubsub.rb +0 -37
  63. data/examples/sentinel/sentinel.conf +0 -9
  64. data/examples/sentinel/start +0 -49
  65. data/examples/sentinel.rb +0 -41
  66. data/examples/sets.rb +0 -36
  67. data/examples/unicorn/config.ru +0 -3
  68. data/examples/unicorn/unicorn.rb +0 -20
  69. data/redis.gemspec +0 -43
  70. data/test/bitpos_test.rb +0 -69
  71. data/test/blocking_commands_test.rb +0 -42
  72. data/test/command_map_test.rb +0 -30
  73. data/test/commands_on_hashes_test.rb +0 -21
  74. data/test/commands_on_hyper_log_log_test.rb +0 -21
  75. data/test/commands_on_lists_test.rb +0 -20
  76. data/test/commands_on_sets_test.rb +0 -77
  77. data/test/commands_on_sorted_sets_test.rb +0 -123
  78. data/test/commands_on_strings_test.rb +0 -101
  79. data/test/commands_on_value_types_test.rb +0 -131
  80. data/test/connection_handling_test.rb +0 -189
  81. data/test/db/.gitkeep +0 -0
  82. data/test/distributed_blocking_commands_test.rb +0 -46
  83. data/test/distributed_commands_on_hashes_test.rb +0 -10
  84. data/test/distributed_commands_on_hyper_log_log_test.rb +0 -33
  85. data/test/distributed_commands_on_lists_test.rb +0 -22
  86. data/test/distributed_commands_on_sets_test.rb +0 -83
  87. data/test/distributed_commands_on_sorted_sets_test.rb +0 -18
  88. data/test/distributed_commands_on_strings_test.rb +0 -59
  89. data/test/distributed_commands_on_value_types_test.rb +0 -95
  90. data/test/distributed_commands_requiring_clustering_test.rb +0 -164
  91. data/test/distributed_connection_handling_test.rb +0 -23
  92. data/test/distributed_internals_test.rb +0 -70
  93. data/test/distributed_key_tags_test.rb +0 -52
  94. data/test/distributed_persistence_control_commands_test.rb +0 -26
  95. data/test/distributed_publish_subscribe_test.rb +0 -92
  96. data/test/distributed_remote_server_control_commands_test.rb +0 -66
  97. data/test/distributed_scripting_test.rb +0 -102
  98. data/test/distributed_sorting_test.rb +0 -20
  99. data/test/distributed_test.rb +0 -58
  100. data/test/distributed_transactions_test.rb +0 -32
  101. data/test/encoding_test.rb +0 -18
  102. data/test/error_replies_test.rb +0 -59
  103. data/test/fork_safety_test.rb +0 -65
  104. data/test/helper.rb +0 -232
  105. data/test/helper_test.rb +0 -24
  106. data/test/internals_test.rb +0 -434
  107. data/test/lint/blocking_commands.rb +0 -150
  108. data/test/lint/hashes.rb +0 -162
  109. data/test/lint/hyper_log_log.rb +0 -60
  110. data/test/lint/lists.rb +0 -143
  111. data/test/lint/sets.rb +0 -125
  112. data/test/lint/sorted_sets.rb +0 -238
  113. data/test/lint/strings.rb +0 -260
  114. data/test/lint/value_types.rb +0 -122
  115. data/test/persistence_control_commands_test.rb +0 -26
  116. data/test/pipelining_commands_test.rb +0 -242
  117. data/test/publish_subscribe_test.rb +0 -210
  118. data/test/remote_server_control_commands_test.rb +0 -117
  119. data/test/scanning_test.rb +0 -413
  120. data/test/scripting_test.rb +0 -78
  121. data/test/sorting_test.rb +0 -59
  122. data/test/support/connection/hiredis.rb +0 -1
  123. data/test/support/connection/ruby.rb +0 -1
  124. data/test/support/connection/synchrony.rb +0 -17
  125. data/test/support/redis_mock.rb +0 -115
  126. data/test/support/wire/synchrony.rb +0 -24
  127. data/test/support/wire/thread.rb +0 -5
  128. data/test/synchrony_driver.rb +0 -88
  129. data/test/test.conf +0 -9
  130. data/test/thread_safety_test.rb +0 -32
  131. data/test/transactions_test.rb +0 -264
  132. data/test/unknown_commands_test.rb +0 -14
  133. data/test/url_param_test.rb +0 -132
data/lib/redis.rb CHANGED
@@ -1,2106 +1,175 @@
1
- require "monitor"
2
- require "redis/errors"
3
-
4
- class Redis
5
-
6
- def self.deprecate(message, trace = caller[0])
7
- $stderr.puts "\n#{message} (in #{trace})"
8
- end
9
-
10
- attr :client
11
-
12
- # @deprecated The preferred way to create a new client object is using `#new`.
13
- # This method does not actually establish a connection to Redis,
14
- # in contrary to what you might expect.
15
- def self.connect(options = {})
16
- new(options)
17
- end
18
-
19
- def self.current
20
- @current ||= Redis.new
21
- end
22
-
23
- def self.current=(redis)
24
- @current = redis
25
- end
26
-
27
- include MonitorMixin
28
-
29
- def initialize(options = {})
30
- @options = options.dup
31
- @original_client = @client = Client.new(options)
32
-
33
- super() # Monitor#initialize
34
- end
35
-
36
- def synchronize
37
- mon_synchronize { yield(@client) }
38
- end
39
-
40
- # Run code with the client reconnecting
41
- def with_reconnect(val=true, &blk)
42
- synchronize do |client|
43
- client.with_reconnect(val, &blk)
44
- end
45
- end
46
-
47
- # Run code without the client reconnecting
48
- def without_reconnect(&blk)
49
- with_reconnect(false, &blk)
50
- end
51
-
52
- # Test whether or not the client is connected
53
- def connected?
54
- @original_client.connected?
55
- end
56
-
57
- # Authenticate to the server.
58
- #
59
- # @param [String] password must match the password specified in the
60
- # `requirepass` directive in the configuration file
61
- # @return [String] `OK`
62
- def auth(password)
63
- synchronize do |client|
64
- client.call([:auth, password])
65
- end
66
- end
67
-
68
- # Change the selected database for the current connection.
69
- #
70
- # @param [Fixnum] db zero-based index of the DB to use (0 to 15)
71
- # @return [String] `OK`
72
- def select(db)
73
- synchronize do |client|
74
- client.db = db
75
- client.call([:select, db])
76
- end
77
- end
78
-
79
- # Ping the server.
80
- #
81
- # @return [String] `PONG`
82
- def ping
83
- synchronize do |client|
84
- client.call([:ping])
85
- end
86
- end
87
-
88
- # Echo the given string.
89
- #
90
- # @param [String] value
91
- # @return [String]
92
- def echo(value)
93
- synchronize do |client|
94
- client.call([:echo, value])
95
- end
96
- end
97
-
98
- # Close the connection.
99
- #
100
- # @return [String] `OK`
101
- def quit
102
- synchronize do |client|
103
- begin
104
- client.call([:quit])
105
- rescue ConnectionError
106
- ensure
107
- client.disconnect
108
- end
109
- end
110
- end
111
-
112
- # Asynchronously rewrite the append-only file.
113
- #
114
- # @return [String] `OK`
115
- def bgrewriteaof
116
- synchronize do |client|
117
- client.call([:bgrewriteaof])
118
- end
119
- end
120
-
121
- # Asynchronously save the dataset to disk.
122
- #
123
- # @return [String] `OK`
124
- def bgsave
125
- synchronize do |client|
126
- client.call([:bgsave])
127
- end
128
- end
129
-
130
- # Get or set server configuration parameters.
131
- #
132
- # @param [Symbol] action e.g. `:get`, `:set`, `:resetstat`
133
- # @return [String, Hash] string reply, or hash when retrieving more than one
134
- # property with `CONFIG GET`
135
- def config(action, *args)
136
- synchronize do |client|
137
- client.call([:config, action] + args) do |reply|
138
- if reply.kind_of?(Array) && action == :get
139
- Hash[_pairify(reply)]
140
- else
141
- reply
142
- end
143
- end
144
- end
145
- end
146
-
147
- # Return the number of keys in the selected database.
148
- #
149
- # @return [Fixnum]
150
- def dbsize
151
- synchronize do |client|
152
- client.call([:dbsize])
153
- end
154
- end
155
-
156
- def debug(*args)
157
- synchronize do |client|
158
- client.call([:debug] + args)
159
- end
160
- end
161
-
162
- # Remove all keys from all databases.
163
- #
164
- # @return [String] `OK`
165
- def flushall
166
- synchronize do |client|
167
- client.call([:flushall])
168
- end
169
- end
170
-
171
- # Remove all keys from the current database.
172
- #
173
- # @return [String] `OK`
174
- def flushdb
175
- synchronize do |client|
176
- client.call([:flushdb])
177
- end
178
- end
179
-
180
- # Get information and statistics about the server.
181
- #
182
- # @param [String, Symbol] cmd e.g. "commandstats"
183
- # @return [Hash<String, String>]
184
- def info(cmd = nil)
185
- synchronize do |client|
186
- client.call([:info, cmd].compact) do |reply|
187
- if reply.kind_of?(String)
188
- reply = Hash[reply.split("\r\n").map do |line|
189
- line.split(":", 2) unless line =~ /^(#|$)/
190
- end.compact]
191
-
192
- if cmd && cmd.to_s == "commandstats"
193
- # Extract nested hashes for INFO COMMANDSTATS
194
- reply = Hash[reply.map do |k, v|
195
- v = v.split(",").map { |e| e.split("=") }
196
- [k[/^cmdstat_(.*)$/, 1], Hash[v]]
197
- end]
198
- end
199
- end
200
-
201
- reply
202
- end
203
- end
204
- end
205
-
206
- # Get the UNIX time stamp of the last successful save to disk.
207
- #
208
- # @return [Fixnum]
209
- def lastsave
210
- synchronize do |client|
211
- client.call([:lastsave])
212
- end
213
- end
214
-
215
- # Listen for all requests received by the server in real time.
216
- #
217
- # There is no way to interrupt this command.
218
- #
219
- # @yield a block to be called for every line of output
220
- # @yieldparam [String] line timestamp and command that was executed
221
- def monitor(&block)
222
- synchronize do |client|
223
- client.call_loop([:monitor], &block)
224
- end
225
- end
226
-
227
- # Synchronously save the dataset to disk.
228
- #
229
- # @return [String]
230
- def save
231
- synchronize do |client|
232
- client.call([:save])
233
- end
234
- end
235
-
236
- # Synchronously save the dataset to disk and then shut down the server.
237
- def shutdown
238
- synchronize do |client|
239
- client.with_reconnect(false) do
240
- begin
241
- client.call([:shutdown])
242
- rescue ConnectionError
243
- # This means Redis has probably exited.
244
- nil
245
- end
246
- end
247
- end
248
- end
249
-
250
- # Make the server a slave of another instance, or promote it as master.
251
- def slaveof(host, port)
252
- synchronize do |client|
253
- client.call([:slaveof, host, port])
254
- end
255
- end
256
-
257
- # Interact with the slowlog (get, len, reset)
258
- #
259
- # @param [String] subcommand e.g. `get`, `len`, `reset`
260
- # @param [Fixnum] length maximum number of entries to return
261
- # @return [Array<String>, Fixnum, String] depends on subcommand
262
- def slowlog(subcommand, length=nil)
263
- synchronize do |client|
264
- args = [:slowlog, subcommand]
265
- args << length if length
266
- client.call args
267
- end
268
- end
269
-
270
- # Internal command used for replication.
271
- def sync
272
- synchronize do |client|
273
- client.call([:sync])
274
- end
275
- end
276
-
277
- # Return the server time.
278
- #
279
- # @example
280
- # r.time # => [ 1333093196, 606806 ]
281
- #
282
- # @return [Array<Fixnum>] tuple of seconds since UNIX epoch and
283
- # microseconds in the current second
284
- def time
285
- synchronize do |client|
286
- client.call([:time]) do |reply|
287
- reply.map(&:to_i) if reply
288
- end
289
- end
290
- end
291
-
292
- # Remove the expiration from a key.
293
- #
294
- # @param [String] key
295
- # @return [Boolean] whether the timeout was removed or not
296
- def persist(key)
297
- synchronize do |client|
298
- client.call([:persist, key], &_boolify)
299
- end
300
- end
301
-
302
- # Set a key's time to live in seconds.
303
- #
304
- # @param [String] key
305
- # @param [Fixnum] seconds time to live
306
- # @return [Boolean] whether the timeout was set or not
307
- def expire(key, seconds)
308
- synchronize do |client|
309
- client.call([:expire, key, seconds], &_boolify)
310
- end
311
- end
312
-
313
- # Set the expiration for a key as a UNIX timestamp.
314
- #
315
- # @param [String] key
316
- # @param [Fixnum] unix_time expiry time specified as a UNIX timestamp
317
- # @return [Boolean] whether the timeout was set or not
318
- def expireat(key, unix_time)
319
- synchronize do |client|
320
- client.call([:expireat, key, unix_time], &_boolify)
321
- end
322
- end
323
-
324
- # Get the time to live (in seconds) for a key.
325
- #
326
- # @param [String] key
327
- # @return [Fixnum] remaining time to live in seconds, or -1 if the
328
- # key does not exist or does not have a timeout
329
- def ttl(key)
330
- synchronize do |client|
331
- client.call([:ttl, key])
332
- end
333
- end
334
-
335
- # Set a key's time to live in milliseconds.
336
- #
337
- # @param [String] key
338
- # @param [Fixnum] milliseconds time to live
339
- # @return [Boolean] whether the timeout was set or not
340
- def pexpire(key, milliseconds)
341
- synchronize do |client|
342
- client.call([:pexpire, key, milliseconds], &_boolify)
343
- end
344
- end
345
-
346
- # Set the expiration for a key as number of milliseconds from UNIX Epoch.
347
- #
348
- # @param [String] key
349
- # @param [Fixnum] ms_unix_time expiry time specified as number of milliseconds from UNIX Epoch.
350
- # @return [Boolean] whether the timeout was set or not
351
- def pexpireat(key, ms_unix_time)
352
- synchronize do |client|
353
- client.call([:pexpireat, key, ms_unix_time], &_boolify)
354
- end
355
- end
356
-
357
- # Get the time to live (in milliseconds) for a key.
358
- #
359
- # @param [String] key
360
- # @return [Fixnum] remaining time to live in milliseconds, or -1 if the
361
- # key does not exist or does not have a timeout
362
- def pttl(key)
363
- synchronize do |client|
364
- client.call([:pttl, key])
365
- end
366
- end
367
-
368
- # Return a serialized version of the value stored at a key.
369
- #
370
- # @param [String] key
371
- # @return [String] serialized_value
372
- def dump(key)
373
- synchronize do |client|
374
- client.call([:dump, key])
375
- end
376
- end
377
-
378
- # Create a key using the serialized value, previously obtained using DUMP.
379
- #
380
- # @param [String] key
381
- # @param [String] ttl
382
- # @param [String] serialized_value
383
- # @return `"OK"`
384
- def restore(key, ttl, serialized_value)
385
- synchronize do |client|
386
- client.call([:restore, key, ttl, serialized_value])
387
- end
388
- end
389
-
390
- # Transfer a key from the connected instance to another instance.
391
- #
392
- # @param [String] key
393
- # @param [Hash] options
394
- # - `:host => String`: host of instance to migrate to
395
- # - `:port => Integer`: port of instance to migrate to
396
- # - `:db => Integer`: database to migrate to (default: same as source)
397
- # - `:timeout => Integer`: timeout (default: same as connection timeout)
398
- # @return [String] `"OK"`
399
- def migrate(key, options)
400
- host = options[:host] || raise(RuntimeError, ":host not specified")
401
- port = options[:port] || raise(RuntimeError, ":port not specified")
402
- db = (options[:db] || client.db).to_i
403
- timeout = (options[:timeout] || client.timeout).to_i
404
-
405
- synchronize do |client|
406
- client.call([:migrate, host, port, key, db, timeout])
407
- end
408
- end
409
-
410
- # Delete one or more keys.
411
- #
412
- # @param [String, Array<String>] keys
413
- # @return [Fixnum] number of keys that were deleted
414
- def del(*keys)
415
- synchronize do |client|
416
- client.call([:del] + keys)
417
- end
418
- end
419
-
420
- # Determine if a key exists.
421
- #
422
- # @param [String] key
423
- # @return [Boolean]
424
- def exists(key)
425
- synchronize do |client|
426
- client.call([:exists, key], &_boolify)
427
- end
428
- end
429
-
430
- # Find all keys matching the given pattern.
431
- #
432
- # @param [String] pattern
433
- # @return [Array<String>]
434
- def keys(pattern = "*")
435
- synchronize do |client|
436
- client.call([:keys, pattern]) do |reply|
437
- if reply.kind_of?(String)
438
- reply.split(" ")
439
- else
440
- reply
441
- end
442
- end
443
- end
444
- end
445
-
446
- # Move a key to another database.
447
- #
448
- # @example Move a key to another database
449
- # redis.set "foo", "bar"
450
- # # => "OK"
451
- # redis.move "foo", 2
452
- # # => true
453
- # redis.exists "foo"
454
- # # => false
455
- # redis.select 2
456
- # # => "OK"
457
- # redis.exists "foo"
458
- # # => true
459
- # redis.get "foo"
460
- # # => "bar"
461
- #
462
- # @param [String] key
463
- # @param [Fixnum] db
464
- # @return [Boolean] whether the key was moved or not
465
- def move(key, db)
466
- synchronize do |client|
467
- client.call([:move, key, db], &_boolify)
468
- end
469
- end
470
-
471
- def object(*args)
472
- synchronize do |client|
473
- client.call([:object] + args)
474
- end
475
- end
476
-
477
- # Return a random key from the keyspace.
478
- #
479
- # @return [String]
480
- def randomkey
481
- synchronize do |client|
482
- client.call([:randomkey])
483
- end
484
- end
485
-
486
- # Rename a key. If the new key already exists it is overwritten.
487
- #
488
- # @param [String] old_name
489
- # @param [String] new_name
490
- # @return [String] `OK`
491
- def rename(old_name, new_name)
492
- synchronize do |client|
493
- client.call([:rename, old_name, new_name])
494
- end
495
- end
496
-
497
- # Rename a key, only if the new key does not exist.
498
- #
499
- # @param [String] old_name
500
- # @param [String] new_name
501
- # @return [Boolean] whether the key was renamed or not
502
- def renamenx(old_name, new_name)
503
- synchronize do |client|
504
- client.call([:renamenx, old_name, new_name], &_boolify)
505
- end
506
- end
507
-
508
- # Sort the elements in a list, set or sorted set.
509
- #
510
- # @example Retrieve the first 2 elements from an alphabetically sorted "list"
511
- # redis.sort("list", :order => "alpha", :limit => [0, 2])
512
- # # => ["a", "b"]
513
- # @example Store an alphabetically descending list in "target"
514
- # redis.sort("list", :order => "desc alpha", :store => "target")
515
- # # => 26
516
- #
517
- # @param [String] key
518
- # @param [Hash] options
519
- # - `:by => String`: use external key to sort elements by
520
- # - `:limit => [offset, count]`: skip `offset` elements, return a maximum
521
- # of `count` elements
522
- # - `:get => [String, Array<String>]`: single key or array of keys to
523
- # retrieve per element in the result
524
- # - `:order => String`: combination of `ASC`, `DESC` and optionally `ALPHA`
525
- # - `:store => String`: key to store the result at
526
- #
527
- # @return [Array<String>, Array<Array<String>>, Fixnum]
528
- # - when `:get` is not specified, or holds a single element, an array of elements
529
- # - when `:get` is specified, and holds more than one element, an array of
530
- # elements where every element is an array with the result for every
531
- # element specified in `:get`
532
- # - when `:store` is specified, the number of elements in the stored result
533
- def sort(key, options = {})
534
- args = []
535
-
536
- by = options[:by]
537
- args.concat(["BY", by]) if by
538
-
539
- limit = options[:limit]
540
- args.concat(["LIMIT"] + limit) if limit
541
-
542
- get = Array(options[:get])
543
- args.concat(["GET"].product(get).flatten) unless get.empty?
544
-
545
- order = options[:order]
546
- args.concat(order.split(" ")) if order
547
-
548
- store = options[:store]
549
- args.concat(["STORE", store]) if store
550
-
551
- synchronize do |client|
552
- client.call([:sort, key] + args) do |reply|
553
- if get.size > 1 && !store
554
- if reply
555
- reply.each_slice(get.size).to_a
556
- end
557
- else
558
- reply
559
- end
560
- end
561
- end
562
- end
563
-
564
- # Determine the type stored at key.
565
- #
566
- # @param [String] key
567
- # @return [String] `string`, `list`, `set`, `zset`, `hash` or `none`
568
- def type(key)
569
- synchronize do |client|
570
- client.call([:type, key])
571
- end
572
- end
573
-
574
- # Decrement the integer value of a key by one.
575
- #
576
- # @example
577
- # redis.decr("value")
578
- # # => 4
579
- #
580
- # @param [String] key
581
- # @return [Fixnum] value after decrementing it
582
- def decr(key)
583
- synchronize do |client|
584
- client.call([:decr, key])
585
- end
586
- end
587
-
588
- # Decrement the integer value of a key by the given number.
589
- #
590
- # @example
591
- # redis.decrby("value", 5)
592
- # # => 0
593
- #
594
- # @param [String] key
595
- # @param [Fixnum] decrement
596
- # @return [Fixnum] value after decrementing it
597
- def decrby(key, decrement)
598
- synchronize do |client|
599
- client.call([:decrby, key, decrement])
600
- end
601
- end
602
-
603
- # Increment the integer value of a key by one.
604
- #
605
- # @example
606
- # redis.incr("value")
607
- # # => 6
608
- #
609
- # @param [String] key
610
- # @return [Fixnum] value after incrementing it
611
- def incr(key)
612
- synchronize do |client|
613
- client.call([:incr, key])
614
- end
615
- end
616
-
617
- # Increment the integer value of a key by the given integer number.
618
- #
619
- # @example
620
- # redis.incrby("value", 5)
621
- # # => 10
622
- #
623
- # @param [String] key
624
- # @param [Fixnum] increment
625
- # @return [Fixnum] value after incrementing it
626
- def incrby(key, increment)
627
- synchronize do |client|
628
- client.call([:incrby, key, increment])
629
- end
630
- end
631
-
632
- # Increment the numeric value of a key by the given float number.
633
- #
634
- # @example
635
- # redis.incrbyfloat("value", 1.23)
636
- # # => 1.23
637
- #
638
- # @param [String] key
639
- # @param [Float] increment
640
- # @return [Float] value after incrementing it
641
- def incrbyfloat(key, increment)
642
- synchronize do |client|
643
- client.call([:incrbyfloat, key, increment], &_floatify)
644
- end
645
- end
646
-
647
- # Set the string value of a key.
648
- #
649
- # @param [String] key
650
- # @param [String] value
651
- # @param [Hash] options
652
- # - `:ex => Fixnum`: Set the specified expire time, in seconds.
653
- # - `:px => Fixnum`: Set the specified expire time, in milliseconds.
654
- # - `:nx => true`: Only set the key if it does not already exist.
655
- # - `:xx => true`: Only set the key if it already exist.
656
- # @return [String, Boolean] `"OK"` or true, false if `:nx => true` or `:xx => true`
657
- def set(key, value, options = {})
658
- args = []
659
-
660
- ex = options[:ex]
661
- args.concat(["EX", ex]) if ex
662
-
663
- px = options[:px]
664
- args.concat(["PX", px]) if px
665
-
666
- nx = options[:nx]
667
- args.concat(["NX"]) if nx
668
-
669
- xx = options[:xx]
670
- args.concat(["XX"]) if xx
671
-
672
- synchronize do |client|
673
- if nx || xx
674
- client.call([:set, key, value.to_s] + args, &_boolify_set)
675
- else
676
- client.call([:set, key, value.to_s] + args)
677
- end
678
- end
679
- end
680
-
681
- alias :[]= :set
682
-
683
- # Set the time to live in seconds of a key.
684
- #
685
- # @param [String] key
686
- # @param [Fixnum] ttl
687
- # @param [String] value
688
- # @return `"OK"`
689
- def setex(key, ttl, value)
690
- synchronize do |client|
691
- client.call([:setex, key, ttl, value.to_s])
692
- end
693
- end
694
-
695
- # Set the time to live in milliseconds of a key.
696
- #
697
- # @param [String] key
698
- # @param [Fixnum] ttl
699
- # @param [String] value
700
- # @return `"OK"`
701
- def psetex(key, ttl, value)
702
- synchronize do |client|
703
- client.call([:psetex, key, ttl, value.to_s])
704
- end
705
- end
706
-
707
- # Set the value of a key, only if the key does not exist.
708
- #
709
- # @param [String] key
710
- # @param [String] value
711
- # @return [Boolean] whether the key was set or not
712
- def setnx(key, value)
713
- synchronize do |client|
714
- client.call([:setnx, key, value.to_s], &_boolify)
715
- end
716
- end
717
-
718
- # Set one or more values.
719
- #
720
- # @example
721
- # redis.mset("key1", "v1", "key2", "v2")
722
- # # => "OK"
723
- #
724
- # @param [Array<String>] args array of keys and values
725
- # @return `"OK"`
726
- #
727
- # @see #mapped_mset
728
- def mset(*args)
729
- synchronize do |client|
730
- client.call([:mset] + args)
731
- end
732
- end
733
-
734
- # Set one or more values.
735
- #
736
- # @example
737
- # redis.mapped_mset({ "f1" => "v1", "f2" => "v2" })
738
- # # => "OK"
739
- #
740
- # @param [Hash] hash keys mapping to values
741
- # @return `"OK"`
742
- #
743
- # @see #mset
744
- def mapped_mset(hash)
745
- mset(hash.to_a.flatten)
746
- end
747
-
748
- # Set one or more values, only if none of the keys exist.
749
- #
750
- # @example
751
- # redis.msetnx("key1", "v1", "key2", "v2")
752
- # # => true
753
- #
754
- # @param [Array<String>] args array of keys and values
755
- # @return [Boolean] whether or not all values were set
756
- #
757
- # @see #mapped_msetnx
758
- def msetnx(*args)
759
- synchronize do |client|
760
- client.call([:msetnx] + args, &_boolify)
761
- end
762
- end
763
-
764
- # Set one or more values, only if none of the keys exist.
765
- #
766
- # @example
767
- # redis.msetnx({ "key1" => "v1", "key2" => "v2" })
768
- # # => true
769
- #
770
- # @param [Hash] hash keys mapping to values
771
- # @return [Boolean] whether or not all values were set
772
- #
773
- # @see #msetnx
774
- def mapped_msetnx(hash)
775
- msetnx(hash.to_a.flatten)
776
- end
777
-
778
- # Get the value of a key.
779
- #
780
- # @param [String] key
781
- # @return [String]
782
- def get(key)
783
- synchronize do |client|
784
- client.call([:get, key])
785
- end
786
- end
787
-
788
- alias :[] :get
789
-
790
- # Get the values of all the given keys.
791
- #
792
- # @example
793
- # redis.mget("key1", "key1")
794
- # # => ["v1", "v2"]
795
- #
796
- # @param [Array<String>] keys
797
- # @return [Array<String>] an array of values for the specified keys
798
- #
799
- # @see #mapped_mget
800
- def mget(*keys, &blk)
801
- synchronize do |client|
802
- client.call([:mget] + keys, &blk)
803
- end
804
- end
805
-
806
- # Get the values of all the given keys.
807
- #
808
- # @example
809
- # redis.mapped_mget("key1", "key1")
810
- # # => { "key1" => "v1", "key2" => "v2" }
811
- #
812
- # @param [Array<String>] keys array of keys
813
- # @return [Hash] a hash mapping the specified keys to their values
814
- #
815
- # @see #mget
816
- def mapped_mget(*keys)
817
- mget(*keys) do |reply|
818
- if reply.kind_of?(Array)
819
- Hash[keys.zip(reply)]
820
- else
821
- reply
822
- end
823
- end
824
- end
825
-
826
- # Overwrite part of a string at key starting at the specified offset.
827
- #
828
- # @param [String] key
829
- # @param [Fixnum] offset byte offset
830
- # @param [String] value
831
- # @return [Fixnum] length of the string after it was modified
832
- def setrange(key, offset, value)
833
- synchronize do |client|
834
- client.call([:setrange, key, offset, value.to_s])
835
- end
836
- end
837
-
838
- # Get a substring of the string stored at a key.
839
- #
840
- # @param [String] key
841
- # @param [Fixnum] start zero-based start offset
842
- # @param [Fixnum] stop zero-based end offset. Use -1 for representing
843
- # the end of the string
844
- # @return [Fixnum] `0` or `1`
845
- def getrange(key, start, stop)
846
- synchronize do |client|
847
- client.call([:getrange, key, start, stop])
848
- end
849
- end
850
-
851
- # Sets or clears the bit at offset in the string value stored at key.
852
- #
853
- # @param [String] key
854
- # @param [Fixnum] offset bit offset
855
- # @param [Fixnum] value bit value `0` or `1`
856
- # @return [Fixnum] the original bit value stored at `offset`
857
- def setbit(key, offset, value)
858
- synchronize do |client|
859
- client.call([:setbit, key, offset, value])
860
- end
861
- end
862
-
863
- # Returns the bit value at offset in the string value stored at key.
864
- #
865
- # @param [String] key
866
- # @param [Fixnum] offset bit offset
867
- # @return [Fixnum] `0` or `1`
868
- def getbit(key, offset)
869
- synchronize do |client|
870
- client.call([:getbit, key, offset])
871
- end
872
- end
873
-
874
- # Append a value to a key.
875
- #
876
- # @param [String] key
877
- # @param [String] value value to append
878
- # @return [Fixnum] length of the string after appending
879
- def append(key, value)
880
- synchronize do |client|
881
- client.call([:append, key, value])
882
- end
883
- end
884
-
885
- # Count the number of set bits in a range of the string value stored at key.
886
- #
887
- # @param [String] key
888
- # @param [Fixnum] start start index
889
- # @param [Fixnum] stop stop index
890
- # @return [Fixnum] the number of bits set to 1
891
- def bitcount(key, start = 0, stop = -1)
892
- synchronize do |client|
893
- client.call([:bitcount, key, start, stop])
894
- end
895
- end
896
-
897
- # Perform a bitwise operation between strings and store the resulting string in a key.
898
- #
899
- # @param [String] operation e.g. `and`, `or`, `xor`, `not`
900
- # @param [String] destkey destination key
901
- # @param [String, Array<String>] keys one or more source keys to perform `operation`
902
- # @return [Fixnum] the length of the string stored in `destkey`
903
- def bitop(operation, destkey, *keys)
904
- synchronize do |client|
905
- client.call([:bitop, operation, destkey] + keys)
906
- end
907
- end
908
-
909
- # Return the position of the first bit set to 1 or 0 in a string.
910
- #
911
- # @param [String] key
912
- # @param [Fixnum] bit whether to look for the first 1 or 0 bit
913
- # @param [Fixnum] start start index
914
- # @param [Fixnum] stop stop index
915
- # @return [Fixnum] the position of the first 1/0 bit.
916
- # -1 if looking for 1 and it is not found or start and stop are given.
917
- def bitpos(key, bit, start=nil, stop=nil)
918
- if stop and not start
919
- raise(ArgumentError, 'stop parameter specified without start parameter')
920
- end
921
-
922
- synchronize do |client|
923
- command = [:bitpos, key, bit]
924
- command << start if start
925
- command << stop if stop
926
- client.call(command)
927
- end
928
- end
929
-
930
- # Set the string value of a key and return its old value.
931
- #
932
- # @param [String] key
933
- # @param [String] value value to replace the current value with
934
- # @return [String] the old value stored in the key, or `nil` if the key
935
- # did not exist
936
- def getset(key, value)
937
- synchronize do |client|
938
- client.call([:getset, key, value.to_s])
939
- end
940
- end
941
-
942
- # Get the length of the value stored in a key.
943
- #
944
- # @param [String] key
945
- # @return [Fixnum] the length of the value stored in the key, or 0
946
- # if the key does not exist
947
- def strlen(key)
948
- synchronize do |client|
949
- client.call([:strlen, key])
950
- end
951
- end
952
-
953
- # Get the length of a list.
954
- #
955
- # @param [String] key
956
- # @return [Fixnum]
957
- def llen(key)
958
- synchronize do |client|
959
- client.call([:llen, key])
960
- end
961
- end
962
-
963
- # Prepend one or more values to a list, creating the list if it doesn't exist
964
- #
965
- # @param [String] key
966
- # @param [String, Array] string value, or array of string values to push
967
- # @return [Fixnum] the length of the list after the push operation
968
- def lpush(key, value)
969
- synchronize do |client|
970
- client.call([:lpush, key, value])
971
- end
972
- end
973
-
974
- # Prepend a value to a list, only if the list exists.
975
- #
976
- # @param [String] key
977
- # @param [String] value
978
- # @return [Fixnum] the length of the list after the push operation
979
- def lpushx(key, value)
980
- synchronize do |client|
981
- client.call([:lpushx, key, value])
982
- end
983
- end
984
-
985
- # Append one or more values to a list, creating the list if it doesn't exist
986
- #
987
- # @param [String] key
988
- # @param [String] value
989
- # @return [Fixnum] the length of the list after the push operation
990
- def rpush(key, value)
991
- synchronize do |client|
992
- client.call([:rpush, key, value])
993
- end
994
- end
995
-
996
- # Append a value to a list, only if the list exists.
997
- #
998
- # @param [String] key
999
- # @param [String] value
1000
- # @return [Fixnum] the length of the list after the push operation
1001
- def rpushx(key, value)
1002
- synchronize do |client|
1003
- client.call([:rpushx, key, value])
1004
- end
1005
- end
1006
-
1007
- # Remove and get the first element in a list.
1008
- #
1009
- # @param [String] key
1010
- # @return [String]
1011
- def lpop(key)
1012
- synchronize do |client|
1013
- client.call([:lpop, key])
1014
- end
1015
- end
1016
-
1017
- # Remove and get the last element in a list.
1018
- #
1019
- # @param [String] key
1020
- # @return [String]
1021
- def rpop(key)
1022
- synchronize do |client|
1023
- client.call([:rpop, key])
1024
- end
1025
- end
1026
-
1027
- # Remove the last element in a list, append it to another list and return it.
1028
- #
1029
- # @param [String] source source key
1030
- # @param [String] destination destination key
1031
- # @return [nil, String] the element, or nil when the source key does not exist
1032
- def rpoplpush(source, destination)
1033
- synchronize do |client|
1034
- client.call([:rpoplpush, source, destination])
1035
- end
1036
- end
1037
-
1038
- def _bpop(cmd, args)
1039
- options = {}
1040
-
1041
- case args.last
1042
- when Hash
1043
- options = args.pop
1044
- when Integer
1045
- # Issue deprecation notice in obnoxious mode...
1046
- options[:timeout] = args.pop
1047
- end
1048
-
1049
- if args.size > 1
1050
- # Issue deprecation notice in obnoxious mode...
1051
- end
1052
-
1053
- keys = args.flatten
1054
- timeout = options[:timeout] || 0
1055
-
1056
- synchronize do |client|
1057
- command = [cmd, keys, timeout]
1058
- timeout += client.timeout if timeout > 0
1059
- client.call_with_timeout(command, timeout)
1060
- end
1061
- end
1062
-
1063
- # Remove and get the first element in a list, or block until one is available.
1064
- #
1065
- # @example With timeout
1066
- # list, element = redis.blpop("list", :timeout => 5)
1067
- # # => nil on timeout
1068
- # # => ["list", "element"] on success
1069
- # @example Without timeout
1070
- # list, element = redis.blpop("list")
1071
- # # => ["list", "element"]
1072
- # @example Blocking pop on multiple lists
1073
- # list, element = redis.blpop(["list", "another_list"])
1074
- # # => ["list", "element"]
1075
- #
1076
- # @param [String, Array<String>] keys one or more keys to perform the
1077
- # blocking pop on
1078
- # @param [Hash] options
1079
- # - `:timeout => Fixnum`: timeout in seconds, defaults to no timeout
1080
- #
1081
- # @return [nil, [String, String]]
1082
- # - `nil` when the operation timed out
1083
- # - tuple of the list that was popped from and element was popped otherwise
1084
- def blpop(*args)
1085
- _bpop(:blpop, args)
1086
- end
1087
-
1088
- # Remove and get the last element in a list, or block until one is available.
1089
- #
1090
- # @param [String, Array<String>] keys one or more keys to perform the
1091
- # blocking pop on
1092
- # @param [Hash] options
1093
- # - `:timeout => Fixnum`: timeout in seconds, defaults to no timeout
1094
- #
1095
- # @return [nil, [String, String]]
1096
- # - `nil` when the operation timed out
1097
- # - tuple of the list that was popped from and element was popped otherwise
1098
- #
1099
- # @see #blpop
1100
- def brpop(*args)
1101
- _bpop(:brpop, args)
1102
- end
1103
-
1104
- # Pop a value from a list, push it to another list and return it; or block
1105
- # until one is available.
1106
- #
1107
- # @param [String] source source key
1108
- # @param [String] destination destination key
1109
- # @param [Hash] options
1110
- # - `:timeout => Fixnum`: timeout in seconds, defaults to no timeout
1111
- #
1112
- # @return [nil, String]
1113
- # - `nil` when the operation timed out
1114
- # - the element was popped and pushed otherwise
1115
- def brpoplpush(source, destination, options = {})
1116
- case options
1117
- when Integer
1118
- # Issue deprecation notice in obnoxious mode...
1119
- options = { :timeout => options }
1120
- end
1121
-
1122
- timeout = options[:timeout] || 0
1123
-
1124
- synchronize do |client|
1125
- command = [:brpoplpush, source, destination, timeout]
1126
- timeout += client.timeout if timeout > 0
1127
- client.call_with_timeout(command, timeout)
1128
- end
1129
- end
1130
-
1131
- # Get an element from a list by its index.
1132
- #
1133
- # @param [String] key
1134
- # @param [Fixnum] index
1135
- # @return [String]
1136
- def lindex(key, index)
1137
- synchronize do |client|
1138
- client.call([:lindex, key, index])
1139
- end
1140
- end
1141
-
1142
- # Insert an element before or after another element in a list.
1143
- #
1144
- # @param [String] key
1145
- # @param [String, Symbol] where `BEFORE` or `AFTER`
1146
- # @param [String] pivot reference element
1147
- # @param [String] value
1148
- # @return [Fixnum] length of the list after the insert operation, or `-1`
1149
- # when the element `pivot` was not found
1150
- def linsert(key, where, pivot, value)
1151
- synchronize do |client|
1152
- client.call([:linsert, key, where, pivot, value])
1153
- end
1154
- end
1155
-
1156
- # Get a range of elements from a list.
1157
- #
1158
- # @param [String] key
1159
- # @param [Fixnum] start start index
1160
- # @param [Fixnum] stop stop index
1161
- # @return [Array<String>]
1162
- def lrange(key, start, stop)
1163
- synchronize do |client|
1164
- client.call([:lrange, key, start, stop])
1165
- end
1166
- end
1167
-
1168
- # Remove elements from a list.
1169
- #
1170
- # @param [String] key
1171
- # @param [Fixnum] count number of elements to remove. Use a positive
1172
- # value to remove the first `count` occurrences of `value`. A negative
1173
- # value to remove the last `count` occurrences of `value`. Or zero, to
1174
- # remove all occurrences of `value` from the list.
1175
- # @param [String] value
1176
- # @return [Fixnum] the number of removed elements
1177
- def lrem(key, count, value)
1178
- synchronize do |client|
1179
- client.call([:lrem, key, count, value])
1180
- end
1181
- end
1182
-
1183
- # Set the value of an element in a list by its index.
1184
- #
1185
- # @param [String] key
1186
- # @param [Fixnum] index
1187
- # @param [String] value
1188
- # @return [String] `OK`
1189
- def lset(key, index, value)
1190
- synchronize do |client|
1191
- client.call([:lset, key, index, value])
1192
- end
1193
- end
1194
-
1195
- # Trim a list to the specified range.
1196
- #
1197
- # @param [String] key
1198
- # @param [Fixnum] start start index
1199
- # @param [Fixnum] stop stop index
1200
- # @return [String] `OK`
1201
- def ltrim(key, start, stop)
1202
- synchronize do |client|
1203
- client.call([:ltrim, key, start, stop])
1204
- end
1205
- end
1206
-
1207
- # Get the number of members in a set.
1208
- #
1209
- # @param [String] key
1210
- # @return [Fixnum]
1211
- def scard(key)
1212
- synchronize do |client|
1213
- client.call([:scard, key])
1214
- end
1215
- end
1216
-
1217
- # Add one or more members to a set.
1218
- #
1219
- # @param [String] key
1220
- # @param [String, Array<String>] member one member, or array of members
1221
- # @return [Boolean, Fixnum] `Boolean` when a single member is specified,
1222
- # holding whether or not adding the member succeeded, or `Fixnum` when an
1223
- # array of members is specified, holding the number of members that were
1224
- # successfully added
1225
- def sadd(key, member)
1226
- synchronize do |client|
1227
- client.call([:sadd, key, member]) do |reply|
1228
- if member.is_a? Array
1229
- # Variadic: return integer
1230
- reply
1231
- else
1232
- # Single argument: return boolean
1233
- _boolify.call(reply)
1234
- end
1235
- end
1236
- end
1237
- end
1238
-
1239
- # Remove one or more members from a set.
1240
- #
1241
- # @param [String] key
1242
- # @param [String, Array<String>] member one member, or array of members
1243
- # @return [Boolean, Fixnum] `Boolean` when a single member is specified,
1244
- # holding whether or not removing the member succeeded, or `Fixnum` when an
1245
- # array of members is specified, holding the number of members that were
1246
- # successfully removed
1247
- def srem(key, member)
1248
- synchronize do |client|
1249
- client.call([:srem, key, member]) do |reply|
1250
- if member.is_a? Array
1251
- # Variadic: return integer
1252
- reply
1253
- else
1254
- # Single argument: return boolean
1255
- _boolify.call(reply)
1256
- end
1257
- end
1258
- end
1259
- end
1260
-
1261
- # Remove and return a random member from a set.
1262
- #
1263
- # @param [String] key
1264
- # @return [String]
1265
- def spop(key)
1266
- synchronize do |client|
1267
- client.call([:spop, key])
1268
- end
1269
- end
1270
-
1271
- # Get one or more random members from a set.
1272
- #
1273
- # @param [String] key
1274
- # @param [Fixnum] count
1275
- # @return [String]
1276
- def srandmember(key, count = nil)
1277
- synchronize do |client|
1278
- if count.nil?
1279
- client.call([:srandmember, key])
1280
- else
1281
- client.call([:srandmember, key, count])
1282
- end
1283
- end
1284
- end
1285
-
1286
- # Move a member from one set to another.
1287
- #
1288
- # @param [String] source source key
1289
- # @param [String] destination destination key
1290
- # @param [String] member member to move from `source` to `destination`
1291
- # @return [Boolean]
1292
- def smove(source, destination, member)
1293
- synchronize do |client|
1294
- client.call([:smove, source, destination, member], &_boolify)
1295
- end
1296
- end
1297
-
1298
- # Determine if a given value is a member of a set.
1299
- #
1300
- # @param [String] key
1301
- # @param [String] member
1302
- # @return [Boolean]
1303
- def sismember(key, member)
1304
- synchronize do |client|
1305
- client.call([:sismember, key, member], &_boolify)
1306
- end
1307
- end
1308
-
1309
- # Get all the members in a set.
1310
- #
1311
- # @param [String] key
1312
- # @return [Array<String>]
1313
- def smembers(key)
1314
- synchronize do |client|
1315
- client.call([:smembers, key])
1316
- end
1317
- end
1318
-
1319
- # Subtract multiple sets.
1320
- #
1321
- # @param [String, Array<String>] keys keys pointing to sets to subtract
1322
- # @return [Array<String>] members in the difference
1323
- def sdiff(*keys)
1324
- synchronize do |client|
1325
- client.call([:sdiff] + keys)
1326
- end
1327
- end
1328
-
1329
- # Subtract multiple sets and store the resulting set in a key.
1330
- #
1331
- # @param [String] destination destination key
1332
- # @param [String, Array<String>] keys keys pointing to sets to subtract
1333
- # @return [Fixnum] number of elements in the resulting set
1334
- def sdiffstore(destination, *keys)
1335
- synchronize do |client|
1336
- client.call([:sdiffstore, destination] + keys)
1337
- end
1338
- end
1339
-
1340
- # Intersect multiple sets.
1341
- #
1342
- # @param [String, Array<String>] keys keys pointing to sets to intersect
1343
- # @return [Array<String>] members in the intersection
1344
- def sinter(*keys)
1345
- synchronize do |client|
1346
- client.call([:sinter] + keys)
1347
- end
1348
- end
1349
-
1350
- # Intersect multiple sets and store the resulting set in a key.
1351
- #
1352
- # @param [String] destination destination key
1353
- # @param [String, Array<String>] keys keys pointing to sets to intersect
1354
- # @return [Fixnum] number of elements in the resulting set
1355
- def sinterstore(destination, *keys)
1356
- synchronize do |client|
1357
- client.call([:sinterstore, destination] + keys)
1358
- end
1359
- end
1360
-
1361
- # Add multiple sets.
1362
- #
1363
- # @param [String, Array<String>] keys keys pointing to sets to unify
1364
- # @return [Array<String>] members in the union
1365
- def sunion(*keys)
1366
- synchronize do |client|
1367
- client.call([:sunion] + keys)
1368
- end
1369
- end
1370
-
1371
- # Add multiple sets and store the resulting set in a key.
1372
- #
1373
- # @param [String] destination destination key
1374
- # @param [String, Array<String>] keys keys pointing to sets to unify
1375
- # @return [Fixnum] number of elements in the resulting set
1376
- def sunionstore(destination, *keys)
1377
- synchronize do |client|
1378
- client.call([:sunionstore, destination] + keys)
1379
- end
1380
- end
1381
-
1382
- # Get the number of members in a sorted set.
1383
- #
1384
- # @example
1385
- # redis.zcard("zset")
1386
- # # => 4
1387
- #
1388
- # @param [String] key
1389
- # @return [Fixnum]
1390
- def zcard(key)
1391
- synchronize do |client|
1392
- client.call([:zcard, key])
1393
- end
1394
- end
1395
-
1396
- # Add one or more members to a sorted set, or update the score for members
1397
- # that already exist.
1398
- #
1399
- # @example Add a single `[score, member]` pair to a sorted set
1400
- # redis.zadd("zset", 32.0, "member")
1401
- # @example Add an array of `[score, member]` pairs to a sorted set
1402
- # redis.zadd("zset", [[32.0, "a"], [64.0, "b"]])
1403
- #
1404
- # @param [String] key
1405
- # @param [[Float, String], Array<[Float, String]>] args
1406
- # - a single `[score, member]` pair
1407
- # - an array of `[score, member]` pairs
1408
- #
1409
- # @return [Boolean, Fixnum]
1410
- # - `Boolean` when a single pair is specified, holding whether or not it was
1411
- # **added** to the sorted set
1412
- # - `Fixnum` when an array of pairs is specified, holding the number of
1413
- # pairs that were **added** to the sorted set
1414
- def zadd(key, *args)
1415
- synchronize do |client|
1416
- if args.size == 1 && args[0].is_a?(Array)
1417
- # Variadic: return integer
1418
- client.call([:zadd, key] + args[0])
1419
- elsif args.size == 2
1420
- # Single pair: return boolean
1421
- client.call([:zadd, key, args[0], args[1]], &_boolify)
1422
- else
1423
- raise ArgumentError, "wrong number of arguments"
1424
- end
1425
- end
1426
- end
1427
-
1428
- # Increment the score of a member in a sorted set.
1429
- #
1430
- # @example
1431
- # redis.zincrby("zset", 32.0, "a")
1432
- # # => 64.0
1433
- #
1434
- # @param [String] key
1435
- # @param [Float] increment
1436
- # @param [String] member
1437
- # @return [Float] score of the member after incrementing it
1438
- def zincrby(key, increment, member)
1439
- synchronize do |client|
1440
- client.call([:zincrby, key, increment, member], &_floatify)
1441
- end
1442
- end
1443
-
1444
- # Remove one or more members from a sorted set.
1445
- #
1446
- # @example Remove a single member from a sorted set
1447
- # redis.zrem("zset", "a")
1448
- # @example Remove an array of members from a sorted set
1449
- # redis.zrem("zset", ["a", "b"])
1450
- #
1451
- # @param [String] key
1452
- # @param [String, Array<String>] member
1453
- # - a single member
1454
- # - an array of members
1455
- #
1456
- # @return [Boolean, Fixnum]
1457
- # - `Boolean` when a single member is specified, holding whether or not it
1458
- # was removed from the sorted set
1459
- # - `Fixnum` when an array of pairs is specified, holding the number of
1460
- # members that were removed to the sorted set
1461
- def zrem(key, member)
1462
- synchronize do |client|
1463
- client.call([:zrem, key, member]) do |reply|
1464
- if member.is_a? Array
1465
- # Variadic: return integer
1466
- reply
1467
- else
1468
- # Single argument: return boolean
1469
- _boolify.call(reply)
1470
- end
1471
- end
1472
- end
1473
- end
1474
-
1475
- # Get the score associated with the given member in a sorted set.
1476
- #
1477
- # @example Get the score for member "a"
1478
- # redis.zscore("zset", "a")
1479
- # # => 32.0
1480
- #
1481
- # @param [String] key
1482
- # @param [String] member
1483
- # @return [Float] score of the member
1484
- def zscore(key, member)
1485
- synchronize do |client|
1486
- client.call([:zscore, key, member], &_floatify)
1487
- end
1488
- end
1489
-
1490
- # Return a range of members in a sorted set, by index.
1491
- #
1492
- # @example Retrieve all members from a sorted set
1493
- # redis.zrange("zset", 0, -1)
1494
- # # => ["a", "b"]
1495
- # @example Retrieve all members and their scores from a sorted set
1496
- # redis.zrange("zset", 0, -1, :with_scores => true)
1497
- # # => [["a", 32.0], ["b", 64.0]]
1498
- #
1499
- # @param [String] key
1500
- # @param [Fixnum] start start index
1501
- # @param [Fixnum] stop stop index
1502
- # @param [Hash] options
1503
- # - `:with_scores => true`: include scores in output
1504
- #
1505
- # @return [Array<String>, Array<[String, Float]>]
1506
- # - when `:with_scores` is not specified, an array of members
1507
- # - when `:with_scores` is specified, an array with `[member, score]` pairs
1508
- def zrange(key, start, stop, options = {})
1509
- args = []
1510
-
1511
- with_scores = options[:with_scores] || options[:withscores]
1512
-
1513
- if with_scores
1514
- args << "WITHSCORES"
1515
- block = _floatify_pairs
1516
- end
1517
-
1518
- synchronize do |client|
1519
- client.call([:zrange, key, start, stop] + args, &block)
1520
- end
1521
- end
1522
-
1523
- # Return a range of members in a sorted set, by index, with scores ordered
1524
- # from high to low.
1525
- #
1526
- # @example Retrieve all members from a sorted set
1527
- # redis.zrevrange("zset", 0, -1)
1528
- # # => ["b", "a"]
1529
- # @example Retrieve all members and their scores from a sorted set
1530
- # redis.zrevrange("zset", 0, -1, :with_scores => true)
1531
- # # => [["b", 64.0], ["a", 32.0]]
1532
- #
1533
- # @see #zrange
1534
- def zrevrange(key, start, stop, options = {})
1535
- args = []
1536
-
1537
- with_scores = options[:with_scores] || options[:withscores]
1538
-
1539
- if with_scores
1540
- args << "WITHSCORES"
1541
- block = _floatify_pairs
1542
- end
1543
-
1544
- synchronize do |client|
1545
- client.call([:zrevrange, key, start, stop] + args, &block)
1546
- end
1547
- end
1548
-
1549
- # Determine the index of a member in a sorted set.
1550
- #
1551
- # @param [String] key
1552
- # @param [String] member
1553
- # @return [Fixnum]
1554
- def zrank(key, member)
1555
- synchronize do |client|
1556
- client.call([:zrank, key, member])
1557
- end
1558
- end
1559
-
1560
- # Determine the index of a member in a sorted set, with scores ordered from
1561
- # high to low.
1562
- #
1563
- # @param [String] key
1564
- # @param [String] member
1565
- # @return [Fixnum]
1566
- def zrevrank(key, member)
1567
- synchronize do |client|
1568
- client.call([:zrevrank, key, member])
1569
- end
1570
- end
1571
-
1572
- # Remove all members in a sorted set within the given indexes.
1573
- #
1574
- # @example Remove first 5 members
1575
- # redis.zremrangebyrank("zset", 0, 4)
1576
- # # => 5
1577
- # @example Remove last 5 members
1578
- # redis.zremrangebyrank("zset", -5, -1)
1579
- # # => 5
1580
- #
1581
- # @param [String] key
1582
- # @param [Fixnum] start start index
1583
- # @param [Fixnum] stop stop index
1584
- # @return [Fixnum] number of members that were removed
1585
- def zremrangebyrank(key, start, stop)
1586
- synchronize do |client|
1587
- client.call([:zremrangebyrank, key, start, stop])
1588
- end
1589
- end
1590
-
1591
- # Return a range of members with the same score in a sorted set, by lexicographical ordering
1592
- #
1593
- # @example Retrieve members matching a
1594
- # redis.zrangebylex("zset", "[a", "[a\xff")
1595
- # # => ["aaren", "aarika", "abagael", "abby"]
1596
- # @example Retrieve the first 2 members matching a
1597
- # redis.zrangebylex("zset", "[a", "[a\xff", :limit => [0, 2])
1598
- # # => ["aaren", "aarika"]
1599
- #
1600
- # @param [String] key
1601
- # @param [String] min
1602
- # - inclusive minimum is specified by prefixing `(`
1603
- # - exclusive minimum is specified by prefixing `[`
1604
- # @param [String] max
1605
- # - inclusive maximum is specified by prefixing `(`
1606
- # - exclusive maximum is specified by prefixing `[`
1607
- # @param [Hash] options
1608
- # - `:limit => [offset, count]`: skip `offset` members, return a maximum of
1609
- # `count` members
1610
- #
1611
- # @return [Array<String>, Array<[String, Float]>]
1612
- def zrangebylex(key, min, max, options = {})
1613
- args = []
1614
-
1615
- limit = options[:limit]
1616
- args.concat(["LIMIT"] + limit) if limit
1617
-
1618
- synchronize do |client|
1619
- client.call([:zrangebylex, key, min, max] + args)
1620
- end
1621
- end
1622
-
1623
- # Return a range of members in a sorted set, by score.
1624
- #
1625
- # @example Retrieve members with score `>= 5` and `< 100`
1626
- # redis.zrangebyscore("zset", "5", "(100")
1627
- # # => ["a", "b"]
1628
- # @example Retrieve the first 2 members with score `>= 0`
1629
- # redis.zrangebyscore("zset", "0", "+inf", :limit => [0, 2])
1630
- # # => ["a", "b"]
1631
- # @example Retrieve members and their scores with scores `> 5`
1632
- # redis.zrangebyscore("zset", "(5", "+inf", :with_scores => true)
1633
- # # => [["a", 32.0], ["b", 64.0]]
1634
- #
1635
- # @param [String] key
1636
- # @param [String] min
1637
- # - inclusive minimum score is specified verbatim
1638
- # - exclusive minimum score is specified by prefixing `(`
1639
- # @param [String] max
1640
- # - inclusive maximum score is specified verbatim
1641
- # - exclusive maximum score is specified by prefixing `(`
1642
- # @param [Hash] options
1643
- # - `:with_scores => true`: include scores in output
1644
- # - `:limit => [offset, count]`: skip `offset` members, return a maximum of
1645
- # `count` members
1646
- #
1647
- # @return [Array<String>, Array<[String, Float]>]
1648
- # - when `:with_scores` is not specified, an array of members
1649
- # - when `:with_scores` is specified, an array with `[member, score]` pairs
1650
- def zrangebyscore(key, min, max, options = {})
1651
- args = []
1652
-
1653
- with_scores = options[:with_scores] || options[:withscores]
1654
-
1655
- if with_scores
1656
- args << "WITHSCORES"
1657
- block = _floatify_pairs
1658
- end
1659
-
1660
- limit = options[:limit]
1661
- args.concat(["LIMIT"] + limit) if limit
1662
-
1663
- synchronize do |client|
1664
- client.call([:zrangebyscore, key, min, max] + args, &block)
1665
- end
1666
- end
1667
-
1668
- # Return a range of members in a sorted set, by score, with scores ordered
1669
- # from high to low.
1670
- #
1671
- # @example Retrieve members with score `< 100` and `>= 5`
1672
- # redis.zrevrangebyscore("zset", "(100", "5")
1673
- # # => ["b", "a"]
1674
- # @example Retrieve the first 2 members with score `<= 0`
1675
- # redis.zrevrangebyscore("zset", "0", "-inf", :limit => [0, 2])
1676
- # # => ["b", "a"]
1677
- # @example Retrieve members and their scores with scores `> 5`
1678
- # redis.zrevrangebyscore("zset", "+inf", "(5", :with_scores => true)
1679
- # # => [["b", 64.0], ["a", 32.0]]
1680
- #
1681
- # @see #zrangebyscore
1682
- def zrevrangebyscore(key, max, min, options = {})
1683
- args = []
1684
-
1685
- with_scores = options[:with_scores] || options[:withscores]
1686
-
1687
- if with_scores
1688
- args << ["WITHSCORES"]
1689
- block = _floatify_pairs
1690
- end
1691
-
1692
- limit = options[:limit]
1693
- args.concat(["LIMIT"] + limit) if limit
1694
-
1695
- synchronize do |client|
1696
- client.call([:zrevrangebyscore, key, max, min] + args, &block)
1697
- end
1698
- end
1699
-
1700
- # Remove all members in a sorted set within the given scores.
1701
- #
1702
- # @example Remove members with score `>= 5` and `< 100`
1703
- # redis.zremrangebyscore("zset", "5", "(100")
1704
- # # => 2
1705
- # @example Remove members with scores `> 5`
1706
- # redis.zremrangebyscore("zset", "(5", "+inf")
1707
- # # => 2
1708
- #
1709
- # @param [String] key
1710
- # @param [String] min
1711
- # - inclusive minimum score is specified verbatim
1712
- # - exclusive minimum score is specified by prefixing `(`
1713
- # @param [String] max
1714
- # - inclusive maximum score is specified verbatim
1715
- # - exclusive maximum score is specified by prefixing `(`
1716
- # @return [Fixnum] number of members that were removed
1717
- def zremrangebyscore(key, min, max)
1718
- synchronize do |client|
1719
- client.call([:zremrangebyscore, key, min, max])
1720
- end
1721
- end
1722
-
1723
- # Count the members in a sorted set with scores within the given values.
1724
- #
1725
- # @example Count members with score `>= 5` and `< 100`
1726
- # redis.zcount("zset", "5", "(100")
1727
- # # => 2
1728
- # @example Count members with scores `> 5`
1729
- # redis.zcount("zset", "(5", "+inf")
1730
- # # => 2
1731
- #
1732
- # @param [String] key
1733
- # @param [String] min
1734
- # - inclusive minimum score is specified verbatim
1735
- # - exclusive minimum score is specified by prefixing `(`
1736
- # @param [String] max
1737
- # - inclusive maximum score is specified verbatim
1738
- # - exclusive maximum score is specified by prefixing `(`
1739
- # @return [Fixnum] number of members in within the specified range
1740
- def zcount(key, min, max)
1741
- synchronize do |client|
1742
- client.call([:zcount, key, min, max])
1743
- end
1744
- end
1745
-
1746
- # Intersect multiple sorted sets and store the resulting sorted set in a new
1747
- # key.
1748
- #
1749
- # @example Compute the intersection of `2*zsetA` with `1*zsetB`, summing their scores
1750
- # redis.zinterstore("zsetC", ["zsetA", "zsetB"], :weights => [2.0, 1.0], :aggregate => "sum")
1751
- # # => 4
1752
- #
1753
- # @param [String] destination destination key
1754
- # @param [Array<String>] keys source keys
1755
- # @param [Hash] options
1756
- # - `:weights => [Float, Float, ...]`: weights to associate with source
1757
- # sorted sets
1758
- # - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
1759
- # @return [Fixnum] number of elements in the resulting sorted set
1760
- def zinterstore(destination, keys, options = {})
1761
- args = []
1762
-
1763
- weights = options[:weights]
1764
- args.concat(["WEIGHTS"] + weights) if weights
1765
-
1766
- aggregate = options[:aggregate]
1767
- args.concat(["AGGREGATE", aggregate]) if aggregate
1768
-
1769
- synchronize do |client|
1770
- client.call([:zinterstore, destination, keys.size] + keys + args)
1771
- end
1772
- end
1773
-
1774
- # Add multiple sorted sets and store the resulting sorted set in a new key.
1775
- #
1776
- # @example Compute the union of `2*zsetA` with `1*zsetB`, summing their scores
1777
- # redis.zunionstore("zsetC", ["zsetA", "zsetB"], :weights => [2.0, 1.0], :aggregate => "sum")
1778
- # # => 8
1779
- #
1780
- # @param [String] destination destination key
1781
- # @param [Array<String>] keys source keys
1782
- # @param [Hash] options
1783
- # - `:weights => [Float, Float, ...]`: weights to associate with source
1784
- # sorted sets
1785
- # - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
1786
- # @return [Fixnum] number of elements in the resulting sorted set
1787
- def zunionstore(destination, keys, options = {})
1788
- args = []
1789
-
1790
- weights = options[:weights]
1791
- args.concat(["WEIGHTS"] + weights) if weights
1792
-
1793
- aggregate = options[:aggregate]
1794
- args.concat(["AGGREGATE", aggregate]) if aggregate
1795
-
1796
- synchronize do |client|
1797
- client.call([:zunionstore, destination, keys.size] + keys + args)
1798
- end
1799
- end
1800
-
1801
- # Get the number of fields in a hash.
1802
- #
1803
- # @param [String] key
1804
- # @return [Fixnum] number of fields in the hash
1805
- def hlen(key)
1806
- synchronize do |client|
1807
- client.call([:hlen, key])
1808
- end
1809
- end
1810
-
1811
- # Set the string value of a hash field.
1812
- #
1813
- # @param [String] key
1814
- # @param [String] field
1815
- # @param [String] value
1816
- # @return [Boolean] whether or not the field was **added** to the hash
1817
- def hset(key, field, value)
1818
- synchronize do |client|
1819
- client.call([:hset, key, field, value], &_boolify)
1820
- end
1821
- end
1822
-
1823
- # Set the value of a hash field, only if the field does not exist.
1824
- #
1825
- # @param [String] key
1826
- # @param [String] field
1827
- # @param [String] value
1828
- # @return [Boolean] whether or not the field was **added** to the hash
1829
- def hsetnx(key, field, value)
1830
- synchronize do |client|
1831
- client.call([:hsetnx, key, field, value], &_boolify)
1832
- end
1833
- end
1834
-
1835
- # Set one or more hash values.
1836
- #
1837
- # @example
1838
- # redis.hmset("hash", "f1", "v1", "f2", "v2")
1839
- # # => "OK"
1840
- #
1841
- # @param [String] key
1842
- # @param [Array<String>] attrs array of fields and values
1843
- # @return `"OK"`
1844
- #
1845
- # @see #mapped_hmset
1846
- def hmset(key, *attrs)
1847
- synchronize do |client|
1848
- client.call([:hmset, key] + attrs)
1849
- end
1850
- end
1851
-
1852
- # Set one or more hash values.
1853
- #
1854
- # @example
1855
- # redis.mapped_hmset("hash", { "f1" => "v1", "f2" => "v2" })
1856
- # # => "OK"
1857
- #
1858
- # @param [String] key
1859
- # @param [Hash] hash fields mapping to values
1860
- # @return `"OK"`
1861
- #
1862
- # @see #hmset
1863
- def mapped_hmset(key, hash)
1864
- hmset(key, hash.to_a.flatten)
1865
- end
1
+ # frozen_string_literal: true
1866
2
 
1867
- # Get the value of a hash field.
1868
- #
1869
- # @param [String] key
1870
- # @param [String] field
1871
- # @return [String]
1872
- def hget(key, field)
1873
- synchronize do |client|
1874
- client.call([:hget, key, field])
1875
- end
1876
- end
1877
-
1878
- # Get the values of all the given hash fields.
1879
- #
1880
- # @example
1881
- # redis.hmget("hash", "f1", "f2")
1882
- # # => ["v1", "v2"]
1883
- #
1884
- # @param [String] key
1885
- # @param [Array<String>] fields array of fields
1886
- # @return [Array<String>] an array of values for the specified fields
1887
- #
1888
- # @see #mapped_hmget
1889
- def hmget(key, *fields, &blk)
1890
- synchronize do |client|
1891
- client.call([:hmget, key] + fields, &blk)
1892
- end
1893
- end
3
+ require "monitor"
4
+ require "redis/errors"
5
+ require "redis/commands"
1894
6
 
1895
- # Get the values of all the given hash fields.
1896
- #
1897
- # @example
1898
- # redis.hmget("hash", "f1", "f2")
1899
- # # => { "f1" => "v1", "f2" => "v2" }
1900
- #
1901
- # @param [String] key
1902
- # @param [Array<String>] fields array of fields
1903
- # @return [Hash] a hash mapping the specified fields to their values
1904
- #
1905
- # @see #hmget
1906
- def mapped_hmget(key, *fields)
1907
- hmget(key, *fields) do |reply|
1908
- if reply.kind_of?(Array)
1909
- Hash[fields.zip(reply)]
1910
- else
1911
- reply
7
+ class Redis
8
+ BASE_PATH = __dir__
9
+ @exists_returns_integer = true
10
+
11
+ Deprecated = Class.new(StandardError)
12
+
13
+ class << self
14
+ attr_reader :exists_returns_integer
15
+ attr_accessor :silence_deprecations, :raise_deprecations
16
+
17
+ def exists_returns_integer=(value)
18
+ unless value
19
+ deprecate!(
20
+ "`Redis#exists(key)` will return an Integer by default in redis-rb 4.3. The option to explicitly " \
21
+ "disable this behaviour via `Redis.exists_returns_integer` will be removed in 5.0. You should use " \
22
+ "`exists?` instead."
23
+ )
1912
24
  end
1913
- end
1914
- end
1915
-
1916
- # Delete one or more hash fields.
1917
- #
1918
- # @param [String] key
1919
- # @param [String, Array<String>] field
1920
- # @return [Fixnum] the number of fields that were removed from the hash
1921
- def hdel(key, field)
1922
- synchronize do |client|
1923
- client.call([:hdel, key, field])
1924
- end
1925
- end
1926
25
 
1927
- # Determine if a hash field exists.
1928
- #
1929
- # @param [String] key
1930
- # @param [String] field
1931
- # @return [Boolean] whether or not the field exists in the hash
1932
- def hexists(key, field)
1933
- synchronize do |client|
1934
- client.call([:hexists, key, field], &_boolify)
26
+ @exists_returns_integer = value
1935
27
  end
1936
- end
1937
28
 
1938
- # Increment the integer value of a hash field by the given integer number.
1939
- #
1940
- # @param [String] key
1941
- # @param [String] field
1942
- # @param [Fixnum] increment
1943
- # @return [Fixnum] value of the field after incrementing it
1944
- def hincrby(key, field, increment)
1945
- synchronize do |client|
1946
- client.call([:hincrby, key, field, increment])
29
+ def deprecate!(message)
30
+ unless silence_deprecations
31
+ if raise_deprecations
32
+ raise Deprecated, message
33
+ else
34
+ ::Kernel.warn(message)
35
+ end
36
+ end
1947
37
  end
1948
- end
1949
38
 
1950
- # Increment the numeric value of a hash field by the given float number.
1951
- #
1952
- # @param [String] key
1953
- # @param [String] field
1954
- # @param [Float] increment
1955
- # @return [Float] value of the field after incrementing it
1956
- def hincrbyfloat(key, field, increment)
1957
- synchronize do |client|
1958
- client.call([:hincrbyfloat, key, field, increment], &_floatify)
39
+ def current
40
+ deprecate!("`Redis.current=` is deprecated and will be removed in 5.0. (called from: #{caller(1, 1).first})")
41
+ @current ||= Redis.new
1959
42
  end
1960
- end
1961
43
 
1962
- # Get all the fields in a hash.
1963
- #
1964
- # @param [String] key
1965
- # @return [Array<String>]
1966
- def hkeys(key)
1967
- synchronize do |client|
1968
- client.call([:hkeys, key])
44
+ def current=(redis)
45
+ deprecate!("`Redis.current=` is deprecated and will be removed in 5.0. (called from: #{caller(1, 1).first})")
46
+ @current = redis
1969
47
  end
1970
48
  end
1971
49
 
1972
- # Get all the values in a hash.
1973
- #
1974
- # @param [String] key
1975
- # @return [Array<String>]
1976
- def hvals(key)
1977
- synchronize do |client|
1978
- client.call([:hvals, key])
1979
- end
1980
- end
50
+ include Commands
1981
51
 
1982
- # Get all the fields and values in a hash.
52
+ # Create a new client instance
1983
53
  #
1984
- # @param [String] key
1985
- # @return [Hash<String, String>]
1986
- def hgetall(key)
1987
- synchronize do |client|
1988
- client.call([:hgetall, key], &_hashify)
1989
- end
54
+ # @param [Hash] options
55
+ # @option options [String] :url (value of the environment variable REDIS_URL) a Redis URL, for a TCP connection:
56
+ # `redis://:[password]@[hostname]:[port]/[db]` (password, port and database are optional), for a unix socket
57
+ # connection: `unix://[path to Redis socket]`. This overrides all other options.
58
+ # @option options [String] :host ("127.0.0.1") server hostname
59
+ # @option options [Integer] :port (6379) server port
60
+ # @option options [String] :path path to server socket (overrides host and port)
61
+ # @option options [Float] :timeout (5.0) timeout in seconds
62
+ # @option options [Float] :connect_timeout (same as timeout) timeout for initial connect in seconds
63
+ # @option options [String] :username Username to authenticate against server
64
+ # @option options [String] :password Password to authenticate against server
65
+ # @option options [Integer] :db (0) Database to select after initial connect
66
+ # @option options [Symbol] :driver Driver to use, currently supported: `:ruby`, `:hiredis`, `:synchrony`
67
+ # @option options [String] :id ID for the client connection, assigns name to current connection by sending
68
+ # `CLIENT SETNAME`
69
+ # @option options [Hash, Integer] :tcp_keepalive Keepalive values, if Integer `intvl` and `probe` are calculated
70
+ # based on the value, if Hash `time`, `intvl` and `probes` can be specified as a Integer
71
+ # @option options [Integer] :reconnect_attempts Number of attempts trying to connect
72
+ # @option options [Boolean] :inherit_socket (false) Whether to use socket in forked process or not
73
+ # @option options [Array] :sentinels List of sentinels to contact
74
+ # @option options [Symbol] :role (:master) Role to fetch via Sentinel, either `:master` or `:slave`
75
+ # @option options [Array<String, Hash{Symbol => String, Integer}>] :cluster List of cluster nodes to contact
76
+ # @option options [Boolean] :replica Whether to use readonly replica nodes in Redis Cluster or not
77
+ # @option options [Class] :connector Class of custom connector
78
+ #
79
+ # @return [Redis] a new client instance
80
+ def initialize(options = {})
81
+ @options = options.dup
82
+ @cluster_mode = options.key?(:cluster)
83
+ client = @cluster_mode ? Cluster : Client
84
+ @original_client = @client = client.new(options)
85
+ @queue = Hash.new { |h, k| h[k] = [] }
86
+ @monitor = Monitor.new
1990
87
  end
1991
88
 
1992
- # Post a message to a channel.
1993
- def publish(channel, message)
89
+ # Run code with the client reconnecting
90
+ def with_reconnect(val = true, &blk)
1994
91
  synchronize do |client|
1995
- client.call([:publish, channel, message])
92
+ client.with_reconnect(val, &blk)
1996
93
  end
1997
94
  end
1998
95
 
1999
- def subscribed?
2000
- synchronize do |client|
2001
- client.kind_of? SubscribedClient
2002
- end
96
+ # Run code without the client reconnecting
97
+ def without_reconnect(&blk)
98
+ with_reconnect(false, &blk)
2003
99
  end
2004
100
 
2005
- # Listen for messages published to the given channels.
2006
- def subscribe(*channels, &block)
2007
- synchronize do |client|
2008
- _subscription(:subscribe, channels, block)
2009
- end
101
+ # Test whether or not the client is connected
102
+ def connected?
103
+ @original_client.connected?
2010
104
  end
2011
105
 
2012
- # Stop listening for messages posted to the given channels.
2013
- def unsubscribe(*channels)
2014
- synchronize do |client|
2015
- raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
2016
- client.unsubscribe(*channels)
2017
- end
106
+ # Disconnect the client as quickly and silently as possible.
107
+ def close
108
+ @original_client.disconnect
2018
109
  end
110
+ alias disconnect! close
2019
111
 
2020
- # Listen for messages published to channels matching the given patterns.
2021
- def psubscribe(*channels, &block)
2022
- synchronize do |client|
2023
- _subscription(:psubscribe, channels, block)
2024
- end
2025
- end
112
+ # @deprecated Queues a command for pipelining.
113
+ #
114
+ # Commands in the queue are executed with the Redis#commit method.
115
+ #
116
+ # See http://redis.io/topics/pipelining for more details.
117
+ #
118
+ def queue(*command)
119
+ ::Redis.deprecate!(
120
+ "Redis#queue is deprecated and will be removed in Redis 5.0.0. Use Redis#pipelined instead." \
121
+ "(called from: #{caller(1, 1).first})"
122
+ )
2026
123
 
2027
- # Stop listening for messages posted to channels matching the given patterns.
2028
- def punsubscribe(*channels)
2029
- synchronize do |client|
2030
- raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
2031
- client.punsubscribe(*channels)
124
+ synchronize do
125
+ @queue[Thread.current.object_id] << command
2032
126
  end
2033
127
  end
2034
128
 
2035
- # Watch the given keys to determine execution of the MULTI/EXEC block.
2036
- #
2037
- # Using a block is optional, but is necessary for thread-safety.
2038
- #
2039
- # An `#unwatch` is automatically issued if an exception is raised within the
2040
- # block that is a subclass of StandardError and is not a ConnectionError.
2041
- #
2042
- # @example With a block
2043
- # redis.watch("key") do
2044
- # if redis.get("key") == "some value"
2045
- # redis.multi do |multi|
2046
- # multi.set("key", "other value")
2047
- # multi.incr("counter")
2048
- # end
2049
- # else
2050
- # redis.unwatch
2051
- # end
2052
- # end
2053
- # # => ["OK", 6]
2054
- #
2055
- # @example Without a block
2056
- # redis.watch("key")
2057
- # # => "OK"
129
+ # @deprecated Sends all commands in the queue.
2058
130
  #
2059
- # @param [String, Array<String>] keys one or more keys to watch
2060
- # @return [Object] if using a block, returns the return value of the block
2061
- # @return [String] if not using a block, returns `OK`
131
+ # See http://redis.io/topics/pipelining for more details.
2062
132
  #
2063
- # @see #unwatch
2064
- # @see #multi
2065
- def watch(*keys)
2066
- synchronize do |client|
2067
- res = client.call([:watch] + keys)
133
+ def commit
134
+ ::Redis.deprecate!(
135
+ "Redis#commit is deprecated and will be removed in Redis 5.0.0. Use Redis#pipelined instead. " \
136
+ "(called from: #{Kernel.caller(1, 1).first})"
137
+ )
2068
138
 
2069
- if block_given?
2070
- begin
2071
- yield(self)
2072
- rescue ConnectionError
2073
- raise
2074
- rescue StandardError
2075
- unwatch
2076
- raise
139
+ synchronize do |client|
140
+ begin
141
+ pipeline = Pipeline.new(client)
142
+ @queue[Thread.current.object_id].each do |command|
143
+ pipeline.call(command)
2077
144
  end
2078
- else
2079
- res
145
+
146
+ client.call_pipelined(pipeline)
147
+ ensure
148
+ @queue.delete(Thread.current.object_id)
2080
149
  end
2081
150
  end
2082
151
  end
2083
152
 
2084
- # Forget about all watched keys.
2085
- #
2086
- # @return [String] `OK`
2087
- #
2088
- # @see #watch
2089
- # @see #multi
2090
- def unwatch
2091
- synchronize do |client|
2092
- client.call([:unwatch])
2093
- end
153
+ def _client
154
+ @client
2094
155
  end
2095
156
 
2096
- def pipelined
2097
- synchronize do |client|
157
+ def pipelined(&block)
158
+ deprecation_displayed = false
159
+ if block&.arity == 0
160
+ Pipeline.deprecation_warning("pipelined", Kernel.caller_locations(1, 5))
161
+ deprecation_displayed = true
162
+ end
163
+
164
+ synchronize do |prior_client|
2098
165
  begin
2099
- original, @client = @client, Pipeline.new
2100
- yield(self)
2101
- original.call_pipeline(@client)
166
+ pipeline = Pipeline.new(prior_client)
167
+ @client = deprecation_displayed ? pipeline : DeprecatedPipeline.new(pipeline)
168
+ pipelined_connection = PipelinedConnection.new(pipeline)
169
+ yield pipelined_connection
170
+ prior_client.call_pipeline(pipeline)
2102
171
  ensure
2103
- @client = original
172
+ @client = prior_client
2104
173
  end
2105
174
  end
2106
175
  end
@@ -2135,372 +204,27 @@ class Redis
2135
204
  #
2136
205
  # @see #watch
2137
206
  # @see #unwatch
2138
- def multi
2139
- synchronize do |client|
2140
- if !block_given?
2141
- client.call([:multi])
2142
- else
2143
- begin
2144
- pipeline = Pipeline::Multi.new
2145
- original, @client = @client, pipeline
2146
- yield(self)
2147
- original.call_pipeline(pipeline)
2148
- ensure
2149
- @client = original
2150
- end
207
+ def multi(&block)
208
+ if block_given?
209
+ deprecation_displayed = false
210
+ if block&.arity == 0
211
+ Pipeline.deprecation_warning("multi", Kernel.caller_locations(1, 5))
212
+ deprecation_displayed = true
2151
213
  end
2152
- end
2153
- end
2154
-
2155
- # Execute all commands issued after MULTI.
2156
- #
2157
- # Only call this method when `#multi` was called **without** a block.
2158
- #
2159
- # @return [nil, Array<...>]
2160
- # - when commands were not executed, `nil`
2161
- # - when commands were executed, an array with their replies
2162
- #
2163
- # @see #multi
2164
- # @see #discard
2165
- def exec
2166
- synchronize do |client|
2167
- client.call([:exec])
2168
- end
2169
- end
2170
-
2171
- # Discard all commands issued after MULTI.
2172
- #
2173
- # Only call this method when `#multi` was called **without** a block.
2174
- #
2175
- # @return `"OK"`
2176
- #
2177
- # @see #multi
2178
- # @see #exec
2179
- def discard
2180
- synchronize do |client|
2181
- client.call([:discard])
2182
- end
2183
- end
2184
-
2185
- # Control remote script registry.
2186
- #
2187
- # @example Load a script
2188
- # sha = redis.script(:load, "return 1")
2189
- # # => <sha of this script>
2190
- # @example Check if a script exists
2191
- # redis.script(:exists, sha)
2192
- # # => true
2193
- # @example Check if multiple scripts exist
2194
- # redis.script(:exists, [sha, other_sha])
2195
- # # => [true, false]
2196
- # @example Flush the script registry
2197
- # redis.script(:flush)
2198
- # # => "OK"
2199
- # @example Kill a running script
2200
- # redis.script(:kill)
2201
- # # => "OK"
2202
- #
2203
- # @param [String] subcommand e.g. `exists`, `flush`, `load`, `kill`
2204
- # @param [Array<String>] args depends on subcommand
2205
- # @return [String, Boolean, Array<Boolean>, ...] depends on subcommand
2206
- #
2207
- # @see #eval
2208
- # @see #evalsha
2209
- def script(subcommand, *args)
2210
- subcommand = subcommand.to_s.downcase
2211
214
 
2212
- if subcommand == "exists"
2213
- synchronize do |client|
2214
- arg = args.first
2215
-
2216
- client.call([:script, :exists, arg]) do |reply|
2217
- reply = reply.map { |r| _boolify.call(r) }
2218
-
2219
- if arg.is_a?(Array)
2220
- reply
2221
- else
2222
- reply.first
2223
- end
215
+ synchronize do |prior_client|
216
+ begin
217
+ pipeline = Pipeline::Multi.new(prior_client)
218
+ @client = deprecation_displayed ? pipeline : DeprecatedMulti.new(pipeline)
219
+ pipelined_connection = PipelinedConnection.new(pipeline)
220
+ yield pipelined_connection
221
+ prior_client.call_pipeline(pipeline)
222
+ ensure
223
+ @client = prior_client
2224
224
  end
2225
225
  end
2226
226
  else
2227
- synchronize do |client|
2228
- client.call([:script, subcommand] + args)
2229
- end
2230
- end
2231
- end
2232
-
2233
- def _eval(cmd, args)
2234
- script = args.shift
2235
- options = args.pop if args.last.is_a?(Hash)
2236
- options ||= {}
2237
-
2238
- keys = args.shift || options[:keys] || []
2239
- argv = args.shift || options[:argv] || []
2240
-
2241
- synchronize do |client|
2242
- client.call([cmd, script, keys.length] + keys + argv)
2243
- end
2244
- end
2245
-
2246
- # Evaluate Lua script.
2247
- #
2248
- # @example EVAL without KEYS nor ARGV
2249
- # redis.eval("return 1")
2250
- # # => 1
2251
- # @example EVAL with KEYS and ARGV as array arguments
2252
- # redis.eval("return { KEYS, ARGV }", ["k1", "k2"], ["a1", "a2"])
2253
- # # => [["k1", "k2"], ["a1", "a2"]]
2254
- # @example EVAL with KEYS and ARGV in a hash argument
2255
- # redis.eval("return { KEYS, ARGV }", :keys => ["k1", "k2"], :argv => ["a1", "a2"])
2256
- # # => [["k1", "k2"], ["a1", "a2"]]
2257
- #
2258
- # @param [Array<String>] keys optional array with keys to pass to the script
2259
- # @param [Array<String>] argv optional array with arguments to pass to the script
2260
- # @param [Hash] options
2261
- # - `:keys => Array<String>`: optional array with keys to pass to the script
2262
- # - `:argv => Array<String>`: optional array with arguments to pass to the script
2263
- # @return depends on the script
2264
- #
2265
- # @see #script
2266
- # @see #evalsha
2267
- def eval(*args)
2268
- _eval(:eval, args)
2269
- end
2270
-
2271
- # Evaluate Lua script by its SHA.
2272
- #
2273
- # @example EVALSHA without KEYS nor ARGV
2274
- # redis.evalsha(sha)
2275
- # # => <depends on script>
2276
- # @example EVALSHA with KEYS and ARGV as array arguments
2277
- # redis.evalsha(sha, ["k1", "k2"], ["a1", "a2"])
2278
- # # => <depends on script>
2279
- # @example EVALSHA with KEYS and ARGV in a hash argument
2280
- # redis.evalsha(sha, :keys => ["k1", "k2"], :argv => ["a1", "a2"])
2281
- # # => <depends on script>
2282
- #
2283
- # @param [Array<String>] keys optional array with keys to pass to the script
2284
- # @param [Array<String>] argv optional array with arguments to pass to the script
2285
- # @param [Hash] options
2286
- # - `:keys => Array<String>`: optional array with keys to pass to the script
2287
- # - `:argv => Array<String>`: optional array with arguments to pass to the script
2288
- # @return depends on the script
2289
- #
2290
- # @see #script
2291
- # @see #eval
2292
- def evalsha(*args)
2293
- _eval(:evalsha, args)
2294
- end
2295
-
2296
- def _scan(command, cursor, args, options = {}, &block)
2297
- # SSCAN/ZSCAN/HSCAN already prepend the key to +args+.
2298
-
2299
- args << cursor
2300
-
2301
- if match = options[:match]
2302
- args.concat(["MATCH", match])
2303
- end
2304
-
2305
- if count = options[:count]
2306
- args.concat(["COUNT", count])
2307
- end
2308
-
2309
- synchronize do |client|
2310
- client.call([command] + args, &block)
2311
- end
2312
- end
2313
-
2314
- # Scan the keyspace
2315
- #
2316
- # @example Retrieve the first batch of keys
2317
- # redis.scan(0)
2318
- # # => ["4", ["key:21", "key:47", "key:42"]]
2319
- # @example Retrieve a batch of keys matching a pattern
2320
- # redis.scan(4, :match => "key:1?")
2321
- # # => ["92", ["key:13", "key:18"]]
2322
- #
2323
- # @param [String, Integer] cursor: the cursor of the iteration
2324
- # @param [Hash] options
2325
- # - `:match => String`: only return keys matching the pattern
2326
- # - `:count => Integer`: return count keys at most per iteration
2327
- #
2328
- # @return [String, Array<String>] the next cursor and all found keys
2329
- def scan(cursor, options={})
2330
- _scan(:scan, cursor, [], options)
2331
- end
2332
-
2333
- # Scan the keyspace
2334
- #
2335
- # @example Retrieve all of the keys (with possible duplicates)
2336
- # redis.scan_each.to_a
2337
- # # => ["key:21", "key:47", "key:42"]
2338
- # @example Execute block for each key matching a pattern
2339
- # redis.scan_each(:match => "key:1?") {|key| puts key}
2340
- # # => key:13
2341
- # # => key:18
2342
- #
2343
- # @param [Hash] options
2344
- # - `:match => String`: only return keys matching the pattern
2345
- # - `:count => Integer`: return count keys at most per iteration
2346
- #
2347
- # @return [Enumerator] an enumerator for all found keys
2348
- def scan_each(options={}, &block)
2349
- return to_enum(:scan_each, options) unless block_given?
2350
- cursor = 0
2351
- loop do
2352
- cursor, keys = scan(cursor, options)
2353
- keys.each(&block)
2354
- break if cursor == "0"
2355
- end
2356
- end
2357
-
2358
- # Scan a hash
2359
- #
2360
- # @example Retrieve the first batch of key/value pairs in a hash
2361
- # redis.hscan("hash", 0)
2362
- #
2363
- # @param [String, Integer] cursor: the cursor of the iteration
2364
- # @param [Hash] options
2365
- # - `:match => String`: only return keys matching the pattern
2366
- # - `:count => Integer`: return count keys at most per iteration
2367
- #
2368
- # @return [String, Array<[String, String]>] the next cursor and all found keys
2369
- def hscan(key, cursor, options={})
2370
- _scan(:hscan, cursor, [key], options) do |reply|
2371
- [reply[0], _pairify(reply[1])]
2372
- end
2373
- end
2374
-
2375
- # Scan a hash
2376
- #
2377
- # @example Retrieve all of the key/value pairs in a hash
2378
- # redis.hscan_each("hash").to_a
2379
- # # => [["key70", "70"], ["key80", "80"]]
2380
- #
2381
- # @param [Hash] options
2382
- # - `:match => String`: only return keys matching the pattern
2383
- # - `:count => Integer`: return count keys at most per iteration
2384
- #
2385
- # @return [Enumerator] an enumerator for all found keys
2386
- def hscan_each(key, options={}, &block)
2387
- return to_enum(:hscan_each, key, options) unless block_given?
2388
- cursor = 0
2389
- loop do
2390
- cursor, values = hscan(key, cursor, options)
2391
- values.each(&block)
2392
- break if cursor == "0"
2393
- end
2394
- end
2395
-
2396
- # Scan a sorted set
2397
- #
2398
- # @example Retrieve the first batch of key/value pairs in a hash
2399
- # redis.zscan("zset", 0)
2400
- #
2401
- # @param [String, Integer] cursor: the cursor of the iteration
2402
- # @param [Hash] options
2403
- # - `:match => String`: only return keys matching the pattern
2404
- # - `:count => Integer`: return count keys at most per iteration
2405
- #
2406
- # @return [String, Array<[String, Float]>] the next cursor and all found
2407
- # members and scores
2408
- def zscan(key, cursor, options={})
2409
- _scan(:zscan, cursor, [key], options) do |reply|
2410
- [reply[0], _floatify_pairs.call(reply[1])]
2411
- end
2412
- end
2413
-
2414
- # Scan a sorted set
2415
- #
2416
- # @example Retrieve all of the members/scores in a sorted set
2417
- # redis.zscan_each("zset").to_a
2418
- # # => [["key70", "70"], ["key80", "80"]]
2419
- #
2420
- # @param [Hash] options
2421
- # - `:match => String`: only return keys matching the pattern
2422
- # - `:count => Integer`: return count keys at most per iteration
2423
- #
2424
- # @return [Enumerator] an enumerator for all found scores and members
2425
- def zscan_each(key, options={}, &block)
2426
- return to_enum(:zscan_each, key, options) unless block_given?
2427
- cursor = 0
2428
- loop do
2429
- cursor, values = zscan(key, cursor, options)
2430
- values.each(&block)
2431
- break if cursor == "0"
2432
- end
2433
- end
2434
-
2435
- # Scan a set
2436
- #
2437
- # @example Retrieve the first batch of keys in a set
2438
- # redis.sscan("set", 0)
2439
- #
2440
- # @param [String, Integer] cursor: the cursor of the iteration
2441
- # @param [Hash] options
2442
- # - `:match => String`: only return keys matching the pattern
2443
- # - `:count => Integer`: return count keys at most per iteration
2444
- #
2445
- # @return [String, Array<String>] the next cursor and all found members
2446
- def sscan(key, cursor, options={})
2447
- _scan(:sscan, cursor, [key], options)
2448
- end
2449
-
2450
- # Scan a set
2451
- #
2452
- # @example Retrieve all of the keys in a set
2453
- # redis.sscan("set").to_a
2454
- # # => ["key1", "key2", "key3"]
2455
- #
2456
- # @param [Hash] options
2457
- # - `:match => String`: only return keys matching the pattern
2458
- # - `:count => Integer`: return count keys at most per iteration
2459
- #
2460
- # @return [Enumerator] an enumerator for all keys in the set
2461
- def sscan_each(key, options={}, &block)
2462
- return to_enum(:sscan_each, key, options) unless block_given?
2463
- cursor = 0
2464
- loop do
2465
- cursor, keys = sscan(key, cursor, options)
2466
- keys.each(&block)
2467
- break if cursor == "0"
2468
- end
2469
- end
2470
-
2471
- # Add one or more members to a HyperLogLog structure.
2472
- #
2473
- # @param [String] key
2474
- # @param [String, Array<String>] member one member, or array of members
2475
- # @return [Boolean] true if at least 1 HyperLogLog internal register was altered. false otherwise.
2476
- def pfadd(key, member)
2477
- synchronize do |client|
2478
- client.call([:pfadd, key, member], &_boolify)
2479
- end
2480
- end
2481
-
2482
- # Get the approximate cardinality of members added to HyperLogLog structure.
2483
- #
2484
- # If called with multiple keys, returns the approximate cardinality of the
2485
- # union of the HyperLogLogs contained in the keys.
2486
- #
2487
- # @param [String, Array<String>] keys
2488
- # @return [Fixnum]
2489
- def pfcount(*keys)
2490
- synchronize do |client|
2491
- client.call([:pfcount] + keys)
2492
- end
2493
- end
2494
-
2495
- # Merge multiple HyperLogLog values into an unique value that will approximate the cardinality of the union of
2496
- # the observed Sets of the source HyperLogLog structures.
2497
- #
2498
- # @param [String] dest_key destination key
2499
- # @param [String, Array<String>] source_key source key, or array of keys
2500
- # @return [Boolean]
2501
- def pfmerge(dest_key, *source_key)
2502
- synchronize do |client|
2503
- client.call([:pfmerge, dest_key, *source_key], &_boolify_set)
227
+ send_command([:multi])
2504
228
  end
2505
229
  end
2506
230
 
@@ -2516,84 +240,55 @@ class Redis
2516
240
  self.class.new(@options)
2517
241
  end
2518
242
 
2519
- def method_missing(command, *args)
2520
- synchronize do |client|
2521
- client.call([command] + args)
2522
- end
2523
- end
2524
-
2525
- private
2526
-
2527
- # Commands returning 1 for true and 0 for false may be executed in a pipeline
2528
- # where the method call will return nil. Propagate the nil instead of falsely
2529
- # returning false.
2530
- def _boolify
2531
- lambda { |value|
2532
- value == 1 if value
2533
- }
2534
- end
2535
-
2536
- def _boolify_set
2537
- lambda { |value|
2538
- if value && "OK" == value
2539
- true
2540
- else
2541
- false
2542
- end
2543
- }
2544
- end
243
+ def connection
244
+ return @original_client.connection_info if @cluster_mode
2545
245
 
2546
- def _hashify
2547
- lambda { |array|
2548
- hash = Hash.new
2549
- array.each_slice(2) do |field, value|
2550
- hash[field] = value
2551
- end
2552
- hash
246
+ {
247
+ host: @original_client.host,
248
+ port: @original_client.port,
249
+ db: @original_client.db,
250
+ id: @original_client.id,
251
+ location: @original_client.location
2553
252
  }
2554
253
  end
2555
254
 
2556
- def _floatify
2557
- lambda { |str|
2558
- return unless str
255
+ private
2559
256
 
2560
- if (inf = str.match(/^(-)?inf/i))
2561
- (inf[1] ? -1.0 : 1.0) / 0.0
2562
- else
2563
- Float(str)
2564
- end
2565
- }
257
+ def synchronize
258
+ @monitor.synchronize { yield(@client) }
2566
259
  end
2567
260
 
2568
- def _floatify_pairs
2569
- lambda { |array|
2570
- return unless array
2571
-
2572
- array.each_slice(2).map do |member, score|
2573
- [member, _floatify.call(score)]
2574
- end
2575
- }
261
+ def send_command(command, &block)
262
+ @monitor.synchronize do
263
+ @client.call(command, &block)
264
+ end
2576
265
  end
2577
266
 
2578
- def _pairify(array)
2579
- array.each_slice(2).to_a
267
+ def send_blocking_command(command, timeout, &block)
268
+ @monitor.synchronize do
269
+ @client.call_with_timeout(command, timeout, &block)
270
+ end
2580
271
  end
2581
272
 
2582
- def _subscription(method, channels, block)
273
+ def _subscription(method, timeout, channels, block)
2583
274
  return @client.call([method] + channels) if subscribed?
2584
275
 
2585
276
  begin
2586
277
  original, @client = @client, SubscribedClient.new(@client)
2587
- @client.send(method, *channels, &block)
278
+ if timeout > 0
279
+ @client.send(method, timeout, *channels, &block)
280
+ else
281
+ @client.send(method, *channels, &block)
282
+ end
2588
283
  ensure
2589
284
  @client = original
2590
285
  end
2591
286
  end
2592
-
2593
287
  end
2594
288
 
2595
289
  require "redis/version"
2596
290
  require "redis/connection"
2597
291
  require "redis/client"
292
+ require "redis/cluster"
2598
293
  require "redis/pipeline"
2599
294
  require "redis/subscribe"