redis 3.3.3 → 5.0.5

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 (136) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +280 -12
  3. data/README.md +141 -147
  4. data/lib/redis/client.rb +77 -539
  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 +285 -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 +818 -0
  18. data/lib/redis/commands/streams.rb +384 -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 +235 -0
  22. data/lib/redis/distributed.rb +300 -108
  23. data/lib/redis/errors.rb +22 -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 +113 -2685
  29. metadata +40 -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/db/.gitkeep +0 -0
  73. data/test/distributed_blocking_commands_test.rb +0 -46
  74. data/test/distributed_commands_on_hashes_test.rb +0 -10
  75. data/test/distributed_commands_on_hyper_log_log_test.rb +0 -33
  76. data/test/distributed_commands_on_lists_test.rb +0 -22
  77. data/test/distributed_commands_on_sets_test.rb +0 -83
  78. data/test/distributed_commands_on_sorted_sets_test.rb +0 -18
  79. data/test/distributed_commands_on_strings_test.rb +0 -59
  80. data/test/distributed_commands_on_value_types_test.rb +0 -95
  81. data/test/distributed_commands_requiring_clustering_test.rb +0 -164
  82. data/test/distributed_connection_handling_test.rb +0 -23
  83. data/test/distributed_internals_test.rb +0 -79
  84. data/test/distributed_key_tags_test.rb +0 -52
  85. data/test/distributed_persistence_control_commands_test.rb +0 -26
  86. data/test/distributed_publish_subscribe_test.rb +0 -92
  87. data/test/distributed_remote_server_control_commands_test.rb +0 -66
  88. data/test/distributed_scripting_test.rb +0 -102
  89. data/test/distributed_sorting_test.rb +0 -20
  90. data/test/distributed_test.rb +0 -58
  91. data/test/distributed_transactions_test.rb +0 -32
  92. data/test/encoding_test.rb +0 -18
  93. data/test/error_replies_test.rb +0 -59
  94. data/test/fork_safety_test.rb +0 -65
  95. data/test/helper.rb +0 -232
  96. data/test/helper_test.rb +0 -24
  97. data/test/internals_test.rb +0 -457
  98. data/test/lint/blocking_commands.rb +0 -150
  99. data/test/lint/hashes.rb +0 -162
  100. data/test/lint/hyper_log_log.rb +0 -60
  101. data/test/lint/lists.rb +0 -143
  102. data/test/lint/sets.rb +0 -140
  103. data/test/lint/sorted_sets.rb +0 -316
  104. data/test/lint/strings.rb +0 -260
  105. data/test/lint/value_types.rb +0 -122
  106. data/test/persistence_control_commands_test.rb +0 -26
  107. data/test/pipelining_commands_test.rb +0 -242
  108. data/test/publish_subscribe_test.rb +0 -282
  109. data/test/remote_server_control_commands_test.rb +0 -118
  110. data/test/scanning_test.rb +0 -413
  111. data/test/scripting_test.rb +0 -78
  112. data/test/sentinel_command_test.rb +0 -80
  113. data/test/sentinel_test.rb +0 -255
  114. data/test/sorting_test.rb +0 -59
  115. data/test/ssl_test.rb +0 -73
  116. data/test/support/connection/hiredis.rb +0 -1
  117. data/test/support/connection/ruby.rb +0 -1
  118. data/test/support/connection/synchrony.rb +0 -17
  119. data/test/support/redis_mock.rb +0 -130
  120. data/test/support/ssl/gen_certs.sh +0 -31
  121. data/test/support/ssl/trusted-ca.crt +0 -25
  122. data/test/support/ssl/trusted-ca.key +0 -27
  123. data/test/support/ssl/trusted-cert.crt +0 -81
  124. data/test/support/ssl/trusted-cert.key +0 -28
  125. data/test/support/ssl/untrusted-ca.crt +0 -26
  126. data/test/support/ssl/untrusted-ca.key +0 -27
  127. data/test/support/ssl/untrusted-cert.crt +0 -82
  128. data/test/support/ssl/untrusted-cert.key +0 -28
  129. data/test/support/wire/synchrony.rb +0 -24
  130. data/test/support/wire/thread.rb +0 -5
  131. data/test/synchrony_driver.rb +0 -88
  132. data/test/test.conf.erb +0 -9
  133. data/test/thread_safety_test.rb +0 -62
  134. data/test/transactions_test.rb +0 -264
  135. data/test/unknown_commands_test.rb +0 -14
  136. 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
469
+ options[:timeout]
401
470
  end
402
471
 
403
- if args.size > 1
404
- # Issue deprecation notice in obnoxious mode...
405
- end
472
+ args.flatten!(1)
406
473
 
407
- keys = args.flatten
408
-
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
 
@@ -473,13 +548,23 @@ class Redis
473
548
  end
474
549
 
475
550
  # Add one or more members to a set.
476
- def sadd(key, member)
477
- node_for(key).sadd(key, member)
551
+ def sadd(key, *members)
552
+ node_for(key).sadd(key, *members)
553
+ end
554
+
555
+ # Add one or more members to a set.
556
+ def sadd?(key, *members)
557
+ node_for(key).sadd?(key, *members)
478
558
  end
479
559
 
480
560
  # Remove one or more members from a set.
481
- def srem(key, member)
482
- node_for(key).srem(key, member)
561
+ def srem(key, *members)
562
+ node_for(key).srem(key, *members)
563
+ end
564
+
565
+ # Remove one or more members from a set.
566
+ def srem?(key, *members)
567
+ node_for(key).srem?(key, *members)
483
568
  end
484
569
 
485
570
  # Remove and return a random member from a set.
@@ -504,50 +589,71 @@ class Redis
504
589
  node_for(key).sismember(key, member)
505
590
  end
506
591
 
592
+ # Determine if multiple values are members of a set.
593
+ def smismember(key, *members)
594
+ node_for(key).smismember(key, *members)
595
+ end
596
+
507
597
  # Get all the members in a set.
508
598
  def smembers(key)
509
599
  node_for(key).smembers(key)
510
600
  end
511
601
 
602
+ # Scan a set
603
+ def sscan(key, cursor, **options)
604
+ node_for(key).sscan(key, cursor, **options)
605
+ end
606
+
607
+ # Scan a set and return an enumerator
608
+ def sscan_each(key, **options, &block)
609
+ node_for(key).sscan_each(key, **options, &block)
610
+ end
611
+
512
612
  # Subtract multiple sets.
513
613
  def sdiff(*keys)
614
+ keys.flatten!(1)
514
615
  ensure_same_node(:sdiff, keys) do |node|
515
- node.sdiff(*keys)
616
+ node.sdiff(keys)
516
617
  end
517
618
  end
518
619
 
519
620
  # Subtract multiple sets and store the resulting set in a key.
520
621
  def sdiffstore(destination, *keys)
521
- ensure_same_node(:sdiffstore, [destination] + keys) do |node|
522
- node.sdiffstore(destination, *keys)
622
+ keys.flatten!(1)
623
+ ensure_same_node(:sdiffstore, [destination].concat(keys)) do |node|
624
+ node.sdiffstore(destination, keys)
523
625
  end
524
626
  end
525
627
 
526
628
  # Intersect multiple sets.
527
629
  def sinter(*keys)
630
+ keys.flatten!(1)
528
631
  ensure_same_node(:sinter, keys) do |node|
529
- node.sinter(*keys)
632
+ node.sinter(keys)
530
633
  end
531
634
  end
532
635
 
533
636
  # Intersect multiple sets and store the resulting set in a key.
534
637
  def sinterstore(destination, *keys)
535
- ensure_same_node(:sinterstore, [destination] + keys) do |node|
536
- node.sinterstore(destination, *keys)
638
+ keys.flatten!(1)
639
+ ensure_same_node(:sinterstore, [destination].concat(keys)) do |node|
640
+ node.sinterstore(destination, keys)
537
641
  end
538
642
  end
539
643
 
540
644
  # Add multiple sets.
541
645
  def sunion(*keys)
646
+ keys.flatten!(1)
542
647
  ensure_same_node(:sunion, keys) do |node|
543
- node.sunion(*keys)
648
+ node.sunion(keys)
544
649
  end
545
650
  end
546
651
 
547
652
  # Add multiple sets and store the resulting set in a key.
548
653
  def sunionstore(destination, *keys)
549
- ensure_same_node(:sunionstore, [destination] + keys) do |node|
550
- node.sunionstore(destination, *keys)
654
+ keys.flatten!(1)
655
+ ensure_same_node(:sunionstore, [destination].concat(keys)) do |node|
656
+ node.sunionstore(destination, keys)
551
657
  end
552
658
  end
553
659
 
@@ -561,6 +667,7 @@ class Redis
561
667
  def zadd(key, *args)
562
668
  node_for(key).zadd(key, *args)
563
669
  end
670
+ ruby2_keywords(:zadd) if respond_to?(:ruby2_keywords, true)
564
671
 
565
672
  # Increment the score of a member in a sorted set.
566
673
  def zincrby(key, increment, member)
@@ -577,15 +684,33 @@ class Redis
577
684
  node_for(key).zscore(key, member)
578
685
  end
579
686
 
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)
687
+ # Get one or more random members from a sorted set.
688
+ def zrandmember(key, count = nil, **options)
689
+ node_for(key).zrandmember(key, count, **options)
690
+ end
691
+
692
+ # Get the scores associated with the given members in a sorted set.
693
+ def zmscore(key, *members)
694
+ node_for(key).zmscore(key, *members)
695
+ end
696
+
697
+ # Return a range of members in a sorted set, by index, score or lexicographical ordering.
698
+ def zrange(key, start, stop, **options)
699
+ node_for(key).zrange(key, start, stop, **options)
700
+ end
701
+
702
+ # Select a range of members in a sorted set, by index, score or lexicographical ordering
703
+ # and store the resulting sorted set in a new key.
704
+ def zrangestore(dest_key, src_key, start, stop, **options)
705
+ ensure_same_node(:zrangestore, [dest_key, src_key]) do |node|
706
+ node.zrangestore(dest_key, src_key, start, stop, **options)
707
+ end
583
708
  end
584
709
 
585
710
  # Return a range of members in a sorted set, by index, with scores ordered
586
711
  # from high to low.
587
- def zrevrange(key, start, stop, options = {})
588
- node_for(key).zrevrange(key, start, stop, options)
712
+ def zrevrange(key, start, stop, **options)
713
+ node_for(key).zrevrange(key, start, stop, **options)
589
714
  end
590
715
 
591
716
  # Determine the index of a member in a sorted set.
@@ -605,14 +730,14 @@ class Redis
605
730
  end
606
731
 
607
732
  # 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)
733
+ def zrangebyscore(key, min, max, **options)
734
+ node_for(key).zrangebyscore(key, min, max, **options)
610
735
  end
611
736
 
612
737
  # Return a range of members in a sorted set, by score, with scores ordered
613
738
  # from high to low.
614
- def zrevrangebyscore(key, max, min, options = {})
615
- node_for(key).zrevrangebyscore(key, max, min, options)
739
+ def zrevrangebyscore(key, max, min, **options)
740
+ node_for(key).zrevrangebyscore(key, max, min, **options)
616
741
  end
617
742
 
618
743
  # Remove all members in a sorted set within the given scores.
@@ -625,18 +750,53 @@ class Redis
625
750
  node_for(key).zcount(key, min, max)
626
751
  end
627
752
 
753
+ # Get the intersection of multiple sorted sets
754
+ def zinter(*keys, **options)
755
+ keys.flatten!(1)
756
+ ensure_same_node(:zinter, keys) do |node|
757
+ node.zinter(keys, **options)
758
+ end
759
+ end
760
+
628
761
  # Intersect multiple sorted sets and store the resulting sorted set in a new
629
762
  # key.
630
- def zinterstore(destination, keys, options = {})
631
- ensure_same_node(:zinterstore, [destination] + keys) do |node|
632
- node.zinterstore(destination, keys, options)
763
+ def zinterstore(destination, *keys, **options)
764
+ keys.flatten!(1)
765
+ ensure_same_node(:zinterstore, [destination].concat(keys)) do |node|
766
+ node.zinterstore(destination, keys, **options)
767
+ end
768
+ end
769
+
770
+ # Return the union of multiple sorted sets.
771
+ def zunion(*keys, **options)
772
+ keys.flatten!(1)
773
+ ensure_same_node(:zunion, keys) do |node|
774
+ node.zunion(keys, **options)
633
775
  end
634
776
  end
635
777
 
636
778
  # 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)
779
+ def zunionstore(destination, *keys, **options)
780
+ keys.flatten!(1)
781
+ ensure_same_node(:zunionstore, [destination].concat(keys)) do |node|
782
+ node.zunionstore(destination, keys, **options)
783
+ end
784
+ end
785
+
786
+ # Return the difference between the first and all successive input sorted sets.
787
+ def zdiff(*keys, **options)
788
+ keys.flatten!(1)
789
+ ensure_same_node(:zdiff, keys) do |node|
790
+ node.zdiff(keys, **options)
791
+ end
792
+ end
793
+
794
+ # Compute the difference between the first and all successive input sorted sets
795
+ # and store the resulting sorted set in a new key.
796
+ def zdiffstore(destination, *keys, **options)
797
+ keys.flatten!(1)
798
+ ensure_same_node(:zdiffstore, [destination] + keys) do |node|
799
+ node.zdiffstore(destination, keys, **options)
640
800
  end
641
801
  end
642
802
 
@@ -645,9 +805,9 @@ class Redis
645
805
  node_for(key).hlen(key)
646
806
  end
647
807
 
648
- # Set the string value of a hash field.
649
- def hset(key, field, value)
650
- node_for(key).hset(key, field, value)
808
+ # Set multiple hash fields to multiple values.
809
+ def hset(key, *attrs)
810
+ node_for(key).hset(key, *attrs)
651
811
  end
652
812
 
653
813
  # Set the value of a hash field, only if the field does not exist.
@@ -661,7 +821,7 @@ class Redis
661
821
  end
662
822
 
663
823
  def mapped_hmset(key, hash)
664
- node_for(key).hmset(key, *hash.to_a.flatten)
824
+ node_for(key).hmset(key, hash)
665
825
  end
666
826
 
667
827
  # Get the value of a hash field.
@@ -671,16 +831,23 @@ class Redis
671
831
 
672
832
  # Get the values of all the given hash fields.
673
833
  def hmget(key, *fields)
674
- node_for(key).hmget(key, *fields)
834
+ fields.flatten!(1)
835
+ node_for(key).hmget(key, fields)
675
836
  end
676
837
 
677
838
  def mapped_hmget(key, *fields)
678
- Hash[*fields.zip(hmget(key, *fields)).flatten]
839
+ fields.flatten!(1)
840
+ node_for(key).mapped_hmget(key, fields)
841
+ end
842
+
843
+ def hrandfield(key, count = nil, **options)
844
+ node_for(key).hrandfield(key, count, **options)
679
845
  end
680
846
 
681
847
  # Delete one or more hash fields.
682
- def hdel(key, field)
683
- node_for(key).hdel(key, field)
848
+ def hdel(key, *fields)
849
+ fields.flatten!(1)
850
+ node_for(key).hdel(key, fields)
684
851
  end
685
852
 
686
853
  # Determine if a hash field exists.
@@ -719,7 +886,7 @@ class Redis
719
886
  end
720
887
 
721
888
  def subscribed?
722
- !! @subscribed_node
889
+ !!@subscribed_node
723
890
  end
724
891
 
725
892
  # Listen for messages published to the given channels.
@@ -737,7 +904,8 @@ class Redis
737
904
 
738
905
  # Stop listening for messages posted to the given channels.
739
906
  def unsubscribe(*channels)
740
- raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
907
+ raise SubscriptionError, "Can't unsubscribe if not subscribed." unless subscribed?
908
+
741
909
  @subscribed_node.unsubscribe(*channels)
742
910
  end
743
911
 
@@ -753,13 +921,26 @@ class Redis
753
921
  end
754
922
 
755
923
  # Watch the given keys to determine execution of the MULTI/EXEC block.
756
- def watch(*keys)
757
- raise CannotDistribute, :watch
924
+ def watch(*keys, &block)
925
+ ensure_same_node(:watch, keys) do |node|
926
+ @watch_key = key_tag(keys.first) || keys.first.to_s
927
+
928
+ begin
929
+ node.watch(*keys, &block)
930
+ rescue StandardError
931
+ @watch_key = nil
932
+ raise
933
+ end
934
+ end
758
935
  end
759
936
 
760
937
  # Forget about all watched keys.
761
938
  def unwatch
762
- raise CannotDistribute, :unwatch
939
+ raise CannotDistribute, :unwatch unless @watch_key
940
+
941
+ result = node_for(@watch_key).unwatch
942
+ @watch_key = nil
943
+ result
763
944
  end
764
945
 
765
946
  def pipelined
@@ -767,18 +948,28 @@ class Redis
767
948
  end
768
949
 
769
950
  # Mark the start of a transaction block.
770
- def multi
771
- raise CannotDistribute, :multi
951
+ def multi(&block)
952
+ raise CannotDistribute, :multi unless @watch_key
953
+
954
+ node_for(@watch_key).multi(&block)
772
955
  end
773
956
 
774
957
  # Execute all commands issued after MULTI.
775
958
  def exec
776
- raise CannotDistribute, :exec
959
+ raise CannotDistribute, :exec unless @watch_key
960
+
961
+ result = node_for(@watch_key).exec
962
+ @watch_key = nil
963
+ result
777
964
  end
778
965
 
779
966
  # Discard all commands issued after MULTI.
780
967
  def discard
781
- raise CannotDistribute, :discard
968
+ raise CannotDistribute, :discard unless @watch_key
969
+
970
+ result = node_for(@watch_key).discard
971
+ @watch_key = nil
972
+ result
782
973
  end
783
974
 
784
975
  # Control remote script registry.
@@ -837,7 +1028,7 @@ class Redis
837
1028
  self.class.new(@node_configs, @default_options)
838
1029
  end
839
1030
 
840
- protected
1031
+ protected
841
1032
 
842
1033
  def on_each_node(command, *args)
843
1034
  nodes.map do |node|
@@ -850,7 +1041,8 @@ class Redis
850
1041
  end
851
1042
 
852
1043
  def key_tag(key)
853
- key.to_s[@tag, 1] if @tag
1044
+ key = key.to_s
1045
+ key[@tag, 1] if key.match?(@tag)
854
1046
  end
855
1047
 
856
1048
  def ensure_same_node(command, keys)