redis 3.3.5 → 4.5.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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +161 -2
- data/README.md +144 -79
- data/lib/redis/client.rb +166 -90
- data/lib/redis/cluster/command.rb +81 -0
- data/lib/redis/cluster/command_loader.rb +33 -0
- data/lib/redis/cluster/key_slot_converter.rb +72 -0
- data/lib/redis/cluster/node.rb +108 -0
- data/lib/redis/cluster/node_key.rb +31 -0
- data/lib/redis/cluster/node_loader.rb +37 -0
- data/lib/redis/cluster/option.rb +93 -0
- data/lib/redis/cluster/slot.rb +86 -0
- data/lib/redis/cluster/slot_loader.rb +49 -0
- data/lib/redis/cluster.rb +291 -0
- data/lib/redis/connection/command_helper.rb +7 -10
- data/lib/redis/connection/hiredis.rb +6 -5
- data/lib/redis/connection/registry.rb +2 -1
- data/lib/redis/connection/ruby.rb +128 -129
- data/lib/redis/connection/synchrony.rb +21 -8
- data/lib/redis/connection.rb +4 -2
- data/lib/redis/distributed.rb +194 -72
- data/lib/redis/errors.rb +48 -0
- data/lib/redis/hash_ring.rb +30 -73
- data/lib/redis/pipeline.rb +55 -15
- data/lib/redis/subscribe.rb +11 -12
- data/lib/redis/version.rb +3 -1
- data/lib/redis.rb +1451 -403
- metadata +49 -202
- data/.gitignore +0 -16
- data/.travis/Gemfile +0 -11
- data/.travis.yml +0 -89
- data/.yardopts +0 -3
- data/Gemfile +0 -4
- data/Rakefile +0 -87
- data/benchmarking/logging.rb +0 -71
- data/benchmarking/pipeline.rb +0 -51
- data/benchmarking/speed.rb +0 -21
- data/benchmarking/suite.rb +0 -24
- data/benchmarking/worker.rb +0 -71
- data/examples/basic.rb +0 -15
- data/examples/consistency.rb +0 -114
- data/examples/dist_redis.rb +0 -43
- data/examples/incr-decr.rb +0 -17
- data/examples/list.rb +0 -26
- data/examples/pubsub.rb +0 -37
- data/examples/sentinel/sentinel.conf +0 -9
- data/examples/sentinel/start +0 -49
- data/examples/sentinel.rb +0 -41
- data/examples/sets.rb +0 -36
- data/examples/unicorn/config.ru +0 -3
- data/examples/unicorn/unicorn.rb +0 -20
- data/redis.gemspec +0 -44
- data/test/bitpos_test.rb +0 -69
- data/test/blocking_commands_test.rb +0 -42
- data/test/client_test.rb +0 -59
- data/test/command_map_test.rb +0 -30
- data/test/commands_on_hashes_test.rb +0 -21
- data/test/commands_on_hyper_log_log_test.rb +0 -21
- data/test/commands_on_lists_test.rb +0 -20
- data/test/commands_on_sets_test.rb +0 -77
- data/test/commands_on_sorted_sets_test.rb +0 -137
- data/test/commands_on_strings_test.rb +0 -101
- data/test/commands_on_value_types_test.rb +0 -133
- data/test/connection_handling_test.rb +0 -277
- data/test/connection_test.rb +0 -57
- data/test/db/.gitkeep +0 -0
- data/test/distributed_blocking_commands_test.rb +0 -46
- data/test/distributed_commands_on_hashes_test.rb +0 -10
- data/test/distributed_commands_on_hyper_log_log_test.rb +0 -33
- data/test/distributed_commands_on_lists_test.rb +0 -22
- data/test/distributed_commands_on_sets_test.rb +0 -83
- data/test/distributed_commands_on_sorted_sets_test.rb +0 -18
- data/test/distributed_commands_on_strings_test.rb +0 -59
- data/test/distributed_commands_on_value_types_test.rb +0 -95
- data/test/distributed_commands_requiring_clustering_test.rb +0 -164
- data/test/distributed_connection_handling_test.rb +0 -23
- data/test/distributed_internals_test.rb +0 -79
- data/test/distributed_key_tags_test.rb +0 -52
- data/test/distributed_persistence_control_commands_test.rb +0 -26
- data/test/distributed_publish_subscribe_test.rb +0 -92
- data/test/distributed_remote_server_control_commands_test.rb +0 -66
- data/test/distributed_scripting_test.rb +0 -102
- data/test/distributed_sorting_test.rb +0 -20
- data/test/distributed_test.rb +0 -58
- data/test/distributed_transactions_test.rb +0 -32
- data/test/encoding_test.rb +0 -18
- data/test/error_replies_test.rb +0 -59
- data/test/fork_safety_test.rb +0 -65
- data/test/helper.rb +0 -232
- data/test/helper_test.rb +0 -24
- data/test/internals_test.rb +0 -417
- data/test/lint/blocking_commands.rb +0 -150
- data/test/lint/hashes.rb +0 -162
- data/test/lint/hyper_log_log.rb +0 -60
- data/test/lint/lists.rb +0 -143
- data/test/lint/sets.rb +0 -140
- data/test/lint/sorted_sets.rb +0 -316
- data/test/lint/strings.rb +0 -260
- data/test/lint/value_types.rb +0 -122
- data/test/persistence_control_commands_test.rb +0 -26
- data/test/pipelining_commands_test.rb +0 -242
- data/test/publish_subscribe_test.rb +0 -282
- data/test/remote_server_control_commands_test.rb +0 -118
- data/test/scanning_test.rb +0 -413
- data/test/scripting_test.rb +0 -78
- data/test/sentinel_command_test.rb +0 -80
- data/test/sentinel_test.rb +0 -255
- data/test/sorting_test.rb +0 -59
- data/test/ssl_test.rb +0 -73
- data/test/support/connection/hiredis.rb +0 -1
- data/test/support/connection/ruby.rb +0 -1
- data/test/support/connection/synchrony.rb +0 -17
- data/test/support/redis_mock.rb +0 -130
- data/test/support/ssl/gen_certs.sh +0 -31
- data/test/support/ssl/trusted-ca.crt +0 -25
- data/test/support/ssl/trusted-ca.key +0 -27
- data/test/support/ssl/trusted-cert.crt +0 -81
- data/test/support/ssl/trusted-cert.key +0 -28
- data/test/support/ssl/untrusted-ca.crt +0 -26
- data/test/support/ssl/untrusted-ca.key +0 -27
- data/test/support/ssl/untrusted-cert.crt +0 -82
- data/test/support/ssl/untrusted-cert.key +0 -28
- data/test/support/wire/synchrony.rb +0 -24
- data/test/support/wire/thread.rb +0 -5
- data/test/synchrony_driver.rb +0 -88
- data/test/test.conf.erb +0 -9
- data/test/thread_safety_test.rb +0 -62
- data/test/transactions_test.rb +0 -264
- data/test/unknown_commands_test.rb +0 -14
- data/test/url_param_test.rb +0 -138
data/lib/redis/distributed.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "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
|
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
|
-
|
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 = { :
|
42
|
+
options = { url: options } if options.is_a?(String)
|
37
43
|
options = @default_options.merge(options)
|
38
|
-
@ring.add_node Redis.new(
|
44
|
+
@ring.add_node Redis.new(options)
|
39
45
|
end
|
40
46
|
|
41
47
|
# Change the selected database for the current connection.
|
@@ -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(
|
158
|
+
def migrate(_key, _options)
|
153
159
|
raise CannotDistribute, :migrate
|
154
160
|
end
|
155
161
|
|
@@ -161,9 +167,42 @@ 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(
|
166
|
-
|
179
|
+
def exists(*args)
|
180
|
+
if !Redis.exists_returns_integer && args.size == 1
|
181
|
+
message = "`Redis#exists(key)` will return an Integer in redis-rb 4.3, if you want to keep the old behavior, " \
|
182
|
+
"use `exists?` instead. To opt-in to the new behavior now you can set Redis.exists_returns_integer = true. " \
|
183
|
+
"(#{::Kernel.caller(1, 1).first})\n"
|
184
|
+
|
185
|
+
if defined?(::Warning)
|
186
|
+
::Warning.warn(message)
|
187
|
+
else
|
188
|
+
warn(message)
|
189
|
+
end
|
190
|
+
exists?(*args)
|
191
|
+
else
|
192
|
+
keys_per_node = args.group_by { |key| node_for(key) }
|
193
|
+
keys_per_node.inject(0) do |sum, (node, keys)|
|
194
|
+
sum + node._exists(*keys)
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
# Determine if any of the keys exists.
|
200
|
+
def exists?(*args)
|
201
|
+
keys_per_node = args.group_by { |key| node_for(key) }
|
202
|
+
keys_per_node.each do |node, keys|
|
203
|
+
return true if node.exists?(*keys)
|
204
|
+
end
|
205
|
+
false
|
167
206
|
end
|
168
207
|
|
169
208
|
# Find all keys matching the given pattern.
|
@@ -196,11 +235,11 @@ class Redis
|
|
196
235
|
end
|
197
236
|
|
198
237
|
# Sort the elements in a list, set or sorted set.
|
199
|
-
def sort(key, options
|
238
|
+
def sort(key, **options)
|
200
239
|
keys = [key, options[:by], options[:store], *Array(options[:get])].compact
|
201
240
|
|
202
241
|
ensure_same_node(:sort, keys) do |node|
|
203
|
-
node.sort(key, options)
|
242
|
+
node.sort(key, **options)
|
204
243
|
end
|
205
244
|
end
|
206
245
|
|
@@ -235,8 +274,8 @@ class Redis
|
|
235
274
|
end
|
236
275
|
|
237
276
|
# Set the string value of a key.
|
238
|
-
def set(key, value, options
|
239
|
-
node_for(key).set(key, value, options)
|
277
|
+
def set(key, value, **options)
|
278
|
+
node_for(key).set(key, value, **options)
|
240
279
|
end
|
241
280
|
|
242
281
|
# Set the time to live in seconds of a key.
|
@@ -255,20 +294,20 @@ class Redis
|
|
255
294
|
end
|
256
295
|
|
257
296
|
# Set multiple keys to multiple values.
|
258
|
-
def mset(*
|
297
|
+
def mset(*_args)
|
259
298
|
raise CannotDistribute, :mset
|
260
299
|
end
|
261
300
|
|
262
|
-
def mapped_mset(
|
301
|
+
def mapped_mset(_hash)
|
263
302
|
raise CannotDistribute, :mapped_mset
|
264
303
|
end
|
265
304
|
|
266
305
|
# Set multiple keys to multiple values, only if none of the keys exist.
|
267
|
-
def msetnx(*
|
306
|
+
def msetnx(*_args)
|
268
307
|
raise CannotDistribute, :msetnx
|
269
308
|
end
|
270
309
|
|
271
|
-
def mapped_msetnx(
|
310
|
+
def mapped_msetnx(_hash)
|
272
311
|
raise CannotDistribute, :mapped_msetnx
|
273
312
|
end
|
274
313
|
|
@@ -277,13 +316,26 @@ class Redis
|
|
277
316
|
node_for(key).get(key)
|
278
317
|
end
|
279
318
|
|
280
|
-
# Get the
|
319
|
+
# Get the value of a key and delete it.
|
320
|
+
def getdel(key)
|
321
|
+
node_for(key).getdel(key)
|
322
|
+
end
|
323
|
+
|
324
|
+
# Get the value of a key and sets its time to live based on options.
|
325
|
+
def getex(key, **options)
|
326
|
+
node_for(key).getex(key, **options)
|
327
|
+
end
|
328
|
+
|
329
|
+
# Get the values of all the given keys as an Array.
|
281
330
|
def mget(*keys)
|
282
|
-
|
331
|
+
mapped_mget(*keys).values_at(*keys)
|
283
332
|
end
|
284
333
|
|
334
|
+
# Get the values of all the given keys as a Hash.
|
285
335
|
def mapped_mget(*keys)
|
286
|
-
|
336
|
+
keys.group_by { |k| node_for k }.inject({}) do |results, (node, subkeys)|
|
337
|
+
results.merge! node.mapped_mget(*subkeys)
|
338
|
+
end
|
287
339
|
end
|
288
340
|
|
289
341
|
# Overwrite part of a string at key starting at the specified offset.
|
@@ -324,7 +376,7 @@ class Redis
|
|
324
376
|
end
|
325
377
|
|
326
378
|
# Return the position of the first bit set to 1 or 0 in a string.
|
327
|
-
def bitpos(key, bit, start=nil, stop=nil)
|
379
|
+
def bitpos(key, bit, start = nil, stop = nil)
|
328
380
|
node_for(key).bitpos(key, bit, start, stop)
|
329
381
|
end
|
330
382
|
|
@@ -342,7 +394,7 @@ class Redis
|
|
342
394
|
get(key)
|
343
395
|
end
|
344
396
|
|
345
|
-
def []=(key,value)
|
397
|
+
def []=(key, value)
|
346
398
|
set(key, value)
|
347
399
|
end
|
348
400
|
|
@@ -351,6 +403,21 @@ class Redis
|
|
351
403
|
node_for(key).llen(key)
|
352
404
|
end
|
353
405
|
|
406
|
+
# Remove the first/last element in a list, append/prepend it to another list and return it.
|
407
|
+
def lmove(source, destination, where_source, where_destination)
|
408
|
+
ensure_same_node(:lmove, [source, destination]) do |node|
|
409
|
+
node.lmove(source, destination, where_source, where_destination)
|
410
|
+
end
|
411
|
+
end
|
412
|
+
|
413
|
+
# Remove the first/last element in a list and append/prepend it
|
414
|
+
# to another list and return it, or block until one is available.
|
415
|
+
def blmove(source, destination, where_source, where_destination, timeout: 0)
|
416
|
+
ensure_same_node(:lmove, [source, destination]) do |node|
|
417
|
+
node.blmove(source, destination, where_source, where_destination, timeout: timeout)
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
354
421
|
# Prepend one or more values to a list.
|
355
422
|
def lpush(key, value)
|
356
423
|
node_for(key).lpush(key, value)
|
@@ -371,14 +438,14 @@ class Redis
|
|
371
438
|
node_for(key).rpushx(key, value)
|
372
439
|
end
|
373
440
|
|
374
|
-
# Remove and get the first
|
375
|
-
def lpop(key)
|
376
|
-
node_for(key).lpop(key)
|
441
|
+
# Remove and get the first elements in a list.
|
442
|
+
def lpop(key, count = nil)
|
443
|
+
node_for(key).lpop(key, count)
|
377
444
|
end
|
378
445
|
|
379
|
-
# Remove and get the last
|
380
|
-
def rpop(key)
|
381
|
-
node_for(key).rpop(key)
|
446
|
+
# Remove and get the last elements in a list.
|
447
|
+
def rpop(key, count = nil)
|
448
|
+
node_for(key).rpop(key, count)
|
382
449
|
end
|
383
450
|
|
384
451
|
# Remove the last element in a list, append it to another list and return
|
@@ -390,14 +457,12 @@ class Redis
|
|
390
457
|
end
|
391
458
|
|
392
459
|
def _bpop(cmd, args)
|
393
|
-
|
394
|
-
|
395
|
-
case args.last
|
396
|
-
when Hash
|
460
|
+
timeout = if args.last.is_a?(Hash)
|
397
461
|
options = args.pop
|
398
|
-
|
462
|
+
options[:timeout]
|
463
|
+
elsif args.last.respond_to?(:to_int)
|
399
464
|
# Issue deprecation notice in obnoxious mode...
|
400
|
-
|
465
|
+
args.pop.to_int
|
401
466
|
end
|
402
467
|
|
403
468
|
if args.size > 1
|
@@ -407,7 +472,11 @@ class Redis
|
|
407
472
|
keys = args.flatten
|
408
473
|
|
409
474
|
ensure_same_node(cmd, keys) do |node|
|
410
|
-
|
475
|
+
if timeout
|
476
|
+
node.__send__(cmd, keys, timeout: timeout)
|
477
|
+
else
|
478
|
+
node.__send__(cmd, keys)
|
479
|
+
end
|
411
480
|
end
|
412
481
|
end
|
413
482
|
|
@@ -425,15 +494,9 @@ class Redis
|
|
425
494
|
|
426
495
|
# Pop a value from a list, push it to another list and return it; or block
|
427
496
|
# until one is available.
|
428
|
-
def brpoplpush(source, destination,
|
429
|
-
case options
|
430
|
-
when Integer
|
431
|
-
# Issue deprecation notice in obnoxious mode...
|
432
|
-
options = { :timeout => options }
|
433
|
-
end
|
434
|
-
|
497
|
+
def brpoplpush(source, destination, deprecated_timeout = 0, **options)
|
435
498
|
ensure_same_node(:brpoplpush, [source, destination]) do |node|
|
436
|
-
node.brpoplpush(source, destination, options)
|
499
|
+
node.brpoplpush(source, destination, deprecated_timeout, **options)
|
437
500
|
end
|
438
501
|
end
|
439
502
|
|
@@ -504,11 +567,26 @@ class Redis
|
|
504
567
|
node_for(key).sismember(key, member)
|
505
568
|
end
|
506
569
|
|
570
|
+
# Determine if multiple values are members of a set.
|
571
|
+
def smismember(key, *members)
|
572
|
+
node_for(key).smismember(key, *members)
|
573
|
+
end
|
574
|
+
|
507
575
|
# Get all the members in a set.
|
508
576
|
def smembers(key)
|
509
577
|
node_for(key).smembers(key)
|
510
578
|
end
|
511
579
|
|
580
|
+
# Scan a set
|
581
|
+
def sscan(key, cursor, **options)
|
582
|
+
node_for(key).sscan(key, cursor, **options)
|
583
|
+
end
|
584
|
+
|
585
|
+
# Scan a set and return an enumerator
|
586
|
+
def sscan_each(key, **options, &block)
|
587
|
+
node_for(key).sscan_each(key, **options, &block)
|
588
|
+
end
|
589
|
+
|
512
590
|
# Subtract multiple sets.
|
513
591
|
def sdiff(*keys)
|
514
592
|
ensure_same_node(:sdiff, keys) do |node|
|
@@ -561,6 +639,7 @@ class Redis
|
|
561
639
|
def zadd(key, *args)
|
562
640
|
node_for(key).zadd(key, *args)
|
563
641
|
end
|
642
|
+
ruby2_keywords(:zadd) if respond_to?(:ruby2_keywords, true)
|
564
643
|
|
565
644
|
# Increment the score of a member in a sorted set.
|
566
645
|
def zincrby(key, increment, member)
|
@@ -577,15 +656,25 @@ class Redis
|
|
577
656
|
node_for(key).zscore(key, member)
|
578
657
|
end
|
579
658
|
|
659
|
+
# Get one or more random members from a sorted set.
|
660
|
+
def zrandmember(key, count = nil, **options)
|
661
|
+
node_for(key).zrandmember(key, count, **options)
|
662
|
+
end
|
663
|
+
|
664
|
+
# Get the scores associated with the given members in a sorted set.
|
665
|
+
def zmscore(key, *members)
|
666
|
+
node_for(key).zmscore(key, *members)
|
667
|
+
end
|
668
|
+
|
580
669
|
# 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)
|
670
|
+
def zrange(key, start, stop, **options)
|
671
|
+
node_for(key).zrange(key, start, stop, **options)
|
583
672
|
end
|
584
673
|
|
585
674
|
# Return a range of members in a sorted set, by index, with scores ordered
|
586
675
|
# from high to low.
|
587
|
-
def zrevrange(key, start, stop, options
|
588
|
-
node_for(key).zrevrange(key, start, stop, options)
|
676
|
+
def zrevrange(key, start, stop, **options)
|
677
|
+
node_for(key).zrevrange(key, start, stop, **options)
|
589
678
|
end
|
590
679
|
|
591
680
|
# Determine the index of a member in a sorted set.
|
@@ -605,14 +694,14 @@ class Redis
|
|
605
694
|
end
|
606
695
|
|
607
696
|
# 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)
|
697
|
+
def zrangebyscore(key, min, max, **options)
|
698
|
+
node_for(key).zrangebyscore(key, min, max, **options)
|
610
699
|
end
|
611
700
|
|
612
701
|
# Return a range of members in a sorted set, by score, with scores ordered
|
613
702
|
# from high to low.
|
614
|
-
def zrevrangebyscore(key, max, min, options
|
615
|
-
node_for(key).zrevrangebyscore(key, max, min, options)
|
703
|
+
def zrevrangebyscore(key, max, min, **options)
|
704
|
+
node_for(key).zrevrangebyscore(key, max, min, **options)
|
616
705
|
end
|
617
706
|
|
618
707
|
# Remove all members in a sorted set within the given scores.
|
@@ -625,18 +714,25 @@ class Redis
|
|
625
714
|
node_for(key).zcount(key, min, max)
|
626
715
|
end
|
627
716
|
|
717
|
+
# Get the intersection of multiple sorted sets
|
718
|
+
def zinter(*keys, **options)
|
719
|
+
ensure_same_node(:zinter, keys) do |node|
|
720
|
+
node.zinter(*keys, **options)
|
721
|
+
end
|
722
|
+
end
|
723
|
+
|
628
724
|
# Intersect multiple sorted sets and store the resulting sorted set in a new
|
629
725
|
# key.
|
630
|
-
def zinterstore(destination, keys, options
|
726
|
+
def zinterstore(destination, keys, **options)
|
631
727
|
ensure_same_node(:zinterstore, [destination] + keys) do |node|
|
632
|
-
node.zinterstore(destination, keys, options)
|
728
|
+
node.zinterstore(destination, keys, **options)
|
633
729
|
end
|
634
730
|
end
|
635
731
|
|
636
732
|
# Add multiple sorted sets and store the resulting sorted set in a new key.
|
637
|
-
def zunionstore(destination, keys, options
|
733
|
+
def zunionstore(destination, keys, **options)
|
638
734
|
ensure_same_node(:zunionstore, [destination] + keys) do |node|
|
639
|
-
node.zunionstore(destination, keys, options)
|
735
|
+
node.zunionstore(destination, keys, **options)
|
640
736
|
end
|
641
737
|
end
|
642
738
|
|
@@ -645,9 +741,9 @@ class Redis
|
|
645
741
|
node_for(key).hlen(key)
|
646
742
|
end
|
647
743
|
|
648
|
-
# Set
|
649
|
-
def hset(key,
|
650
|
-
node_for(key).hset(key,
|
744
|
+
# Set multiple hash fields to multiple values.
|
745
|
+
def hset(key, *attrs)
|
746
|
+
node_for(key).hset(key, *attrs)
|
651
747
|
end
|
652
748
|
|
653
749
|
# Set the value of a hash field, only if the field does not exist.
|
@@ -679,8 +775,8 @@ class Redis
|
|
679
775
|
end
|
680
776
|
|
681
777
|
# Delete one or more hash fields.
|
682
|
-
def hdel(key,
|
683
|
-
node_for(key).hdel(key,
|
778
|
+
def hdel(key, *fields)
|
779
|
+
node_for(key).hdel(key, *fields)
|
684
780
|
end
|
685
781
|
|
686
782
|
# Determine if a hash field exists.
|
@@ -719,7 +815,7 @@ class Redis
|
|
719
815
|
end
|
720
816
|
|
721
817
|
def subscribed?
|
722
|
-
|
818
|
+
!!@subscribed_node
|
723
819
|
end
|
724
820
|
|
725
821
|
# Listen for messages published to the given channels.
|
@@ -737,7 +833,8 @@ class Redis
|
|
737
833
|
|
738
834
|
# Stop listening for messages posted to the given channels.
|
739
835
|
def unsubscribe(*channels)
|
740
|
-
raise
|
836
|
+
raise "Can't unsubscribe if not subscribed." unless subscribed?
|
837
|
+
|
741
838
|
@subscribed_node.unsubscribe(*channels)
|
742
839
|
end
|
743
840
|
|
@@ -753,13 +850,26 @@ class Redis
|
|
753
850
|
end
|
754
851
|
|
755
852
|
# Watch the given keys to determine execution of the MULTI/EXEC block.
|
756
|
-
def watch(*keys)
|
757
|
-
|
853
|
+
def watch(*keys, &block)
|
854
|
+
ensure_same_node(:watch, keys) do |node|
|
855
|
+
@watch_key = key_tag(keys.first) || keys.first.to_s
|
856
|
+
|
857
|
+
begin
|
858
|
+
node.watch(*keys, &block)
|
859
|
+
rescue StandardError
|
860
|
+
@watch_key = nil
|
861
|
+
raise
|
862
|
+
end
|
863
|
+
end
|
758
864
|
end
|
759
865
|
|
760
866
|
# Forget about all watched keys.
|
761
867
|
def unwatch
|
762
|
-
raise CannotDistribute, :unwatch
|
868
|
+
raise CannotDistribute, :unwatch unless @watch_key
|
869
|
+
|
870
|
+
result = node_for(@watch_key).unwatch
|
871
|
+
@watch_key = nil
|
872
|
+
result
|
763
873
|
end
|
764
874
|
|
765
875
|
def pipelined
|
@@ -767,18 +877,30 @@ class Redis
|
|
767
877
|
end
|
768
878
|
|
769
879
|
# Mark the start of a transaction block.
|
770
|
-
def multi
|
771
|
-
raise CannotDistribute, :multi
|
880
|
+
def multi(&block)
|
881
|
+
raise CannotDistribute, :multi unless @watch_key
|
882
|
+
|
883
|
+
result = node_for(@watch_key).multi(&block)
|
884
|
+
@watch_key = nil if block_given?
|
885
|
+
result
|
772
886
|
end
|
773
887
|
|
774
888
|
# Execute all commands issued after MULTI.
|
775
889
|
def exec
|
776
|
-
raise CannotDistribute, :exec
|
890
|
+
raise CannotDistribute, :exec unless @watch_key
|
891
|
+
|
892
|
+
result = node_for(@watch_key).exec
|
893
|
+
@watch_key = nil
|
894
|
+
result
|
777
895
|
end
|
778
896
|
|
779
897
|
# Discard all commands issued after MULTI.
|
780
898
|
def discard
|
781
|
-
raise CannotDistribute, :discard
|
899
|
+
raise CannotDistribute, :discard unless @watch_key
|
900
|
+
|
901
|
+
result = node_for(@watch_key).discard
|
902
|
+
@watch_key = nil
|
903
|
+
result
|
782
904
|
end
|
783
905
|
|
784
906
|
# Control remote script registry.
|
@@ -837,7 +959,7 @@ class Redis
|
|
837
959
|
self.class.new(@node_configs, @default_options)
|
838
960
|
end
|
839
961
|
|
840
|
-
|
962
|
+
protected
|
841
963
|
|
842
964
|
def on_each_node(command, *args)
|
843
965
|
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,50 @@ 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 some cluster subcommands were called.
|
50
|
+
class OrchestrationCommandNotSupported < BaseError
|
51
|
+
def initialize(command, subcommand = '')
|
52
|
+
str = [command, subcommand].map(&:to_s).reject(&:empty?).join(' ').upcase
|
53
|
+
msg = "#{str} command should be used with care "\
|
54
|
+
'only by applications orchestrating Redis Cluster, like redis-trib, '\
|
55
|
+
'and the command if used out of the right context can leave the cluster '\
|
56
|
+
'in a wrong state or cause data loss.'
|
57
|
+
super(msg)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Raised when error occurs on any node of cluster.
|
62
|
+
class CommandErrorCollection < BaseError
|
63
|
+
attr_reader :errors
|
64
|
+
|
65
|
+
# @param errors [Hash{String => Redis::CommandError}]
|
66
|
+
# @param error_message [String]
|
67
|
+
def initialize(errors, error_message = 'Command errors were replied on any node')
|
68
|
+
@errors = errors
|
69
|
+
super(error_message)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Raised when cluster client can't select node.
|
74
|
+
class AmbiguousNodeError < BaseError
|
75
|
+
def initialize(command)
|
76
|
+
super("Cluster client doesn't know which node the #{command} command should be sent to.")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Raised when commands in pipelining include cross slot keys.
|
81
|
+
class CrossSlotPipeliningError < BaseError
|
82
|
+
def initialize(keys)
|
83
|
+
super("Cluster client couldn't send pipelining to single node. "\
|
84
|
+
"The commands include cross slot keys. #{keys}")
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
40
88
|
end
|