redis 3.3.5 → 4.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +232 -2
  3. data/README.md +169 -89
  4. data/lib/redis/client.rb +177 -100
  5. data/lib/redis/cluster/command.rb +79 -0
  6. data/lib/redis/cluster/command_loader.rb +33 -0
  7. data/lib/redis/cluster/key_slot_converter.rb +72 -0
  8. data/lib/redis/cluster/node.rb +120 -0
  9. data/lib/redis/cluster/node_key.rb +31 -0
  10. data/lib/redis/cluster/node_loader.rb +34 -0
  11. data/lib/redis/cluster/option.rb +100 -0
  12. data/lib/redis/cluster/slot.rb +86 -0
  13. data/lib/redis/cluster/slot_loader.rb +46 -0
  14. data/lib/redis/cluster.rb +315 -0
  15. data/lib/redis/commands/bitmaps.rb +63 -0
  16. data/lib/redis/commands/cluster.rb +45 -0
  17. data/lib/redis/commands/connection.rb +58 -0
  18. data/lib/redis/commands/geo.rb +84 -0
  19. data/lib/redis/commands/hashes.rb +251 -0
  20. data/lib/redis/commands/hyper_log_log.rb +37 -0
  21. data/lib/redis/commands/keys.rb +455 -0
  22. data/lib/redis/commands/lists.rb +290 -0
  23. data/lib/redis/commands/pubsub.rb +72 -0
  24. data/lib/redis/commands/scripting.rb +114 -0
  25. data/lib/redis/commands/server.rb +188 -0
  26. data/lib/redis/commands/sets.rb +223 -0
  27. data/lib/redis/commands/sorted_sets.rb +812 -0
  28. data/lib/redis/commands/streams.rb +382 -0
  29. data/lib/redis/commands/strings.rb +313 -0
  30. data/lib/redis/commands/transactions.rb +139 -0
  31. data/lib/redis/commands.rb +240 -0
  32. data/lib/redis/connection/command_helper.rb +7 -10
  33. data/lib/redis/connection/hiredis.rb +5 -3
  34. data/lib/redis/connection/registry.rb +2 -1
  35. data/lib/redis/connection/ruby.rb +136 -128
  36. data/lib/redis/connection/synchrony.rb +24 -9
  37. data/lib/redis/connection.rb +3 -1
  38. data/lib/redis/distributed.rb +255 -85
  39. data/lib/redis/errors.rb +57 -0
  40. data/lib/redis/hash_ring.rb +30 -73
  41. data/lib/redis/pipeline.rb +178 -13
  42. data/lib/redis/subscribe.rb +11 -12
  43. data/lib/redis/version.rb +3 -1
  44. data/lib/redis.rb +174 -2661
  45. metadata +66 -202
  46. data/.gitignore +0 -16
  47. data/.travis/Gemfile +0 -11
  48. data/.travis.yml +0 -89
  49. data/.yardopts +0 -3
  50. data/Gemfile +0 -4
  51. data/Rakefile +0 -87
  52. data/benchmarking/logging.rb +0 -71
  53. data/benchmarking/pipeline.rb +0 -51
  54. data/benchmarking/speed.rb +0 -21
  55. data/benchmarking/suite.rb +0 -24
  56. data/benchmarking/worker.rb +0 -71
  57. data/examples/basic.rb +0 -15
  58. data/examples/consistency.rb +0 -114
  59. data/examples/dist_redis.rb +0 -43
  60. data/examples/incr-decr.rb +0 -17
  61. data/examples/list.rb +0 -26
  62. data/examples/pubsub.rb +0 -37
  63. data/examples/sentinel/sentinel.conf +0 -9
  64. data/examples/sentinel/start +0 -49
  65. data/examples/sentinel.rb +0 -41
  66. data/examples/sets.rb +0 -36
  67. data/examples/unicorn/config.ru +0 -3
  68. data/examples/unicorn/unicorn.rb +0 -20
  69. data/redis.gemspec +0 -44
  70. data/test/bitpos_test.rb +0 -69
  71. data/test/blocking_commands_test.rb +0 -42
  72. data/test/client_test.rb +0 -59
  73. data/test/command_map_test.rb +0 -30
  74. data/test/commands_on_hashes_test.rb +0 -21
  75. data/test/commands_on_hyper_log_log_test.rb +0 -21
  76. data/test/commands_on_lists_test.rb +0 -20
  77. data/test/commands_on_sets_test.rb +0 -77
  78. data/test/commands_on_sorted_sets_test.rb +0 -137
  79. data/test/commands_on_strings_test.rb +0 -101
  80. data/test/commands_on_value_types_test.rb +0 -133
  81. data/test/connection_handling_test.rb +0 -277
  82. data/test/connection_test.rb +0 -57
  83. data/test/db/.gitkeep +0 -0
  84. data/test/distributed_blocking_commands_test.rb +0 -46
  85. data/test/distributed_commands_on_hashes_test.rb +0 -10
  86. data/test/distributed_commands_on_hyper_log_log_test.rb +0 -33
  87. data/test/distributed_commands_on_lists_test.rb +0 -22
  88. data/test/distributed_commands_on_sets_test.rb +0 -83
  89. data/test/distributed_commands_on_sorted_sets_test.rb +0 -18
  90. data/test/distributed_commands_on_strings_test.rb +0 -59
  91. data/test/distributed_commands_on_value_types_test.rb +0 -95
  92. data/test/distributed_commands_requiring_clustering_test.rb +0 -164
  93. data/test/distributed_connection_handling_test.rb +0 -23
  94. data/test/distributed_internals_test.rb +0 -79
  95. data/test/distributed_key_tags_test.rb +0 -52
  96. data/test/distributed_persistence_control_commands_test.rb +0 -26
  97. data/test/distributed_publish_subscribe_test.rb +0 -92
  98. data/test/distributed_remote_server_control_commands_test.rb +0 -66
  99. data/test/distributed_scripting_test.rb +0 -102
  100. data/test/distributed_sorting_test.rb +0 -20
  101. data/test/distributed_test.rb +0 -58
  102. data/test/distributed_transactions_test.rb +0 -32
  103. data/test/encoding_test.rb +0 -18
  104. data/test/error_replies_test.rb +0 -59
  105. data/test/fork_safety_test.rb +0 -65
  106. data/test/helper.rb +0 -232
  107. data/test/helper_test.rb +0 -24
  108. data/test/internals_test.rb +0 -417
  109. data/test/lint/blocking_commands.rb +0 -150
  110. data/test/lint/hashes.rb +0 -162
  111. data/test/lint/hyper_log_log.rb +0 -60
  112. data/test/lint/lists.rb +0 -143
  113. data/test/lint/sets.rb +0 -140
  114. data/test/lint/sorted_sets.rb +0 -316
  115. data/test/lint/strings.rb +0 -260
  116. data/test/lint/value_types.rb +0 -122
  117. data/test/persistence_control_commands_test.rb +0 -26
  118. data/test/pipelining_commands_test.rb +0 -242
  119. data/test/publish_subscribe_test.rb +0 -282
  120. data/test/remote_server_control_commands_test.rb +0 -118
  121. data/test/scanning_test.rb +0 -413
  122. data/test/scripting_test.rb +0 -78
  123. data/test/sentinel_command_test.rb +0 -80
  124. data/test/sentinel_test.rb +0 -255
  125. data/test/sorting_test.rb +0 -59
  126. data/test/ssl_test.rb +0 -73
  127. data/test/support/connection/hiredis.rb +0 -1
  128. data/test/support/connection/ruby.rb +0 -1
  129. data/test/support/connection/synchrony.rb +0 -17
  130. data/test/support/redis_mock.rb +0 -130
  131. data/test/support/ssl/gen_certs.sh +0 -31
  132. data/test/support/ssl/trusted-ca.crt +0 -25
  133. data/test/support/ssl/trusted-ca.key +0 -27
  134. data/test/support/ssl/trusted-cert.crt +0 -81
  135. data/test/support/ssl/trusted-cert.key +0 -28
  136. data/test/support/ssl/untrusted-ca.crt +0 -26
  137. data/test/support/ssl/untrusted-ca.key +0 -27
  138. data/test/support/ssl/untrusted-cert.crt +0 -82
  139. data/test/support/ssl/untrusted-cert.key +0 -28
  140. data/test/support/wire/synchrony.rb +0 -24
  141. data/test/support/wire/thread.rb +0 -5
  142. data/test/synchrony_driver.rb +0 -88
  143. data/test/test.conf.erb +0 -9
  144. data/test/thread_safety_test.rb +0 -62
  145. data/test/transactions_test.rb +0 -264
  146. data/test/unknown_commands_test.rb +0 -14
  147. 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
 
@@ -22,10 +24,14 @@ class Redis
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,9 @@ 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
+ @ring.add_node Redis.new(options)
39
45
  end
40
46
 
41
47
  # Change the selected database for the current connection.
@@ -109,13 +115,13 @@ class Redis
109
115
  end
110
116
 
111
117
  # Set a key's time to live in seconds.
112
- def expire(key, seconds)
113
- node_for(key).expire(key, seconds)
118
+ def expire(key, seconds, **kwargs)
119
+ node_for(key).expire(key, seconds, **kwargs)
114
120
  end
115
121
 
116
122
  # Set the expiration for a key as a UNIX timestamp.
117
- def expireat(key, unix_time)
118
- node_for(key).expireat(key, unix_time)
123
+ def expireat(key, unix_time, **kwargs)
124
+ node_for(key).expireat(key, unix_time, **kwargs)
119
125
  end
120
126
 
121
127
  # Get the time to live (in seconds) for a key.
@@ -124,13 +130,13 @@ class Redis
124
130
  end
125
131
 
126
132
  # Set a key's time to live in milliseconds.
127
- def pexpire(key, milliseconds)
128
- node_for(key).pexpire(key, milliseconds)
133
+ def pexpire(key, milliseconds, **kwarg)
134
+ node_for(key).pexpire(key, milliseconds, **kwarg)
129
135
  end
130
136
 
131
137
  # 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)
138
+ def pexpireat(key, ms_unix_time, **kwarg)
139
+ node_for(key).pexpireat(key, ms_unix_time, **kwarg)
134
140
  end
135
141
 
136
142
  # Get the time to live (in milliseconds) for a key.
@@ -144,12 +150,12 @@ class Redis
144
150
  end
145
151
 
146
152
  # 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)
153
+ def restore(key, ttl, serialized_value, **options)
154
+ node_for(key).restore(key, ttl, serialized_value, **options)
149
155
  end
150
156
 
151
157
  # Transfer a key from the connected instance to another instance.
152
- def migrate(key, options)
158
+ def migrate(_key, _options)
153
159
  raise CannotDistribute, :migrate
154
160
  end
155
161
 
@@ -161,9 +167,38 @@ class Redis
161
167
  end
162
168
  end
163
169
 
170
+ # Unlink keys.
171
+ def unlink(*args)
172
+ keys_per_node = args.group_by { |key| node_for(key) }
173
+ keys_per_node.inject(0) do |sum, (node, keys)|
174
+ sum + node.unlink(*keys)
175
+ end
176
+ end
177
+
164
178
  # Determine if a key exists.
165
- def exists(key)
166
- node_for(key).exists(key)
179
+ def exists(*args)
180
+ if !Redis.exists_returns_integer && args.size == 1
181
+ ::Redis.deprecate!(
182
+ "`Redis#exists(key)` will return an Integer in redis-rb 4.3, if you want to keep the old behavior, " \
183
+ "use `exists?` instead. To opt-in to the new behavior now you can set Redis.exists_returns_integer = true. " \
184
+ "(#{::Kernel.caller(1, 1).first})\n"
185
+ )
186
+ exists?(*args)
187
+ else
188
+ keys_per_node = args.group_by { |key| node_for(key) }
189
+ keys_per_node.inject(0) do |sum, (node, keys)|
190
+ sum + node._exists(*keys)
191
+ end
192
+ end
193
+ end
194
+
195
+ # Determine if any of the keys exists.
196
+ def exists?(*args)
197
+ keys_per_node = args.group_by { |key| node_for(key) }
198
+ keys_per_node.each do |node, keys|
199
+ return true if node.exists?(*keys)
200
+ end
201
+ false
167
202
  end
168
203
 
169
204
  # Find all keys matching the given pattern.
@@ -176,6 +211,13 @@ class Redis
176
211
  node_for(key).move(key, db)
177
212
  end
178
213
 
214
+ # Copy a value from one key to another.
215
+ def copy(source, destination, **options)
216
+ ensure_same_node(:copy, [source, destination]) do |node|
217
+ node.copy(source, destination, **options)
218
+ end
219
+ end
220
+
179
221
  # Return a random key from the keyspace.
180
222
  def randomkey
181
223
  raise CannotDistribute, :randomkey
@@ -196,11 +238,11 @@ class Redis
196
238
  end
197
239
 
198
240
  # Sort the elements in a list, set or sorted set.
199
- def sort(key, options = {})
241
+ def sort(key, **options)
200
242
  keys = [key, options[:by], options[:store], *Array(options[:get])].compact
201
243
 
202
244
  ensure_same_node(:sort, keys) do |node|
203
- node.sort(key, options)
245
+ node.sort(key, **options)
204
246
  end
205
247
  end
206
248
 
@@ -235,8 +277,8 @@ class Redis
235
277
  end
236
278
 
237
279
  # Set the string value of a key.
238
- def set(key, value, options = {})
239
- node_for(key).set(key, value, options)
280
+ def set(key, value, **options)
281
+ node_for(key).set(key, value, **options)
240
282
  end
241
283
 
242
284
  # Set the time to live in seconds of a key.
@@ -255,20 +297,20 @@ class Redis
255
297
  end
256
298
 
257
299
  # Set multiple keys to multiple values.
258
- def mset(*args)
300
+ def mset(*_args)
259
301
  raise CannotDistribute, :mset
260
302
  end
261
303
 
262
- def mapped_mset(hash)
304
+ def mapped_mset(_hash)
263
305
  raise CannotDistribute, :mapped_mset
264
306
  end
265
307
 
266
308
  # Set multiple keys to multiple values, only if none of the keys exist.
267
- def msetnx(*args)
309
+ def msetnx(*_args)
268
310
  raise CannotDistribute, :msetnx
269
311
  end
270
312
 
271
- def mapped_msetnx(hash)
313
+ def mapped_msetnx(_hash)
272
314
  raise CannotDistribute, :mapped_msetnx
273
315
  end
274
316
 
@@ -277,13 +319,26 @@ class Redis
277
319
  node_for(key).get(key)
278
320
  end
279
321
 
280
- # Get the values of all the given keys.
322
+ # Get the value of a key and delete it.
323
+ def getdel(key)
324
+ node_for(key).getdel(key)
325
+ end
326
+
327
+ # Get the value of a key and sets its time to live based on options.
328
+ def getex(key, **options)
329
+ node_for(key).getex(key, **options)
330
+ end
331
+
332
+ # Get the values of all the given keys as an Array.
281
333
  def mget(*keys)
282
- raise CannotDistribute, :mget
334
+ mapped_mget(*keys).values_at(*keys)
283
335
  end
284
336
 
337
+ # Get the values of all the given keys as a Hash.
285
338
  def mapped_mget(*keys)
286
- raise CannotDistribute, :mapped_mget
339
+ keys.group_by { |k| node_for k }.inject({}) do |results, (node, subkeys)|
340
+ results.merge! node.mapped_mget(*subkeys)
341
+ end
287
342
  end
288
343
 
289
344
  # Overwrite part of a string at key starting at the specified offset.
@@ -324,7 +379,7 @@ class Redis
324
379
  end
325
380
 
326
381
  # Return the position of the first bit set to 1 or 0 in a string.
327
- def bitpos(key, bit, start=nil, stop=nil)
382
+ def bitpos(key, bit, start = nil, stop = nil)
328
383
  node_for(key).bitpos(key, bit, start, stop)
329
384
  end
330
385
 
@@ -342,7 +397,7 @@ class Redis
342
397
  get(key)
343
398
  end
344
399
 
345
- def []=(key,value)
400
+ def []=(key, value)
346
401
  set(key, value)
347
402
  end
348
403
 
@@ -351,6 +406,21 @@ class Redis
351
406
  node_for(key).llen(key)
352
407
  end
353
408
 
409
+ # Remove the first/last element in a list, append/prepend it to another list and return it.
410
+ def lmove(source, destination, where_source, where_destination)
411
+ ensure_same_node(:lmove, [source, destination]) do |node|
412
+ node.lmove(source, destination, where_source, where_destination)
413
+ end
414
+ end
415
+
416
+ # Remove the first/last element in a list and append/prepend it
417
+ # to another list and return it, or block until one is available.
418
+ def blmove(source, destination, where_source, where_destination, timeout: 0)
419
+ ensure_same_node(:lmove, [source, destination]) do |node|
420
+ node.blmove(source, destination, where_source, where_destination, timeout: timeout)
421
+ end
422
+ end
423
+
354
424
  # Prepend one or more values to a list.
355
425
  def lpush(key, value)
356
426
  node_for(key).lpush(key, value)
@@ -371,14 +441,14 @@ class Redis
371
441
  node_for(key).rpushx(key, value)
372
442
  end
373
443
 
374
- # Remove and get the first element in a list.
375
- def lpop(key)
376
- node_for(key).lpop(key)
444
+ # Remove and get the first elements in a list.
445
+ def lpop(key, count = nil)
446
+ node_for(key).lpop(key, count)
377
447
  end
378
448
 
379
- # Remove and get the last element in a list.
380
- def rpop(key)
381
- node_for(key).rpop(key)
449
+ # Remove and get the last elements in a list.
450
+ def rpop(key, count = nil)
451
+ node_for(key).rpop(key, count)
382
452
  end
383
453
 
384
454
  # Remove the last element in a list, append it to another list and return
@@ -390,24 +460,27 @@ class Redis
390
460
  end
391
461
 
392
462
  def _bpop(cmd, args)
393
- options = {}
394
-
395
- case args.last
396
- when Hash
463
+ timeout = if args.last.is_a?(Hash)
397
464
  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...
465
+ options[:timeout]
466
+ elsif args.last.respond_to?(:to_int)
467
+ last_arg = args.pop
468
+ ::Redis.deprecate!(
469
+ "Passing the timeout as a positional argument is deprecated, it should be passed as a keyword argument:\n" \
470
+ " redis.#{cmd}(#{args.map(&:inspect).join(', ')}, timeout: #{last_arg.to_int})" \
471
+ "(called from: #{caller(2, 1).first})"
472
+ )
473
+ last_arg.to_int
405
474
  end
406
475
 
407
476
  keys = args.flatten
408
477
 
409
478
  ensure_same_node(cmd, keys) do |node|
410
- node.__send__(cmd, keys, options)
479
+ if timeout
480
+ node.__send__(cmd, keys, timeout: timeout)
481
+ else
482
+ node.__send__(cmd, keys)
483
+ end
411
484
  end
412
485
  end
413
486
 
@@ -425,15 +498,9 @@ class Redis
425
498
 
426
499
  # Pop a value from a list, push it to another list and return it; or block
427
500
  # 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
-
501
+ def brpoplpush(source, destination, deprecated_timeout = 0, **options)
435
502
  ensure_same_node(:brpoplpush, [source, destination]) do |node|
436
- node.brpoplpush(source, destination, options)
503
+ node.brpoplpush(source, destination, deprecated_timeout, **options)
437
504
  end
438
505
  end
439
506
 
@@ -477,11 +544,21 @@ class Redis
477
544
  node_for(key).sadd(key, member)
478
545
  end
479
546
 
547
+ # Add one or more members to a set.
548
+ def sadd?(key, member)
549
+ node_for(key).sadd?(key, member)
550
+ end
551
+
480
552
  # Remove one or more members from a set.
481
553
  def srem(key, member)
482
554
  node_for(key).srem(key, member)
483
555
  end
484
556
 
557
+ # Remove one or more members from a set.
558
+ def srem?(key, member)
559
+ node_for(key).srem?(key, member)
560
+ end
561
+
485
562
  # Remove and return a random member from a set.
486
563
  def spop(key, count = nil)
487
564
  node_for(key).spop(key, count)
@@ -504,11 +581,26 @@ class Redis
504
581
  node_for(key).sismember(key, member)
505
582
  end
506
583
 
584
+ # Determine if multiple values are members of a set.
585
+ def smismember(key, *members)
586
+ node_for(key).smismember(key, *members)
587
+ end
588
+
507
589
  # Get all the members in a set.
508
590
  def smembers(key)
509
591
  node_for(key).smembers(key)
510
592
  end
511
593
 
594
+ # Scan a set
595
+ def sscan(key, cursor, **options)
596
+ node_for(key).sscan(key, cursor, **options)
597
+ end
598
+
599
+ # Scan a set and return an enumerator
600
+ def sscan_each(key, **options, &block)
601
+ node_for(key).sscan_each(key, **options, &block)
602
+ end
603
+
512
604
  # Subtract multiple sets.
513
605
  def sdiff(*keys)
514
606
  ensure_same_node(:sdiff, keys) do |node|
@@ -561,6 +653,7 @@ class Redis
561
653
  def zadd(key, *args)
562
654
  node_for(key).zadd(key, *args)
563
655
  end
656
+ ruby2_keywords(:zadd) if respond_to?(:ruby2_keywords, true)
564
657
 
565
658
  # Increment the score of a member in a sorted set.
566
659
  def zincrby(key, increment, member)
@@ -577,15 +670,33 @@ class Redis
577
670
  node_for(key).zscore(key, member)
578
671
  end
579
672
 
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)
673
+ # Get one or more random members from a sorted set.
674
+ def zrandmember(key, count = nil, **options)
675
+ node_for(key).zrandmember(key, count, **options)
676
+ end
677
+
678
+ # Get the scores associated with the given members in a sorted set.
679
+ def zmscore(key, *members)
680
+ node_for(key).zmscore(key, *members)
681
+ end
682
+
683
+ # Return a range of members in a sorted set, by index, score or lexicographical ordering.
684
+ def zrange(key, start, stop, **options)
685
+ node_for(key).zrange(key, start, stop, **options)
686
+ end
687
+
688
+ # Select a range of members in a sorted set, by index, score or lexicographical ordering
689
+ # and store the resulting sorted set in a new key.
690
+ def zrangestore(dest_key, src_key, start, stop, **options)
691
+ ensure_same_node(:zrangestore, [dest_key, src_key]) do |node|
692
+ node.zrangestore(dest_key, src_key, start, stop, **options)
693
+ end
583
694
  end
584
695
 
585
696
  # Return a range of members in a sorted set, by index, with scores ordered
586
697
  # from high to low.
587
- def zrevrange(key, start, stop, options = {})
588
- node_for(key).zrevrange(key, start, stop, options)
698
+ def zrevrange(key, start, stop, **options)
699
+ node_for(key).zrevrange(key, start, stop, **options)
589
700
  end
590
701
 
591
702
  # Determine the index of a member in a sorted set.
@@ -605,14 +716,14 @@ class Redis
605
716
  end
606
717
 
607
718
  # 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)
719
+ def zrangebyscore(key, min, max, **options)
720
+ node_for(key).zrangebyscore(key, min, max, **options)
610
721
  end
611
722
 
612
723
  # Return a range of members in a sorted set, by score, with scores ordered
613
724
  # from high to low.
614
- def zrevrangebyscore(key, max, min, options = {})
615
- node_for(key).zrevrangebyscore(key, max, min, options)
725
+ def zrevrangebyscore(key, max, min, **options)
726
+ node_for(key).zrevrangebyscore(key, max, min, **options)
616
727
  end
617
728
 
618
729
  # Remove all members in a sorted set within the given scores.
@@ -625,18 +736,47 @@ class Redis
625
736
  node_for(key).zcount(key, min, max)
626
737
  end
627
738
 
739
+ # Get the intersection of multiple sorted sets
740
+ def zinter(*keys, **options)
741
+ ensure_same_node(:zinter, keys) do |node|
742
+ node.zinter(*keys, **options)
743
+ end
744
+ end
745
+
628
746
  # Intersect multiple sorted sets and store the resulting sorted set in a new
629
747
  # key.
630
- def zinterstore(destination, keys, options = {})
748
+ def zinterstore(destination, keys, **options)
631
749
  ensure_same_node(:zinterstore, [destination] + keys) do |node|
632
- node.zinterstore(destination, keys, options)
750
+ node.zinterstore(destination, keys, **options)
751
+ end
752
+ end
753
+
754
+ # Return the union of multiple sorted sets.
755
+ def zunion(*keys, **options)
756
+ ensure_same_node(:zunion, keys) do |node|
757
+ node.zunion(*keys, **options)
633
758
  end
634
759
  end
635
760
 
636
761
  # Add multiple sorted sets and store the resulting sorted set in a new key.
637
- def zunionstore(destination, keys, options = {})
762
+ def zunionstore(destination, keys, **options)
638
763
  ensure_same_node(:zunionstore, [destination] + keys) do |node|
639
- node.zunionstore(destination, keys, options)
764
+ node.zunionstore(destination, keys, **options)
765
+ end
766
+ end
767
+
768
+ # Return the difference between the first and all successive input sorted sets.
769
+ def zdiff(*keys, **options)
770
+ ensure_same_node(:zdiff, keys) do |node|
771
+ node.zdiff(*keys, **options)
772
+ end
773
+ end
774
+
775
+ # Compute the difference between the first and all successive input sorted sets
776
+ # and store the resulting sorted set in a new key.
777
+ def zdiffstore(destination, keys, **options)
778
+ ensure_same_node(:zdiffstore, [destination] + keys) do |node|
779
+ node.zdiffstore(destination, keys, **options)
640
780
  end
641
781
  end
642
782
 
@@ -645,9 +785,9 @@ class Redis
645
785
  node_for(key).hlen(key)
646
786
  end
647
787
 
648
- # Set the string value of a hash field.
649
- def hset(key, field, value)
650
- node_for(key).hset(key, field, value)
788
+ # Set multiple hash fields to multiple values.
789
+ def hset(key, *attrs)
790
+ node_for(key).hset(key, *attrs)
651
791
  end
652
792
 
653
793
  # Set the value of a hash field, only if the field does not exist.
@@ -678,9 +818,13 @@ class Redis
678
818
  Hash[*fields.zip(hmget(key, *fields)).flatten]
679
819
  end
680
820
 
821
+ def hrandfield(key, count = nil, **options)
822
+ node_for(key).hrandfield(key, count, **options)
823
+ end
824
+
681
825
  # Delete one or more hash fields.
682
- def hdel(key, field)
683
- node_for(key).hdel(key, field)
826
+ def hdel(key, *fields)
827
+ node_for(key).hdel(key, *fields)
684
828
  end
685
829
 
686
830
  # Determine if a hash field exists.
@@ -719,7 +863,7 @@ class Redis
719
863
  end
720
864
 
721
865
  def subscribed?
722
- !! @subscribed_node
866
+ !!@subscribed_node
723
867
  end
724
868
 
725
869
  # Listen for messages published to the given channels.
@@ -737,7 +881,8 @@ class Redis
737
881
 
738
882
  # Stop listening for messages posted to the given channels.
739
883
  def unsubscribe(*channels)
740
- raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
884
+ raise "Can't unsubscribe if not subscribed." unless subscribed?
885
+
741
886
  @subscribed_node.unsubscribe(*channels)
742
887
  end
743
888
 
@@ -753,13 +898,26 @@ class Redis
753
898
  end
754
899
 
755
900
  # Watch the given keys to determine execution of the MULTI/EXEC block.
756
- def watch(*keys)
757
- raise CannotDistribute, :watch
901
+ def watch(*keys, &block)
902
+ ensure_same_node(:watch, keys) do |node|
903
+ @watch_key = key_tag(keys.first) || keys.first.to_s
904
+
905
+ begin
906
+ node.watch(*keys, &block)
907
+ rescue StandardError
908
+ @watch_key = nil
909
+ raise
910
+ end
911
+ end
758
912
  end
759
913
 
760
914
  # Forget about all watched keys.
761
915
  def unwatch
762
- raise CannotDistribute, :unwatch
916
+ raise CannotDistribute, :unwatch unless @watch_key
917
+
918
+ result = node_for(@watch_key).unwatch
919
+ @watch_key = nil
920
+ result
763
921
  end
764
922
 
765
923
  def pipelined
@@ -767,18 +925,30 @@ class Redis
767
925
  end
768
926
 
769
927
  # Mark the start of a transaction block.
770
- def multi
771
- raise CannotDistribute, :multi
928
+ def multi(&block)
929
+ raise CannotDistribute, :multi unless @watch_key
930
+
931
+ result = node_for(@watch_key).multi(&block)
932
+ @watch_key = nil if block_given?
933
+ result
772
934
  end
773
935
 
774
936
  # Execute all commands issued after MULTI.
775
937
  def exec
776
- raise CannotDistribute, :exec
938
+ raise CannotDistribute, :exec unless @watch_key
939
+
940
+ result = node_for(@watch_key).exec
941
+ @watch_key = nil
942
+ result
777
943
  end
778
944
 
779
945
  # Discard all commands issued after MULTI.
780
946
  def discard
781
- raise CannotDistribute, :discard
947
+ raise CannotDistribute, :discard unless @watch_key
948
+
949
+ result = node_for(@watch_key).discard
950
+ @watch_key = nil
951
+ result
782
952
  end
783
953
 
784
954
  # Control remote script registry.
@@ -837,7 +1007,7 @@ class Redis
837
1007
  self.class.new(@node_configs, @default_options)
838
1008
  end
839
1009
 
840
- protected
1010
+ protected
841
1011
 
842
1012
  def on_each_node(command, *args)
843
1013
  nodes.map do |node|
data/lib/redis/errors.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Redis
2
4
  # Base error for all redis-rb errors.
3
5
  class BaseError < RuntimeError
@@ -37,4 +39,59 @@ class Redis
37
39
  # Raised when the connection was inherited by a child process.
38
40
  class InheritedError < BaseConnectionError
39
41
  end
42
+
43
+ # Raised when client options are invalid.
44
+ class InvalidClientOptionError < BaseError
45
+ end
46
+
47
+ class Cluster
48
+ # Raised when client connected to redis as cluster mode
49
+ # and failed to fetch cluster state information by commands.
50
+ class InitialSetupError < BaseError
51
+ # @param errors [Array<Redis::BaseError>]
52
+ def initialize(errors)
53
+ super("Redis client could not fetch cluster information: #{errors.map(&:message).uniq.join(',')}")
54
+ end
55
+ end
56
+
57
+ # Raised when client connected to redis as cluster mode
58
+ # and some cluster subcommands were called.
59
+ class OrchestrationCommandNotSupported < BaseError
60
+ def initialize(command, subcommand = '')
61
+ str = [command, subcommand].map(&:to_s).reject(&:empty?).join(' ').upcase
62
+ msg = "#{str} command should be used with care "\
63
+ 'only by applications orchestrating Redis Cluster, like redis-trib, '\
64
+ 'and the command if used out of the right context can leave the cluster '\
65
+ 'in a wrong state or cause data loss.'
66
+ super(msg)
67
+ end
68
+ end
69
+
70
+ # Raised when error occurs on any node of cluster.
71
+ class CommandErrorCollection < BaseError
72
+ attr_reader :errors
73
+
74
+ # @param errors [Hash{String => Redis::CommandError}]
75
+ # @param error_message [String]
76
+ def initialize(errors, error_message = 'Command errors were replied on any node')
77
+ @errors = errors
78
+ super(error_message)
79
+ end
80
+ end
81
+
82
+ # Raised when cluster client can't select node.
83
+ class AmbiguousNodeError < BaseError
84
+ def initialize(command)
85
+ super("Cluster client doesn't know which node the #{command} command should be sent to.")
86
+ end
87
+ end
88
+
89
+ # Raised when commands in pipelining include cross slot keys.
90
+ class CrossSlotPipeliningError < BaseError
91
+ def initialize(keys)
92
+ super("Cluster client couldn't send pipelining to single node. "\
93
+ "The commands include cross slot keys. #{keys}")
94
+ end
95
+ end
96
+ end
40
97
  end