redis 3.3.3 → 5.0.5

Sign up to get free protection for your applications and to get access to all the features.
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)