redis 4.0.1 → 4.8.1

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 (148) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +220 -0
  3. data/README.md +152 -28
  4. data/lib/redis/client.rb +171 -107
  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 +5 -2
  33. data/lib/redis/connection/hiredis.rb +7 -5
  34. data/lib/redis/connection/registry.rb +2 -1
  35. data/lib/redis/connection/ruby.rb +139 -111
  36. data/lib/redis/connection/synchrony.rb +17 -10
  37. data/lib/redis/connection.rb +3 -1
  38. data/lib/redis/distributed.rb +244 -87
  39. data/lib/redis/errors.rb +57 -0
  40. data/lib/redis/hash_ring.rb +15 -14
  41. data/lib/redis/pipeline.rb +181 -10
  42. data/lib/redis/subscribe.rb +11 -12
  43. data/lib/redis/version.rb +3 -1
  44. data/lib/redis.rb +180 -2716
  45. metadata +45 -195
  46. data/.gitignore +0 -16
  47. data/.travis/Gemfile +0 -13
  48. data/.travis.yml +0 -73
  49. data/.yardopts +0 -3
  50. data/Gemfile +0 -3
  51. data/benchmarking/logging.rb +0 -71
  52. data/benchmarking/pipeline.rb +0 -51
  53. data/benchmarking/speed.rb +0 -21
  54. data/benchmarking/suite.rb +0 -24
  55. data/benchmarking/worker.rb +0 -71
  56. data/bors.toml +0 -14
  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/makefile +0 -42
  70. data/redis.gemspec +0 -42
  71. data/test/bitpos_test.rb +0 -63
  72. data/test/blocking_commands_test.rb +0 -40
  73. data/test/client_test.rb +0 -59
  74. data/test/command_map_test.rb +0 -28
  75. data/test/commands_on_hashes_test.rb +0 -19
  76. data/test/commands_on_hyper_log_log_test.rb +0 -19
  77. data/test/commands_on_lists_test.rb +0 -18
  78. data/test/commands_on_sets_test.rb +0 -75
  79. data/test/commands_on_sorted_sets_test.rb +0 -150
  80. data/test/commands_on_strings_test.rb +0 -99
  81. data/test/commands_on_value_types_test.rb +0 -171
  82. data/test/connection_handling_test.rb +0 -275
  83. data/test/connection_test.rb +0 -57
  84. data/test/db/.gitkeep +0 -0
  85. data/test/distributed_blocking_commands_test.rb +0 -44
  86. data/test/distributed_commands_on_hashes_test.rb +0 -8
  87. data/test/distributed_commands_on_hyper_log_log_test.rb +0 -31
  88. data/test/distributed_commands_on_lists_test.rb +0 -20
  89. data/test/distributed_commands_on_sets_test.rb +0 -106
  90. data/test/distributed_commands_on_sorted_sets_test.rb +0 -16
  91. data/test/distributed_commands_on_strings_test.rb +0 -69
  92. data/test/distributed_commands_on_value_types_test.rb +0 -93
  93. data/test/distributed_commands_requiring_clustering_test.rb +0 -162
  94. data/test/distributed_connection_handling_test.rb +0 -21
  95. data/test/distributed_internals_test.rb +0 -68
  96. data/test/distributed_key_tags_test.rb +0 -50
  97. data/test/distributed_persistence_control_commands_test.rb +0 -24
  98. data/test/distributed_publish_subscribe_test.rb +0 -90
  99. data/test/distributed_remote_server_control_commands_test.rb +0 -64
  100. data/test/distributed_scripting_test.rb +0 -100
  101. data/test/distributed_sorting_test.rb +0 -18
  102. data/test/distributed_test.rb +0 -56
  103. data/test/distributed_transactions_test.rb +0 -30
  104. data/test/encoding_test.rb +0 -14
  105. data/test/error_replies_test.rb +0 -57
  106. data/test/fork_safety_test.rb +0 -60
  107. data/test/helper.rb +0 -201
  108. data/test/helper_test.rb +0 -22
  109. data/test/internals_test.rb +0 -389
  110. data/test/lint/blocking_commands.rb +0 -150
  111. data/test/lint/hashes.rb +0 -162
  112. data/test/lint/hyper_log_log.rb +0 -60
  113. data/test/lint/lists.rb +0 -143
  114. data/test/lint/sets.rb +0 -140
  115. data/test/lint/sorted_sets.rb +0 -316
  116. data/test/lint/strings.rb +0 -246
  117. data/test/lint/value_types.rb +0 -130
  118. data/test/persistence_control_commands_test.rb +0 -24
  119. data/test/pipelining_commands_test.rb +0 -238
  120. data/test/publish_subscribe_test.rb +0 -280
  121. data/test/remote_server_control_commands_test.rb +0 -175
  122. data/test/scanning_test.rb +0 -407
  123. data/test/scripting_test.rb +0 -76
  124. data/test/sentinel_command_test.rb +0 -78
  125. data/test/sentinel_test.rb +0 -253
  126. data/test/sorting_test.rb +0 -57
  127. data/test/ssl_test.rb +0 -69
  128. data/test/support/connection/hiredis.rb +0 -1
  129. data/test/support/connection/ruby.rb +0 -1
  130. data/test/support/connection/synchrony.rb +0 -17
  131. data/test/support/redis_mock.rb +0 -130
  132. data/test/support/ssl/gen_certs.sh +0 -31
  133. data/test/support/ssl/trusted-ca.crt +0 -25
  134. data/test/support/ssl/trusted-ca.key +0 -27
  135. data/test/support/ssl/trusted-cert.crt +0 -81
  136. data/test/support/ssl/trusted-cert.key +0 -28
  137. data/test/support/ssl/untrusted-ca.crt +0 -26
  138. data/test/support/ssl/untrusted-ca.key +0 -27
  139. data/test/support/ssl/untrusted-cert.crt +0 -82
  140. data/test/support/ssl/untrusted-cert.key +0 -28
  141. data/test/support/wire/synchrony.rb +0 -24
  142. data/test/support/wire/thread.rb +0 -5
  143. data/test/synchrony_driver.rb +0 -85
  144. data/test/test.conf.erb +0 -9
  145. data/test/thread_safety_test.rb +0 -60
  146. data/test/transactions_test.rb +0 -262
  147. data/test/unknown_commands_test.rb +0 -12
  148. data/test/url_param_test.rb +0 -136
@@ -1,15 +1,17 @@
1
- require_relative "hash_ring"
1
+ # frozen_string_literal: true
2
+
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, options = {})
148
- node_for(key).restore(key, ttl, serialized_value, options)
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,6 +319,16 @@ class Redis
277
319
  node_for(key).get(key)
278
320
  end
279
321
 
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
+
280
332
  # Get the values of all the given keys as an Array.
281
333
  def mget(*keys)
282
334
  mapped_mget(*keys).values_at(*keys)
@@ -327,7 +379,7 @@ class Redis
327
379
  end
328
380
 
329
381
  # Return the position of the first bit set to 1 or 0 in a string.
330
- def bitpos(key, bit, start=nil, stop=nil)
382
+ def bitpos(key, bit, start = nil, stop = nil)
331
383
  node_for(key).bitpos(key, bit, start, stop)
332
384
  end
333
385
 
@@ -345,7 +397,7 @@ class Redis
345
397
  get(key)
346
398
  end
347
399
 
348
- def []=(key,value)
400
+ def []=(key, value)
349
401
  set(key, value)
350
402
  end
351
403
 
@@ -354,6 +406,21 @@ class Redis
354
406
  node_for(key).llen(key)
355
407
  end
356
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
+
357
424
  # Prepend one or more values to a list.
358
425
  def lpush(key, value)
359
426
  node_for(key).lpush(key, value)
@@ -374,14 +441,14 @@ class Redis
374
441
  node_for(key).rpushx(key, value)
375
442
  end
376
443
 
377
- # Remove and get the first element in a list.
378
- def lpop(key)
379
- 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)
380
447
  end
381
448
 
382
- # Remove and get the last element in a list.
383
- def rpop(key)
384
- 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)
385
452
  end
386
453
 
387
454
  # Remove the last element in a list, append it to another list and return
@@ -393,24 +460,27 @@ class Redis
393
460
  end
394
461
 
395
462
  def _bpop(cmd, args)
396
- options = {}
397
-
398
- case args.last
399
- when Hash
463
+ timeout = if args.last.is_a?(Hash)
400
464
  options = args.pop
401
- when Integer
402
- # Issue deprecation notice in obnoxious mode...
403
- options[:timeout] = args.pop
404
- end
405
-
406
- if args.size > 1
407
- # 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
408
474
  end
409
475
 
410
476
  keys = args.flatten
411
477
 
412
478
  ensure_same_node(cmd, keys) do |node|
413
- node.__send__(cmd, keys, options)
479
+ if timeout
480
+ node.__send__(cmd, keys, timeout: timeout)
481
+ else
482
+ node.__send__(cmd, keys)
483
+ end
414
484
  end
415
485
  end
416
486
 
@@ -428,15 +498,9 @@ class Redis
428
498
 
429
499
  # Pop a value from a list, push it to another list and return it; or block
430
500
  # until one is available.
431
- def brpoplpush(source, destination, options = {})
432
- case options
433
- when Integer
434
- # Issue deprecation notice in obnoxious mode...
435
- options = { :timeout => options }
436
- end
437
-
501
+ def brpoplpush(source, destination, deprecated_timeout = 0, **options)
438
502
  ensure_same_node(:brpoplpush, [source, destination]) do |node|
439
- node.brpoplpush(source, destination, options)
503
+ node.brpoplpush(source, destination, deprecated_timeout, **options)
440
504
  end
441
505
  end
442
506
 
@@ -480,11 +544,21 @@ class Redis
480
544
  node_for(key).sadd(key, member)
481
545
  end
482
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
+
483
552
  # Remove one or more members from a set.
484
553
  def srem(key, member)
485
554
  node_for(key).srem(key, member)
486
555
  end
487
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
+
488
562
  # Remove and return a random member from a set.
489
563
  def spop(key, count = nil)
490
564
  node_for(key).spop(key, count)
@@ -507,19 +581,24 @@ class Redis
507
581
  node_for(key).sismember(key, member)
508
582
  end
509
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
+
510
589
  # Get all the members in a set.
511
590
  def smembers(key)
512
591
  node_for(key).smembers(key)
513
592
  end
514
593
 
515
594
  # Scan a set
516
- def sscan(key, cursor, options={})
517
- node_for(key).sscan(key, cursor, options)
595
+ def sscan(key, cursor, **options)
596
+ node_for(key).sscan(key, cursor, **options)
518
597
  end
519
598
 
520
599
  # Scan a set and return an enumerator
521
- def sscan_each(key, options={}, &block)
522
- node_for(key).sscan_each(key, options, &block)
600
+ def sscan_each(key, **options, &block)
601
+ node_for(key).sscan_each(key, **options, &block)
523
602
  end
524
603
 
525
604
  # Subtract multiple sets.
@@ -574,6 +653,7 @@ class Redis
574
653
  def zadd(key, *args)
575
654
  node_for(key).zadd(key, *args)
576
655
  end
656
+ ruby2_keywords(:zadd) if respond_to?(:ruby2_keywords, true)
577
657
 
578
658
  # Increment the score of a member in a sorted set.
579
659
  def zincrby(key, increment, member)
@@ -590,15 +670,33 @@ class Redis
590
670
  node_for(key).zscore(key, member)
591
671
  end
592
672
 
593
- # Return a range of members in a sorted set, by index.
594
- def zrange(key, start, stop, options = {})
595
- 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
596
694
  end
597
695
 
598
696
  # Return a range of members in a sorted set, by index, with scores ordered
599
697
  # from high to low.
600
- def zrevrange(key, start, stop, options = {})
601
- node_for(key).zrevrange(key, start, stop, options)
698
+ def zrevrange(key, start, stop, **options)
699
+ node_for(key).zrevrange(key, start, stop, **options)
602
700
  end
603
701
 
604
702
  # Determine the index of a member in a sorted set.
@@ -618,14 +716,14 @@ class Redis
618
716
  end
619
717
 
620
718
  # Return a range of members in a sorted set, by score.
621
- def zrangebyscore(key, min, max, options = {})
622
- node_for(key).zrangebyscore(key, min, max, options)
719
+ def zrangebyscore(key, min, max, **options)
720
+ node_for(key).zrangebyscore(key, min, max, **options)
623
721
  end
624
722
 
625
723
  # Return a range of members in a sorted set, by score, with scores ordered
626
724
  # from high to low.
627
- def zrevrangebyscore(key, max, min, options = {})
628
- node_for(key).zrevrangebyscore(key, max, min, options)
725
+ def zrevrangebyscore(key, max, min, **options)
726
+ node_for(key).zrevrangebyscore(key, max, min, **options)
629
727
  end
630
728
 
631
729
  # Remove all members in a sorted set within the given scores.
@@ -638,18 +736,47 @@ class Redis
638
736
  node_for(key).zcount(key, min, max)
639
737
  end
640
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
+
641
746
  # Intersect multiple sorted sets and store the resulting sorted set in a new
642
747
  # key.
643
- def zinterstore(destination, keys, options = {})
748
+ def zinterstore(destination, keys, **options)
644
749
  ensure_same_node(:zinterstore, [destination] + keys) do |node|
645
- 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)
646
758
  end
647
759
  end
648
760
 
649
761
  # Add multiple sorted sets and store the resulting sorted set in a new key.
650
- def zunionstore(destination, keys, options = {})
762
+ def zunionstore(destination, keys, **options)
651
763
  ensure_same_node(:zunionstore, [destination] + keys) do |node|
652
- 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)
653
780
  end
654
781
  end
655
782
 
@@ -658,9 +785,9 @@ class Redis
658
785
  node_for(key).hlen(key)
659
786
  end
660
787
 
661
- # Set the string value of a hash field.
662
- def hset(key, field, value)
663
- 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)
664
791
  end
665
792
 
666
793
  # Set the value of a hash field, only if the field does not exist.
@@ -691,9 +818,13 @@ class Redis
691
818
  Hash[*fields.zip(hmget(key, *fields)).flatten]
692
819
  end
693
820
 
821
+ def hrandfield(key, count = nil, **options)
822
+ node_for(key).hrandfield(key, count, **options)
823
+ end
824
+
694
825
  # Delete one or more hash fields.
695
- def hdel(key, field)
696
- node_for(key).hdel(key, field)
826
+ def hdel(key, *fields)
827
+ node_for(key).hdel(key, *fields)
697
828
  end
698
829
 
699
830
  # Determine if a hash field exists.
@@ -732,7 +863,7 @@ class Redis
732
863
  end
733
864
 
734
865
  def subscribed?
735
- !! @subscribed_node
866
+ !!@subscribed_node
736
867
  end
737
868
 
738
869
  # Listen for messages published to the given channels.
@@ -750,7 +881,8 @@ class Redis
750
881
 
751
882
  # Stop listening for messages posted to the given channels.
752
883
  def unsubscribe(*channels)
753
- raise RuntimeError, "Can't unsubscribe if not subscribed." unless subscribed?
884
+ raise "Can't unsubscribe if not subscribed." unless subscribed?
885
+
754
886
  @subscribed_node.unsubscribe(*channels)
755
887
  end
756
888
 
@@ -766,13 +898,26 @@ class Redis
766
898
  end
767
899
 
768
900
  # Watch the given keys to determine execution of the MULTI/EXEC block.
769
- def watch(*keys)
770
- 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
771
912
  end
772
913
 
773
914
  # Forget about all watched keys.
774
915
  def unwatch
775
- raise CannotDistribute, :unwatch
916
+ raise CannotDistribute, :unwatch unless @watch_key
917
+
918
+ result = node_for(@watch_key).unwatch
919
+ @watch_key = nil
920
+ result
776
921
  end
777
922
 
778
923
  def pipelined
@@ -780,18 +925,30 @@ class Redis
780
925
  end
781
926
 
782
927
  # Mark the start of a transaction block.
783
- def multi
784
- 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
785
934
  end
786
935
 
787
936
  # Execute all commands issued after MULTI.
788
937
  def exec
789
- raise CannotDistribute, :exec
938
+ raise CannotDistribute, :exec unless @watch_key
939
+
940
+ result = node_for(@watch_key).exec
941
+ @watch_key = nil
942
+ result
790
943
  end
791
944
 
792
945
  # Discard all commands issued after MULTI.
793
946
  def discard
794
- raise CannotDistribute, :discard
947
+ raise CannotDistribute, :discard unless @watch_key
948
+
949
+ result = node_for(@watch_key).discard
950
+ @watch_key = nil
951
+ result
795
952
  end
796
953
 
797
954
  # Control remote script registry.
@@ -850,7 +1007,7 @@ class Redis
850
1007
  self.class.new(@node_configs, @default_options)
851
1008
  end
852
1009
 
853
- protected
1010
+ protected
854
1011
 
855
1012
  def on_each_node(command, *args)
856
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