redis 3.3.5 → 5.0.7

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 (137) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +290 -2
  3. data/README.md +146 -146
  4. data/lib/redis/client.rb +79 -541
  5. data/lib/redis/commands/bitmaps.rb +66 -0
  6. data/lib/redis/commands/cluster.rb +28 -0
  7. data/lib/redis/commands/connection.rb +53 -0
  8. data/lib/redis/commands/geo.rb +84 -0
  9. data/lib/redis/commands/hashes.rb +254 -0
  10. data/lib/redis/commands/hyper_log_log.rb +37 -0
  11. data/lib/redis/commands/keys.rb +437 -0
  12. data/lib/redis/commands/lists.rb +339 -0
  13. data/lib/redis/commands/pubsub.rb +54 -0
  14. data/lib/redis/commands/scripting.rb +114 -0
  15. data/lib/redis/commands/server.rb +188 -0
  16. data/lib/redis/commands/sets.rb +214 -0
  17. data/lib/redis/commands/sorted_sets.rb +884 -0
  18. data/lib/redis/commands/streams.rb +402 -0
  19. data/lib/redis/commands/strings.rb +314 -0
  20. data/lib/redis/commands/transactions.rb +115 -0
  21. data/lib/redis/commands.rb +237 -0
  22. data/lib/redis/distributed.rb +328 -108
  23. data/lib/redis/errors.rb +23 -1
  24. data/lib/redis/hash_ring.rb +36 -79
  25. data/lib/redis/pipeline.rb +69 -83
  26. data/lib/redis/subscribe.rb +26 -19
  27. data/lib/redis/version.rb +3 -1
  28. data/lib/redis.rb +115 -2695
  29. metadata +38 -218
  30. data/.gitignore +0 -16
  31. data/.travis/Gemfile +0 -11
  32. data/.travis.yml +0 -89
  33. data/.yardopts +0 -3
  34. data/Gemfile +0 -4
  35. data/Rakefile +0 -87
  36. data/benchmarking/logging.rb +0 -71
  37. data/benchmarking/pipeline.rb +0 -51
  38. data/benchmarking/speed.rb +0 -21
  39. data/benchmarking/suite.rb +0 -24
  40. data/benchmarking/worker.rb +0 -71
  41. data/examples/basic.rb +0 -15
  42. data/examples/consistency.rb +0 -114
  43. data/examples/dist_redis.rb +0 -43
  44. data/examples/incr-decr.rb +0 -17
  45. data/examples/list.rb +0 -26
  46. data/examples/pubsub.rb +0 -37
  47. data/examples/sentinel/sentinel.conf +0 -9
  48. data/examples/sentinel/start +0 -49
  49. data/examples/sentinel.rb +0 -41
  50. data/examples/sets.rb +0 -36
  51. data/examples/unicorn/config.ru +0 -3
  52. data/examples/unicorn/unicorn.rb +0 -20
  53. data/lib/redis/connection/command_helper.rb +0 -44
  54. data/lib/redis/connection/hiredis.rb +0 -66
  55. data/lib/redis/connection/registry.rb +0 -12
  56. data/lib/redis/connection/ruby.rb +0 -429
  57. data/lib/redis/connection/synchrony.rb +0 -133
  58. data/lib/redis/connection.rb +0 -9
  59. data/redis.gemspec +0 -44
  60. data/test/bitpos_test.rb +0 -69
  61. data/test/blocking_commands_test.rb +0 -42
  62. data/test/client_test.rb +0 -59
  63. data/test/command_map_test.rb +0 -30
  64. data/test/commands_on_hashes_test.rb +0 -21
  65. data/test/commands_on_hyper_log_log_test.rb +0 -21
  66. data/test/commands_on_lists_test.rb +0 -20
  67. data/test/commands_on_sets_test.rb +0 -77
  68. data/test/commands_on_sorted_sets_test.rb +0 -137
  69. data/test/commands_on_strings_test.rb +0 -101
  70. data/test/commands_on_value_types_test.rb +0 -133
  71. data/test/connection_handling_test.rb +0 -277
  72. data/test/connection_test.rb +0 -57
  73. data/test/db/.gitkeep +0 -0
  74. data/test/distributed_blocking_commands_test.rb +0 -46
  75. data/test/distributed_commands_on_hashes_test.rb +0 -10
  76. data/test/distributed_commands_on_hyper_log_log_test.rb +0 -33
  77. data/test/distributed_commands_on_lists_test.rb +0 -22
  78. data/test/distributed_commands_on_sets_test.rb +0 -83
  79. data/test/distributed_commands_on_sorted_sets_test.rb +0 -18
  80. data/test/distributed_commands_on_strings_test.rb +0 -59
  81. data/test/distributed_commands_on_value_types_test.rb +0 -95
  82. data/test/distributed_commands_requiring_clustering_test.rb +0 -164
  83. data/test/distributed_connection_handling_test.rb +0 -23
  84. data/test/distributed_internals_test.rb +0 -79
  85. data/test/distributed_key_tags_test.rb +0 -52
  86. data/test/distributed_persistence_control_commands_test.rb +0 -26
  87. data/test/distributed_publish_subscribe_test.rb +0 -92
  88. data/test/distributed_remote_server_control_commands_test.rb +0 -66
  89. data/test/distributed_scripting_test.rb +0 -102
  90. data/test/distributed_sorting_test.rb +0 -20
  91. data/test/distributed_test.rb +0 -58
  92. data/test/distributed_transactions_test.rb +0 -32
  93. data/test/encoding_test.rb +0 -18
  94. data/test/error_replies_test.rb +0 -59
  95. data/test/fork_safety_test.rb +0 -65
  96. data/test/helper.rb +0 -232
  97. data/test/helper_test.rb +0 -24
  98. data/test/internals_test.rb +0 -417
  99. data/test/lint/blocking_commands.rb +0 -150
  100. data/test/lint/hashes.rb +0 -162
  101. data/test/lint/hyper_log_log.rb +0 -60
  102. data/test/lint/lists.rb +0 -143
  103. data/test/lint/sets.rb +0 -140
  104. data/test/lint/sorted_sets.rb +0 -316
  105. data/test/lint/strings.rb +0 -260
  106. data/test/lint/value_types.rb +0 -122
  107. data/test/persistence_control_commands_test.rb +0 -26
  108. data/test/pipelining_commands_test.rb +0 -242
  109. data/test/publish_subscribe_test.rb +0 -282
  110. data/test/remote_server_control_commands_test.rb +0 -118
  111. data/test/scanning_test.rb +0 -413
  112. data/test/scripting_test.rb +0 -78
  113. data/test/sentinel_command_test.rb +0 -80
  114. data/test/sentinel_test.rb +0 -255
  115. data/test/sorting_test.rb +0 -59
  116. data/test/ssl_test.rb +0 -73
  117. data/test/support/connection/hiredis.rb +0 -1
  118. data/test/support/connection/ruby.rb +0 -1
  119. data/test/support/connection/synchrony.rb +0 -17
  120. data/test/support/redis_mock.rb +0 -130
  121. data/test/support/ssl/gen_certs.sh +0 -31
  122. data/test/support/ssl/trusted-ca.crt +0 -25
  123. data/test/support/ssl/trusted-ca.key +0 -27
  124. data/test/support/ssl/trusted-cert.crt +0 -81
  125. data/test/support/ssl/trusted-cert.key +0 -28
  126. data/test/support/ssl/untrusted-ca.crt +0 -26
  127. data/test/support/ssl/untrusted-ca.key +0 -27
  128. data/test/support/ssl/untrusted-cert.crt +0 -82
  129. data/test/support/ssl/untrusted-cert.key +0 -28
  130. data/test/support/wire/synchrony.rb +0 -24
  131. data/test/support/wire/thread.rb +0 -5
  132. data/test/synchrony_driver.rb +0 -88
  133. data/test/test.conf.erb +0 -9
  134. data/test/thread_safety_test.rb +0 -62
  135. data/test/transactions_test.rb +0 -264
  136. data/test/unknown_commands_test.rb +0 -14
  137. data/test/url_param_test.rb +0 -138
@@ -1,15 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "redis/hash_ring"
2
4
 
3
5
  class Redis
4
6
  class Distributed
5
-
6
7
  class CannotDistribute < RuntimeError
7
8
  def initialize(command)
8
9
  @command = command
9
10
  end
10
11
 
11
12
  def message
12
- "#{@command.to_s.upcase} cannot be used in Redis::Distributed because the keys involved need to be on the same server or because we cannot guarantee that the operation will be atomic."
13
+ "#{@command.to_s.upcase} cannot be used in Redis::Distributed because the keys involved need " \
14
+ "to be on the same server or because we cannot guarantee that the operation will be atomic."
13
15
  end
14
16
  end
15
17
 
@@ -18,14 +20,18 @@ class Redis
18
20
  def initialize(node_configs, options = {})
19
21
  @tag = options[:tag] || /^\{(.+?)\}/
20
22
  @ring = options[:ring] || HashRing.new
21
- @node_configs = node_configs.dup
23
+ @node_configs = node_configs.map(&:dup)
22
24
  @default_options = options.dup
23
25
  node_configs.each { |node_config| add_node(node_config) }
24
26
  @subscribed_node = nil
27
+ @watch_key = nil
25
28
  end
26
29
 
27
30
  def node_for(key)
28
- @ring.get_node(key_tag(key.to_s) || key.to_s)
31
+ key = key_tag(key.to_s) || key.to_s
32
+ raise CannotDistribute, :watch if @watch_key && @watch_key != key
33
+
34
+ @ring.get_node(key)
29
35
  end
30
36
 
31
37
  def nodes
@@ -33,9 +39,11 @@ class Redis
33
39
  end
34
40
 
35
41
  def add_node(options)
36
- options = { :url => options } if options.is_a?(String)
42
+ options = { url: options } if options.is_a?(String)
37
43
  options = @default_options.merge(options)
38
- @ring.add_node Redis.new( options )
44
+ options.delete(:tag)
45
+ options.delete(:ring)
46
+ @ring.add_node Redis.new(options)
39
47
  end
40
48
 
41
49
  # Change the selected database for the current connection.
@@ -58,6 +66,10 @@ class Redis
58
66
  on_each_node :quit
59
67
  end
60
68
 
69
+ def close
70
+ on_each_node :close
71
+ end
72
+
61
73
  # Asynchronously save the dataset to disk.
62
74
  def bgsave
63
75
  on_each_node :bgsave
@@ -109,13 +121,13 @@ class Redis
109
121
  end
110
122
 
111
123
  # Set a key's time to live in seconds.
112
- def expire(key, seconds)
113
- node_for(key).expire(key, seconds)
124
+ def expire(key, seconds, **kwargs)
125
+ node_for(key).expire(key, seconds, **kwargs)
114
126
  end
115
127
 
116
128
  # Set the expiration for a key as a UNIX timestamp.
117
- def expireat(key, unix_time)
118
- node_for(key).expireat(key, unix_time)
129
+ def expireat(key, unix_time, **kwargs)
130
+ node_for(key).expireat(key, unix_time, **kwargs)
119
131
  end
120
132
 
121
133
  # Get the time to live (in seconds) for a key.
@@ -124,13 +136,13 @@ class Redis
124
136
  end
125
137
 
126
138
  # Set a key's time to live in milliseconds.
127
- def pexpire(key, milliseconds)
128
- node_for(key).pexpire(key, milliseconds)
139
+ def pexpire(key, milliseconds, **kwarg)
140
+ node_for(key).pexpire(key, milliseconds, **kwarg)
129
141
  end
130
142
 
131
143
  # Set the expiration for a key as number of milliseconds from UNIX Epoch.
132
- def pexpireat(key, ms_unix_time)
133
- node_for(key).pexpireat(key, ms_unix_time)
144
+ def pexpireat(key, ms_unix_time, **kwarg)
145
+ node_for(key).pexpireat(key, ms_unix_time, **kwarg)
134
146
  end
135
147
 
136
148
  # Get the time to live (in milliseconds) for a key.
@@ -144,26 +156,50 @@ class Redis
144
156
  end
145
157
 
146
158
  # Create a key using the serialized value, previously obtained using DUMP.
147
- def restore(key, ttl, serialized_value)
148
- node_for(key).restore(key, ttl, serialized_value)
159
+ def restore(key, ttl, serialized_value, **options)
160
+ node_for(key).restore(key, ttl, serialized_value, **options)
149
161
  end
150
162
 
151
163
  # Transfer a key from the connected instance to another instance.
152
- def migrate(key, options)
164
+ def migrate(_key, _options)
153
165
  raise CannotDistribute, :migrate
154
166
  end
155
167
 
156
168
  # Delete a key.
157
169
  def del(*args)
170
+ args.flatten!(1)
158
171
  keys_per_node = args.group_by { |key| node_for(key) }
159
172
  keys_per_node.inject(0) do |sum, (node, keys)|
160
173
  sum + node.del(*keys)
161
174
  end
162
175
  end
163
176
 
177
+ # Unlink keys.
178
+ def unlink(*args)
179
+ args.flatten!(1)
180
+ keys_per_node = args.group_by { |key| node_for(key) }
181
+ keys_per_node.inject(0) do |sum, (node, keys)|
182
+ sum + node.unlink(*keys)
183
+ end
184
+ end
185
+
164
186
  # Determine if a key exists.
165
- def exists(key)
166
- node_for(key).exists(key)
187
+ def exists(*args)
188
+ args.flatten!(1)
189
+ keys_per_node = args.group_by { |key| node_for(key) }
190
+ keys_per_node.inject(0) do |sum, (node, keys)|
191
+ sum + node.exists(*keys)
192
+ end
193
+ end
194
+
195
+ # Determine if any of the keys exists.
196
+ def exists?(*args)
197
+ args.flatten!(1)
198
+ keys_per_node = args.group_by { |key| node_for(key) }
199
+ keys_per_node.each do |node, keys|
200
+ return true if node.exists?(*keys)
201
+ end
202
+ false
167
203
  end
168
204
 
169
205
  # Find all keys matching the given pattern.
@@ -176,6 +212,13 @@ class Redis
176
212
  node_for(key).move(key, db)
177
213
  end
178
214
 
215
+ # Copy a value from one key to another.
216
+ def copy(source, destination, **options)
217
+ ensure_same_node(:copy, [source, destination]) do |node|
218
+ node.copy(source, destination, **options)
219
+ end
220
+ end
221
+
179
222
  # Return a random key from the keyspace.
180
223
  def randomkey
181
224
  raise CannotDistribute, :randomkey
@@ -196,11 +239,11 @@ class Redis
196
239
  end
197
240
 
198
241
  # Sort the elements in a list, set or sorted set.
199
- def sort(key, options = {})
242
+ def sort(key, **options)
200
243
  keys = [key, options[:by], options[:store], *Array(options[:get])].compact
201
244
 
202
245
  ensure_same_node(:sort, keys) do |node|
203
- node.sort(key, options)
246
+ node.sort(key, **options)
204
247
  end
205
248
  end
206
249
 
@@ -235,8 +278,8 @@ class Redis
235
278
  end
236
279
 
237
280
  # Set the string value of a key.
238
- def set(key, value, options = {})
239
- node_for(key).set(key, value, options)
281
+ def set(key, value, **options)
282
+ node_for(key).set(key, value, **options)
240
283
  end
241
284
 
242
285
  # Set the time to live in seconds of a key.
@@ -255,20 +298,20 @@ class Redis
255
298
  end
256
299
 
257
300
  # Set multiple keys to multiple values.
258
- def mset(*args)
301
+ def mset(*)
259
302
  raise CannotDistribute, :mset
260
303
  end
261
304
 
262
- def mapped_mset(hash)
305
+ def mapped_mset(_hash)
263
306
  raise CannotDistribute, :mapped_mset
264
307
  end
265
308
 
266
309
  # Set multiple keys to multiple values, only if none of the keys exist.
267
- def msetnx(*args)
310
+ def msetnx(*)
268
311
  raise CannotDistribute, :msetnx
269
312
  end
270
313
 
271
- def mapped_msetnx(hash)
314
+ def mapped_msetnx(_hash)
272
315
  raise CannotDistribute, :mapped_msetnx
273
316
  end
274
317
 
@@ -277,13 +320,28 @@ class Redis
277
320
  node_for(key).get(key)
278
321
  end
279
322
 
280
- # Get the values of all the given keys.
323
+ # Get the value of a key and delete it.
324
+ def getdel(key)
325
+ node_for(key).getdel(key)
326
+ end
327
+
328
+ # Get the value of a key and sets its time to live based on options.
329
+ def getex(key, **options)
330
+ node_for(key).getex(key, **options)
331
+ end
332
+
333
+ # Get the values of all the given keys as an Array.
281
334
  def mget(*keys)
282
- raise CannotDistribute, :mget
335
+ keys.flatten!(1)
336
+ mapped_mget(*keys).values_at(*keys)
283
337
  end
284
338
 
339
+ # Get the values of all the given keys as a Hash.
285
340
  def mapped_mget(*keys)
286
- raise CannotDistribute, :mapped_mget
341
+ keys.flatten!(1)
342
+ keys.group_by { |k| node_for k }.inject({}) do |results, (node, subkeys)|
343
+ results.merge! node.mapped_mget(*subkeys)
344
+ end
287
345
  end
288
346
 
289
347
  # Overwrite part of a string at key starting at the specified offset.
@@ -318,13 +376,14 @@ class Redis
318
376
 
319
377
  # Perform a bitwise operation between strings and store the resulting string in a key.
320
378
  def bitop(operation, destkey, *keys)
379
+ keys.flatten!(1)
321
380
  ensure_same_node(:bitop, [destkey] + keys) do |node|
322
- node.bitop(operation, destkey, *keys)
381
+ node.bitop(operation, destkey, keys)
323
382
  end
324
383
  end
325
384
 
326
385
  # Return the position of the first bit set to 1 or 0 in a string.
327
- def bitpos(key, bit, start=nil, stop=nil)
386
+ def bitpos(key, bit, start = nil, stop = nil)
328
387
  node_for(key).bitpos(key, bit, start, stop)
329
388
  end
330
389
 
@@ -342,7 +401,7 @@ class Redis
342
401
  get(key)
343
402
  end
344
403
 
345
- def []=(key,value)
404
+ def []=(key, value)
346
405
  set(key, value)
347
406
  end
348
407
 
@@ -351,6 +410,21 @@ class Redis
351
410
  node_for(key).llen(key)
352
411
  end
353
412
 
413
+ # Remove the first/last element in a list, append/prepend it to another list and return it.
414
+ def lmove(source, destination, where_source, where_destination)
415
+ ensure_same_node(:lmove, [source, destination]) do |node|
416
+ node.lmove(source, destination, where_source, where_destination)
417
+ end
418
+ end
419
+
420
+ # Remove the first/last element in a list and append/prepend it
421
+ # to another list and return it, or block until one is available.
422
+ def blmove(source, destination, where_source, where_destination, timeout: 0)
423
+ ensure_same_node(:lmove, [source, destination]) do |node|
424
+ node.blmove(source, destination, where_source, where_destination, timeout: timeout)
425
+ end
426
+ end
427
+
354
428
  # Prepend one or more values to a list.
355
429
  def lpush(key, value)
356
430
  node_for(key).lpush(key, value)
@@ -371,14 +445,14 @@ class Redis
371
445
  node_for(key).rpushx(key, value)
372
446
  end
373
447
 
374
- # Remove and get the first element in a list.
375
- def lpop(key)
376
- node_for(key).lpop(key)
448
+ # Remove and get the first elements in a list.
449
+ def lpop(key, count = nil)
450
+ node_for(key).lpop(key, count)
377
451
  end
378
452
 
379
- # Remove and get the last element in a list.
380
- def rpop(key)
381
- node_for(key).rpop(key)
453
+ # Remove and get the last elements in a list.
454
+ def rpop(key, count = nil)
455
+ node_for(key).rpop(key, count)
382
456
  end
383
457
 
384
458
  # Remove the last element in a list, append it to another list and return
@@ -390,24 +464,19 @@ class Redis
390
464
  end
391
465
 
392
466
  def _bpop(cmd, args)
393
- options = {}
394
-
395
- case args.last
396
- when Hash
467
+ timeout = if args.last.is_a?(Hash)
397
468
  options = args.pop
398
- when Integer
399
- # Issue deprecation notice in obnoxious mode...
400
- options[:timeout] = args.pop
401
- end
402
-
403
- if args.size > 1
404
- # Issue deprecation notice in obnoxious mode...
469
+ options[:timeout]
405
470
  end
406
471
 
407
- keys = args.flatten
472
+ args.flatten!(1)
408
473
 
409
- ensure_same_node(cmd, keys) do |node|
410
- node.__send__(cmd, keys, options)
474
+ ensure_same_node(cmd, args) do |node|
475
+ if timeout
476
+ node.__send__(cmd, args, timeout: timeout)
477
+ else
478
+ node.__send__(cmd, args)
479
+ end
411
480
  end
412
481
  end
413
482
 
@@ -417,6 +486,18 @@ class Redis
417
486
  _bpop(:blpop, args)
418
487
  end
419
488
 
489
+ def bzpopmax(*args)
490
+ _bpop(:bzpopmax, args) do |reply|
491
+ reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
492
+ end
493
+ end
494
+
495
+ def bzpopmin(*args)
496
+ _bpop(:bzpopmin, args) do |reply|
497
+ reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
498
+ end
499
+ end
500
+
420
501
  # Remove and get the last element in a list, or block until one is
421
502
  # available.
422
503
  def brpop(*args)
@@ -425,15 +506,9 @@ class Redis
425
506
 
426
507
  # Pop a value from a list, push it to another list and return it; or block
427
508
  # until one is available.
428
- def brpoplpush(source, destination, options = {})
429
- case options
430
- when Integer
431
- # Issue deprecation notice in obnoxious mode...
432
- options = { :timeout => options }
433
- end
434
-
509
+ def brpoplpush(source, destination, **options)
435
510
  ensure_same_node(:brpoplpush, [source, destination]) do |node|
436
- node.brpoplpush(source, destination, options)
511
+ node.brpoplpush(source, destination, **options)
437
512
  end
438
513
  end
439
514
 
@@ -467,19 +542,43 @@ class Redis
467
542
  node_for(key).ltrim(key, start, stop)
468
543
  end
469
544
 
545
+ # Iterate over keys, blocking and removing elements from the first non empty liist found.
546
+ def blmpop(timeout, *keys, modifier: "LEFT", count: nil)
547
+ ensure_same_node(:blmpop, keys) do |node|
548
+ node.blmpop(timeout, *keys, modifier: modifier, count: count)
549
+ end
550
+ end
551
+
552
+ # Iterate over keys, removing elements from the first non list found.
553
+ def lmpop(*keys, modifier: "LEFT", count: nil)
554
+ ensure_same_node(:lmpop, keys) do |node|
555
+ node.lmpop(*keys, modifier: modifier, count: count)
556
+ end
557
+ end
558
+
470
559
  # Get the number of members in a set.
471
560
  def scard(key)
472
561
  node_for(key).scard(key)
473
562
  end
474
563
 
475
564
  # Add one or more members to a set.
476
- def sadd(key, member)
477
- node_for(key).sadd(key, member)
565
+ def sadd(key, *members)
566
+ node_for(key).sadd(key, *members)
567
+ end
568
+
569
+ # Add one or more members to a set.
570
+ def sadd?(key, *members)
571
+ node_for(key).sadd?(key, *members)
572
+ end
573
+
574
+ # Remove one or more members from a set.
575
+ def srem(key, *members)
576
+ node_for(key).srem(key, *members)
478
577
  end
479
578
 
480
579
  # Remove one or more members from a set.
481
- def srem(key, member)
482
- node_for(key).srem(key, member)
580
+ def srem?(key, *members)
581
+ node_for(key).srem?(key, *members)
483
582
  end
484
583
 
485
584
  # Remove and return a random member from a set.
@@ -504,50 +603,71 @@ class Redis
504
603
  node_for(key).sismember(key, member)
505
604
  end
506
605
 
606
+ # Determine if multiple values are members of a set.
607
+ def smismember(key, *members)
608
+ node_for(key).smismember(key, *members)
609
+ end
610
+
507
611
  # Get all the members in a set.
508
612
  def smembers(key)
509
613
  node_for(key).smembers(key)
510
614
  end
511
615
 
616
+ # Scan a set
617
+ def sscan(key, cursor, **options)
618
+ node_for(key).sscan(key, cursor, **options)
619
+ end
620
+
621
+ # Scan a set and return an enumerator
622
+ def sscan_each(key, **options, &block)
623
+ node_for(key).sscan_each(key, **options, &block)
624
+ end
625
+
512
626
  # Subtract multiple sets.
513
627
  def sdiff(*keys)
628
+ keys.flatten!(1)
514
629
  ensure_same_node(:sdiff, keys) do |node|
515
- node.sdiff(*keys)
630
+ node.sdiff(keys)
516
631
  end
517
632
  end
518
633
 
519
634
  # Subtract multiple sets and store the resulting set in a key.
520
635
  def sdiffstore(destination, *keys)
521
- ensure_same_node(:sdiffstore, [destination] + keys) do |node|
522
- node.sdiffstore(destination, *keys)
636
+ keys.flatten!(1)
637
+ ensure_same_node(:sdiffstore, [destination].concat(keys)) do |node|
638
+ node.sdiffstore(destination, keys)
523
639
  end
524
640
  end
525
641
 
526
642
  # Intersect multiple sets.
527
643
  def sinter(*keys)
644
+ keys.flatten!(1)
528
645
  ensure_same_node(:sinter, keys) do |node|
529
- node.sinter(*keys)
646
+ node.sinter(keys)
530
647
  end
531
648
  end
532
649
 
533
650
  # Intersect multiple sets and store the resulting set in a key.
534
651
  def sinterstore(destination, *keys)
535
- ensure_same_node(:sinterstore, [destination] + keys) do |node|
536
- node.sinterstore(destination, *keys)
652
+ keys.flatten!(1)
653
+ ensure_same_node(:sinterstore, [destination].concat(keys)) do |node|
654
+ node.sinterstore(destination, keys)
537
655
  end
538
656
  end
539
657
 
540
658
  # Add multiple sets.
541
659
  def sunion(*keys)
660
+ keys.flatten!(1)
542
661
  ensure_same_node(:sunion, keys) do |node|
543
- node.sunion(*keys)
662
+ node.sunion(keys)
544
663
  end
545
664
  end
546
665
 
547
666
  # Add multiple sets and store the resulting set in a key.
548
667
  def sunionstore(destination, *keys)
549
- ensure_same_node(:sunionstore, [destination] + keys) do |node|
550
- node.sunionstore(destination, *keys)
668
+ keys.flatten!(1)
669
+ ensure_same_node(:sunionstore, [destination].concat(keys)) do |node|
670
+ node.sunionstore(destination, keys)
551
671
  end
552
672
  end
553
673
 
@@ -561,6 +681,7 @@ class Redis
561
681
  def zadd(key, *args)
562
682
  node_for(key).zadd(key, *args)
563
683
  end
684
+ ruby2_keywords(:zadd) if respond_to?(:ruby2_keywords, true)
564
685
 
565
686
  # Increment the score of a member in a sorted set.
566
687
  def zincrby(key, increment, member)
@@ -577,15 +698,47 @@ class Redis
577
698
  node_for(key).zscore(key, member)
578
699
  end
579
700
 
580
- # Return a range of members in a sorted set, by index.
581
- def zrange(key, start, stop, options = {})
582
- node_for(key).zrange(key, start, stop, options)
701
+ # Get one or more random members from a sorted set.
702
+ def zrandmember(key, count = nil, **options)
703
+ node_for(key).zrandmember(key, count, **options)
704
+ end
705
+
706
+ # Get the scores associated with the given members in a sorted set.
707
+ def zmscore(key, *members)
708
+ node_for(key).zmscore(key, *members)
709
+ end
710
+
711
+ # Iterate over keys, blocking and removing members from the first non empty sorted set found.
712
+ def bzmpop(timeout, *keys, modifier: "MIN", count: nil)
713
+ ensure_same_node(:bzmpop, keys) do |node|
714
+ node.bzmpop(timeout, *keys, modifier: modifier, count: count)
715
+ end
716
+ end
717
+
718
+ # Iterate over keys, removing members from the first non empty sorted set found.
719
+ def zmpop(*keys, modifier: "MIN", count: nil)
720
+ ensure_same_node(:zmpop, keys) do |node|
721
+ node.zmpop(*keys, modifier: modifier, count: count)
722
+ end
723
+ end
724
+
725
+ # Return a range of members in a sorted set, by index, score or lexicographical ordering.
726
+ def zrange(key, start, stop, **options)
727
+ node_for(key).zrange(key, start, stop, **options)
728
+ end
729
+
730
+ # Select a range of members in a sorted set, by index, score or lexicographical ordering
731
+ # and store the resulting sorted set in a new key.
732
+ def zrangestore(dest_key, src_key, start, stop, **options)
733
+ ensure_same_node(:zrangestore, [dest_key, src_key]) do |node|
734
+ node.zrangestore(dest_key, src_key, start, stop, **options)
735
+ end
583
736
  end
584
737
 
585
738
  # Return a range of members in a sorted set, by index, with scores ordered
586
739
  # from high to low.
587
- def zrevrange(key, start, stop, options = {})
588
- node_for(key).zrevrange(key, start, stop, options)
740
+ def zrevrange(key, start, stop, **options)
741
+ node_for(key).zrevrange(key, start, stop, **options)
589
742
  end
590
743
 
591
744
  # Determine the index of a member in a sorted set.
@@ -605,14 +758,14 @@ class Redis
605
758
  end
606
759
 
607
760
  # Return a range of members in a sorted set, by score.
608
- def zrangebyscore(key, min, max, options = {})
609
- node_for(key).zrangebyscore(key, min, max, options)
761
+ def zrangebyscore(key, min, max, **options)
762
+ node_for(key).zrangebyscore(key, min, max, **options)
610
763
  end
611
764
 
612
765
  # Return a range of members in a sorted set, by score, with scores ordered
613
766
  # from high to low.
614
- def zrevrangebyscore(key, max, min, options = {})
615
- node_for(key).zrevrangebyscore(key, max, min, options)
767
+ def zrevrangebyscore(key, max, min, **options)
768
+ node_for(key).zrevrangebyscore(key, max, min, **options)
616
769
  end
617
770
 
618
771
  # Remove all members in a sorted set within the given scores.
@@ -625,18 +778,53 @@ class Redis
625
778
  node_for(key).zcount(key, min, max)
626
779
  end
627
780
 
781
+ # Get the intersection of multiple sorted sets
782
+ def zinter(*keys, **options)
783
+ keys.flatten!(1)
784
+ ensure_same_node(:zinter, keys) do |node|
785
+ node.zinter(keys, **options)
786
+ end
787
+ end
788
+
628
789
  # Intersect multiple sorted sets and store the resulting sorted set in a new
629
790
  # key.
630
- def zinterstore(destination, keys, options = {})
631
- ensure_same_node(:zinterstore, [destination] + keys) do |node|
632
- node.zinterstore(destination, keys, options)
791
+ def zinterstore(destination, *keys, **options)
792
+ keys.flatten!(1)
793
+ ensure_same_node(:zinterstore, [destination].concat(keys)) do |node|
794
+ node.zinterstore(destination, keys, **options)
795
+ end
796
+ end
797
+
798
+ # Return the union of multiple sorted sets.
799
+ def zunion(*keys, **options)
800
+ keys.flatten!(1)
801
+ ensure_same_node(:zunion, keys) do |node|
802
+ node.zunion(keys, **options)
633
803
  end
634
804
  end
635
805
 
636
806
  # Add multiple sorted sets and store the resulting sorted set in a new key.
637
- def zunionstore(destination, keys, options = {})
638
- ensure_same_node(:zunionstore, [destination] + keys) do |node|
639
- node.zunionstore(destination, keys, options)
807
+ def zunionstore(destination, *keys, **options)
808
+ keys.flatten!(1)
809
+ ensure_same_node(:zunionstore, [destination].concat(keys)) do |node|
810
+ node.zunionstore(destination, keys, **options)
811
+ end
812
+ end
813
+
814
+ # Return the difference between the first and all successive input sorted sets.
815
+ def zdiff(*keys, **options)
816
+ keys.flatten!(1)
817
+ ensure_same_node(:zdiff, keys) do |node|
818
+ node.zdiff(keys, **options)
819
+ end
820
+ end
821
+
822
+ # Compute the difference between the first and all successive input sorted sets
823
+ # and store the resulting sorted set in a new key.
824
+ def zdiffstore(destination, *keys, **options)
825
+ keys.flatten!(1)
826
+ ensure_same_node(:zdiffstore, [destination] + keys) do |node|
827
+ node.zdiffstore(destination, keys, **options)
640
828
  end
641
829
  end
642
830
 
@@ -645,9 +833,9 @@ class Redis
645
833
  node_for(key).hlen(key)
646
834
  end
647
835
 
648
- # Set the string value of a hash field.
649
- def hset(key, field, value)
650
- node_for(key).hset(key, field, value)
836
+ # Set multiple hash fields to multiple values.
837
+ def hset(key, *attrs)
838
+ node_for(key).hset(key, *attrs)
651
839
  end
652
840
 
653
841
  # Set the value of a hash field, only if the field does not exist.
@@ -661,7 +849,7 @@ class Redis
661
849
  end
662
850
 
663
851
  def mapped_hmset(key, hash)
664
- node_for(key).hmset(key, *hash.to_a.flatten)
852
+ node_for(key).hmset(key, hash)
665
853
  end
666
854
 
667
855
  # Get the value of a hash field.
@@ -671,16 +859,23 @@ class Redis
671
859
 
672
860
  # Get the values of all the given hash fields.
673
861
  def hmget(key, *fields)
674
- node_for(key).hmget(key, *fields)
862
+ fields.flatten!(1)
863
+ node_for(key).hmget(key, fields)
675
864
  end
676
865
 
677
866
  def mapped_hmget(key, *fields)
678
- Hash[*fields.zip(hmget(key, *fields)).flatten]
867
+ fields.flatten!(1)
868
+ node_for(key).mapped_hmget(key, fields)
869
+ end
870
+
871
+ def hrandfield(key, count = nil, **options)
872
+ node_for(key).hrandfield(key, count, **options)
679
873
  end
680
874
 
681
875
  # Delete one or more hash fields.
682
- def hdel(key, field)
683
- node_for(key).hdel(key, field)
876
+ def hdel(key, *fields)
877
+ fields.flatten!(1)
878
+ node_for(key).hdel(key, fields)
684
879
  end
685
880
 
686
881
  # Determine if a hash field exists.
@@ -719,7 +914,7 @@ class Redis
719
914
  end
720
915
 
721
916
  def subscribed?
722
- !! @subscribed_node
917
+ !!@subscribed_node
723
918
  end
724
919
 
725
920
  # Listen for messages published to the given channels.
@@ -737,7 +932,8 @@ class Redis
737
932
 
738
933
  # Stop listening for messages posted to the given channels.
739
934
  def unsubscribe(*channels)
740
- raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
935
+ raise SubscriptionError, "Can't unsubscribe if not subscribed." unless subscribed?
936
+
741
937
  @subscribed_node.unsubscribe(*channels)
742
938
  end
743
939
 
@@ -753,13 +949,26 @@ class Redis
753
949
  end
754
950
 
755
951
  # Watch the given keys to determine execution of the MULTI/EXEC block.
756
- def watch(*keys)
757
- raise CannotDistribute, :watch
952
+ def watch(*keys, &block)
953
+ ensure_same_node(:watch, keys) do |node|
954
+ @watch_key = key_tag(keys.first) || keys.first.to_s
955
+
956
+ begin
957
+ node.watch(*keys, &block)
958
+ rescue StandardError
959
+ @watch_key = nil
960
+ raise
961
+ end
962
+ end
758
963
  end
759
964
 
760
965
  # Forget about all watched keys.
761
966
  def unwatch
762
- raise CannotDistribute, :unwatch
967
+ raise CannotDistribute, :unwatch unless @watch_key
968
+
969
+ result = node_for(@watch_key).unwatch
970
+ @watch_key = nil
971
+ result
763
972
  end
764
973
 
765
974
  def pipelined
@@ -767,18 +976,28 @@ class Redis
767
976
  end
768
977
 
769
978
  # Mark the start of a transaction block.
770
- def multi
771
- raise CannotDistribute, :multi
979
+ def multi(&block)
980
+ raise CannotDistribute, :multi unless @watch_key
981
+
982
+ node_for(@watch_key).multi(&block)
772
983
  end
773
984
 
774
985
  # Execute all commands issued after MULTI.
775
986
  def exec
776
- raise CannotDistribute, :exec
987
+ raise CannotDistribute, :exec unless @watch_key
988
+
989
+ result = node_for(@watch_key).exec
990
+ @watch_key = nil
991
+ result
777
992
  end
778
993
 
779
994
  # Discard all commands issued after MULTI.
780
995
  def discard
781
- raise CannotDistribute, :discard
996
+ raise CannotDistribute, :discard unless @watch_key
997
+
998
+ result = node_for(@watch_key).discard
999
+ @watch_key = nil
1000
+ result
782
1001
  end
783
1002
 
784
1003
  # Control remote script registry.
@@ -837,7 +1056,7 @@ class Redis
837
1056
  self.class.new(@node_configs, @default_options)
838
1057
  end
839
1058
 
840
- protected
1059
+ protected
841
1060
 
842
1061
  def on_each_node(command, *args)
843
1062
  nodes.map do |node|
@@ -850,7 +1069,8 @@ class Redis
850
1069
  end
851
1070
 
852
1071
  def key_tag(key)
853
- key.to_s[@tag, 1] if @tag
1072
+ key = key.to_s
1073
+ key[@tag, 1] if key.match?(@tag)
854
1074
  end
855
1075
 
856
1076
  def ensure_same_node(command, keys)