redis 4.8.0 → 5.0.8
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 +4 -4
- data/CHANGELOG.md +64 -1
- data/README.md +101 -161
- data/lib/redis/client.rb +82 -625
- data/lib/redis/commands/bitmaps.rb +4 -1
- data/lib/redis/commands/cluster.rb +1 -18
- data/lib/redis/commands/connection.rb +5 -10
- data/lib/redis/commands/geo.rb +3 -3
- data/lib/redis/commands/hashes.rb +9 -6
- data/lib/redis/commands/hyper_log_log.rb +1 -1
- data/lib/redis/commands/keys.rb +5 -23
- data/lib/redis/commands/lists.rb +74 -25
- data/lib/redis/commands/pubsub.rb +28 -25
- data/lib/redis/commands/server.rb +15 -15
- data/lib/redis/commands/sets.rb +31 -40
- data/lib/redis/commands/sorted_sets.rb +84 -12
- data/lib/redis/commands/streams.rb +39 -19
- data/lib/redis/commands/strings.rb +18 -17
- data/lib/redis/commands/transactions.rb +7 -31
- data/lib/redis/commands.rb +4 -7
- data/lib/redis/distributed.rb +114 -64
- data/lib/redis/errors.rb +15 -50
- data/lib/redis/hash_ring.rb +26 -26
- data/lib/redis/pipeline.rb +43 -222
- data/lib/redis/subscribe.rb +50 -14
- data/lib/redis/version.rb +1 -1
- data/lib/redis.rb +76 -184
- metadata +10 -54
- data/lib/redis/cluster/command.rb +0 -79
- data/lib/redis/cluster/command_loader.rb +0 -33
- data/lib/redis/cluster/key_slot_converter.rb +0 -72
- data/lib/redis/cluster/node.rb +0 -120
- data/lib/redis/cluster/node_key.rb +0 -31
- data/lib/redis/cluster/node_loader.rb +0 -34
- data/lib/redis/cluster/option.rb +0 -100
- data/lib/redis/cluster/slot.rb +0 -86
- data/lib/redis/cluster/slot_loader.rb +0 -46
- data/lib/redis/cluster.rb +0 -315
- data/lib/redis/connection/command_helper.rb +0 -41
- data/lib/redis/connection/hiredis.rb +0 -68
- data/lib/redis/connection/registry.rb +0 -13
- data/lib/redis/connection/ruby.rb +0 -437
- data/lib/redis/connection/synchrony.rb +0 -148
- data/lib/redis/connection.rb +0 -11
data/lib/redis/distributed.rb
CHANGED
@@ -20,7 +20,7 @@ class Redis
|
|
20
20
|
def initialize(node_configs, options = {})
|
21
21
|
@tag = options[:tag] || /^\{(.+?)\}/
|
22
22
|
@ring = options[:ring] || HashRing.new
|
23
|
-
@node_configs = node_configs.dup
|
23
|
+
@node_configs = node_configs.map(&:dup)
|
24
24
|
@default_options = options.dup
|
25
25
|
node_configs.each { |node_config| add_node(node_config) }
|
26
26
|
@subscribed_node = nil
|
@@ -41,6 +41,8 @@ class Redis
|
|
41
41
|
def add_node(options)
|
42
42
|
options = { url: options } if options.is_a?(String)
|
43
43
|
options = @default_options.merge(options)
|
44
|
+
options.delete(:tag)
|
45
|
+
options.delete(:ring)
|
44
46
|
@ring.add_node Redis.new(options)
|
45
47
|
end
|
46
48
|
|
@@ -64,6 +66,10 @@ class Redis
|
|
64
66
|
on_each_node :quit
|
65
67
|
end
|
66
68
|
|
69
|
+
def close
|
70
|
+
on_each_node :close
|
71
|
+
end
|
72
|
+
|
67
73
|
# Asynchronously save the dataset to disk.
|
68
74
|
def bgsave
|
69
75
|
on_each_node :bgsave
|
@@ -161,6 +167,7 @@ class Redis
|
|
161
167
|
|
162
168
|
# Delete a key.
|
163
169
|
def del(*args)
|
170
|
+
args.flatten!(1)
|
164
171
|
keys_per_node = args.group_by { |key| node_for(key) }
|
165
172
|
keys_per_node.inject(0) do |sum, (node, keys)|
|
166
173
|
sum + node.del(*keys)
|
@@ -169,6 +176,7 @@ class Redis
|
|
169
176
|
|
170
177
|
# Unlink keys.
|
171
178
|
def unlink(*args)
|
179
|
+
args.flatten!(1)
|
172
180
|
keys_per_node = args.group_by { |key| node_for(key) }
|
173
181
|
keys_per_node.inject(0) do |sum, (node, keys)|
|
174
182
|
sum + node.unlink(*keys)
|
@@ -177,23 +185,16 @@ class Redis
|
|
177
185
|
|
178
186
|
# Determine if a key exists.
|
179
187
|
def exists(*args)
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
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
|
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
192
|
end
|
193
193
|
end
|
194
194
|
|
195
195
|
# Determine if any of the keys exists.
|
196
196
|
def exists?(*args)
|
197
|
+
args.flatten!(1)
|
197
198
|
keys_per_node = args.group_by { |key| node_for(key) }
|
198
199
|
keys_per_node.each do |node, keys|
|
199
200
|
return true if node.exists?(*keys)
|
@@ -297,7 +298,7 @@ class Redis
|
|
297
298
|
end
|
298
299
|
|
299
300
|
# Set multiple keys to multiple values.
|
300
|
-
def mset(*
|
301
|
+
def mset(*)
|
301
302
|
raise CannotDistribute, :mset
|
302
303
|
end
|
303
304
|
|
@@ -306,7 +307,7 @@ class Redis
|
|
306
307
|
end
|
307
308
|
|
308
309
|
# Set multiple keys to multiple values, only if none of the keys exist.
|
309
|
-
def msetnx(*
|
310
|
+
def msetnx(*)
|
310
311
|
raise CannotDistribute, :msetnx
|
311
312
|
end
|
312
313
|
|
@@ -331,11 +332,13 @@ class Redis
|
|
331
332
|
|
332
333
|
# Get the values of all the given keys as an Array.
|
333
334
|
def mget(*keys)
|
335
|
+
keys.flatten!(1)
|
334
336
|
mapped_mget(*keys).values_at(*keys)
|
335
337
|
end
|
336
338
|
|
337
339
|
# Get the values of all the given keys as a Hash.
|
338
340
|
def mapped_mget(*keys)
|
341
|
+
keys.flatten!(1)
|
339
342
|
keys.group_by { |k| node_for k }.inject({}) do |results, (node, subkeys)|
|
340
343
|
results.merge! node.mapped_mget(*subkeys)
|
341
344
|
end
|
@@ -373,8 +376,9 @@ class Redis
|
|
373
376
|
|
374
377
|
# Perform a bitwise operation between strings and store the resulting string in a key.
|
375
378
|
def bitop(operation, destkey, *keys)
|
379
|
+
keys.flatten!(1)
|
376
380
|
ensure_same_node(:bitop, [destkey] + keys) do |node|
|
377
|
-
node.bitop(operation, destkey,
|
381
|
+
node.bitop(operation, destkey, keys)
|
378
382
|
end
|
379
383
|
end
|
380
384
|
|
@@ -463,23 +467,15 @@ class Redis
|
|
463
467
|
timeout = if args.last.is_a?(Hash)
|
464
468
|
options = args.pop
|
465
469
|
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
|
474
470
|
end
|
475
471
|
|
476
|
-
|
472
|
+
args.flatten!(1)
|
477
473
|
|
478
|
-
ensure_same_node(cmd,
|
474
|
+
ensure_same_node(cmd, args) do |node|
|
479
475
|
if timeout
|
480
|
-
node.__send__(cmd,
|
476
|
+
node.__send__(cmd, args, timeout: timeout)
|
481
477
|
else
|
482
|
-
node.__send__(cmd,
|
478
|
+
node.__send__(cmd, args)
|
483
479
|
end
|
484
480
|
end
|
485
481
|
end
|
@@ -490,6 +486,18 @@ class Redis
|
|
490
486
|
_bpop(:blpop, args)
|
491
487
|
end
|
492
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
|
+
|
493
501
|
# Remove and get the last element in a list, or block until one is
|
494
502
|
# available.
|
495
503
|
def brpop(*args)
|
@@ -498,9 +506,9 @@ class Redis
|
|
498
506
|
|
499
507
|
# Pop a value from a list, push it to another list and return it; or block
|
500
508
|
# until one is available.
|
501
|
-
def brpoplpush(source, destination,
|
509
|
+
def brpoplpush(source, destination, **options)
|
502
510
|
ensure_same_node(:brpoplpush, [source, destination]) do |node|
|
503
|
-
node.brpoplpush(source, destination,
|
511
|
+
node.brpoplpush(source, destination, **options)
|
504
512
|
end
|
505
513
|
end
|
506
514
|
|
@@ -534,29 +542,43 @@ class Redis
|
|
534
542
|
node_for(key).ltrim(key, start, stop)
|
535
543
|
end
|
536
544
|
|
545
|
+
# Iterate over keys, blocking and removing elements from the first non empty liist found.
|
546
|
+
def blmpop(timeout, *keys, modifier: "LEFT", count: nil)
|
547
|
+
ensure_same_node(:blmpop, keys) do |node|
|
548
|
+
node.blmpop(timeout, *keys, modifier: modifier, count: count)
|
549
|
+
end
|
550
|
+
end
|
551
|
+
|
552
|
+
# Iterate over keys, removing elements from the first non list found.
|
553
|
+
def lmpop(*keys, modifier: "LEFT", count: nil)
|
554
|
+
ensure_same_node(:lmpop, keys) do |node|
|
555
|
+
node.lmpop(*keys, modifier: modifier, count: count)
|
556
|
+
end
|
557
|
+
end
|
558
|
+
|
537
559
|
# Get the number of members in a set.
|
538
560
|
def scard(key)
|
539
561
|
node_for(key).scard(key)
|
540
562
|
end
|
541
563
|
|
542
564
|
# Add one or more members to a set.
|
543
|
-
def sadd(key,
|
544
|
-
node_for(key).sadd(key,
|
565
|
+
def sadd(key, *members)
|
566
|
+
node_for(key).sadd(key, *members)
|
545
567
|
end
|
546
568
|
|
547
569
|
# Add one or more members to a set.
|
548
|
-
def sadd?(key,
|
549
|
-
node_for(key).sadd?(key,
|
570
|
+
def sadd?(key, *members)
|
571
|
+
node_for(key).sadd?(key, *members)
|
550
572
|
end
|
551
573
|
|
552
574
|
# Remove one or more members from a set.
|
553
|
-
def srem(key,
|
554
|
-
node_for(key).srem(key,
|
575
|
+
def srem(key, *members)
|
576
|
+
node_for(key).srem(key, *members)
|
555
577
|
end
|
556
578
|
|
557
579
|
# Remove one or more members from a set.
|
558
|
-
def srem?(key,
|
559
|
-
node_for(key).srem?(key,
|
580
|
+
def srem?(key, *members)
|
581
|
+
node_for(key).srem?(key, *members)
|
560
582
|
end
|
561
583
|
|
562
584
|
# Remove and return a random member from a set.
|
@@ -603,43 +625,49 @@ class Redis
|
|
603
625
|
|
604
626
|
# Subtract multiple sets.
|
605
627
|
def sdiff(*keys)
|
628
|
+
keys.flatten!(1)
|
606
629
|
ensure_same_node(:sdiff, keys) do |node|
|
607
|
-
node.sdiff(
|
630
|
+
node.sdiff(keys)
|
608
631
|
end
|
609
632
|
end
|
610
633
|
|
611
634
|
# Subtract multiple sets and store the resulting set in a key.
|
612
635
|
def sdiffstore(destination, *keys)
|
613
|
-
|
614
|
-
|
636
|
+
keys.flatten!(1)
|
637
|
+
ensure_same_node(:sdiffstore, [destination].concat(keys)) do |node|
|
638
|
+
node.sdiffstore(destination, keys)
|
615
639
|
end
|
616
640
|
end
|
617
641
|
|
618
642
|
# Intersect multiple sets.
|
619
643
|
def sinter(*keys)
|
644
|
+
keys.flatten!(1)
|
620
645
|
ensure_same_node(:sinter, keys) do |node|
|
621
|
-
node.sinter(
|
646
|
+
node.sinter(keys)
|
622
647
|
end
|
623
648
|
end
|
624
649
|
|
625
650
|
# Intersect multiple sets and store the resulting set in a key.
|
626
651
|
def sinterstore(destination, *keys)
|
627
|
-
|
628
|
-
|
652
|
+
keys.flatten!(1)
|
653
|
+
ensure_same_node(:sinterstore, [destination].concat(keys)) do |node|
|
654
|
+
node.sinterstore(destination, keys)
|
629
655
|
end
|
630
656
|
end
|
631
657
|
|
632
658
|
# Add multiple sets.
|
633
659
|
def sunion(*keys)
|
660
|
+
keys.flatten!(1)
|
634
661
|
ensure_same_node(:sunion, keys) do |node|
|
635
|
-
node.sunion(
|
662
|
+
node.sunion(keys)
|
636
663
|
end
|
637
664
|
end
|
638
665
|
|
639
666
|
# Add multiple sets and store the resulting set in a key.
|
640
667
|
def sunionstore(destination, *keys)
|
641
|
-
|
642
|
-
|
668
|
+
keys.flatten!(1)
|
669
|
+
ensure_same_node(:sunionstore, [destination].concat(keys)) do |node|
|
670
|
+
node.sunionstore(destination, keys)
|
643
671
|
end
|
644
672
|
end
|
645
673
|
|
@@ -680,6 +708,20 @@ class Redis
|
|
680
708
|
node_for(key).zmscore(key, *members)
|
681
709
|
end
|
682
710
|
|
711
|
+
# Iterate over keys, blocking and removing members from the first non empty sorted set found.
|
712
|
+
def bzmpop(timeout, *keys, modifier: "MIN", count: nil)
|
713
|
+
ensure_same_node(:bzmpop, keys) do |node|
|
714
|
+
node.bzmpop(timeout, *keys, modifier: modifier, count: count)
|
715
|
+
end
|
716
|
+
end
|
717
|
+
|
718
|
+
# Iterate over keys, removing members from the first non empty sorted set found.
|
719
|
+
def zmpop(*keys, modifier: "MIN", count: nil)
|
720
|
+
ensure_same_node(:zmpop, keys) do |node|
|
721
|
+
node.zmpop(*keys, modifier: modifier, count: count)
|
722
|
+
end
|
723
|
+
end
|
724
|
+
|
683
725
|
# Return a range of members in a sorted set, by index, score or lexicographical ordering.
|
684
726
|
def zrange(key, start, stop, **options)
|
685
727
|
node_for(key).zrange(key, start, stop, **options)
|
@@ -738,43 +780,49 @@ class Redis
|
|
738
780
|
|
739
781
|
# Get the intersection of multiple sorted sets
|
740
782
|
def zinter(*keys, **options)
|
783
|
+
keys.flatten!(1)
|
741
784
|
ensure_same_node(:zinter, keys) do |node|
|
742
|
-
node.zinter(
|
785
|
+
node.zinter(keys, **options)
|
743
786
|
end
|
744
787
|
end
|
745
788
|
|
746
789
|
# Intersect multiple sorted sets and store the resulting sorted set in a new
|
747
790
|
# key.
|
748
|
-
def zinterstore(destination, keys, **options)
|
749
|
-
|
791
|
+
def zinterstore(destination, *keys, **options)
|
792
|
+
keys.flatten!(1)
|
793
|
+
ensure_same_node(:zinterstore, [destination].concat(keys)) do |node|
|
750
794
|
node.zinterstore(destination, keys, **options)
|
751
795
|
end
|
752
796
|
end
|
753
797
|
|
754
798
|
# Return the union of multiple sorted sets.
|
755
799
|
def zunion(*keys, **options)
|
800
|
+
keys.flatten!(1)
|
756
801
|
ensure_same_node(:zunion, keys) do |node|
|
757
|
-
node.zunion(
|
802
|
+
node.zunion(keys, **options)
|
758
803
|
end
|
759
804
|
end
|
760
805
|
|
761
806
|
# Add multiple sorted sets and store the resulting sorted set in a new key.
|
762
|
-
def zunionstore(destination, keys, **options)
|
763
|
-
|
807
|
+
def zunionstore(destination, *keys, **options)
|
808
|
+
keys.flatten!(1)
|
809
|
+
ensure_same_node(:zunionstore, [destination].concat(keys)) do |node|
|
764
810
|
node.zunionstore(destination, keys, **options)
|
765
811
|
end
|
766
812
|
end
|
767
813
|
|
768
814
|
# Return the difference between the first and all successive input sorted sets.
|
769
815
|
def zdiff(*keys, **options)
|
816
|
+
keys.flatten!(1)
|
770
817
|
ensure_same_node(:zdiff, keys) do |node|
|
771
|
-
node.zdiff(
|
818
|
+
node.zdiff(keys, **options)
|
772
819
|
end
|
773
820
|
end
|
774
821
|
|
775
822
|
# Compute the difference between the first and all successive input sorted sets
|
776
823
|
# and store the resulting sorted set in a new key.
|
777
|
-
def zdiffstore(destination, keys, **options)
|
824
|
+
def zdiffstore(destination, *keys, **options)
|
825
|
+
keys.flatten!(1)
|
778
826
|
ensure_same_node(:zdiffstore, [destination] + keys) do |node|
|
779
827
|
node.zdiffstore(destination, keys, **options)
|
780
828
|
end
|
@@ -801,7 +849,7 @@ class Redis
|
|
801
849
|
end
|
802
850
|
|
803
851
|
def mapped_hmset(key, hash)
|
804
|
-
node_for(key).hmset(key,
|
852
|
+
node_for(key).hmset(key, hash)
|
805
853
|
end
|
806
854
|
|
807
855
|
# Get the value of a hash field.
|
@@ -811,11 +859,13 @@ class Redis
|
|
811
859
|
|
812
860
|
# Get the values of all the given hash fields.
|
813
861
|
def hmget(key, *fields)
|
814
|
-
|
862
|
+
fields.flatten!(1)
|
863
|
+
node_for(key).hmget(key, fields)
|
815
864
|
end
|
816
865
|
|
817
866
|
def mapped_hmget(key, *fields)
|
818
|
-
|
867
|
+
fields.flatten!(1)
|
868
|
+
node_for(key).mapped_hmget(key, fields)
|
819
869
|
end
|
820
870
|
|
821
871
|
def hrandfield(key, count = nil, **options)
|
@@ -824,7 +874,8 @@ class Redis
|
|
824
874
|
|
825
875
|
# Delete one or more hash fields.
|
826
876
|
def hdel(key, *fields)
|
827
|
-
|
877
|
+
fields.flatten!(1)
|
878
|
+
node_for(key).hdel(key, fields)
|
828
879
|
end
|
829
880
|
|
830
881
|
# Determine if a hash field exists.
|
@@ -881,7 +932,7 @@ class Redis
|
|
881
932
|
|
882
933
|
# Stop listening for messages posted to the given channels.
|
883
934
|
def unsubscribe(*channels)
|
884
|
-
raise "Can't unsubscribe if not subscribed." unless subscribed?
|
935
|
+
raise SubscriptionError, "Can't unsubscribe if not subscribed." unless subscribed?
|
885
936
|
|
886
937
|
@subscribed_node.unsubscribe(*channels)
|
887
938
|
end
|
@@ -928,9 +979,7 @@ class Redis
|
|
928
979
|
def multi(&block)
|
929
980
|
raise CannotDistribute, :multi unless @watch_key
|
930
981
|
|
931
|
-
|
932
|
-
@watch_key = nil if block_given?
|
933
|
-
result
|
982
|
+
node_for(@watch_key).multi(&block)
|
934
983
|
end
|
935
984
|
|
936
985
|
# Execute all commands issued after MULTI.
|
@@ -1020,7 +1069,8 @@ class Redis
|
|
1020
1069
|
end
|
1021
1070
|
|
1022
1071
|
def key_tag(key)
|
1023
|
-
key.to_s
|
1072
|
+
key = key.to_s
|
1073
|
+
key[@tag, 1] if key.match?(@tag)
|
1024
1074
|
end
|
1025
1075
|
|
1026
1076
|
def ensure_same_node(command, keys)
|
data/lib/redis/errors.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
class Redis
|
4
4
|
# Base error for all redis-rb errors.
|
5
|
-
class BaseError <
|
5
|
+
class BaseError < StandardError
|
6
6
|
end
|
7
7
|
|
8
8
|
# Raised by the connection when a protocol error occurs.
|
@@ -20,6 +20,15 @@ class Redis
|
|
20
20
|
class CommandError < BaseError
|
21
21
|
end
|
22
22
|
|
23
|
+
class PermissionError < CommandError
|
24
|
+
end
|
25
|
+
|
26
|
+
class WrongTypeError < CommandError
|
27
|
+
end
|
28
|
+
|
29
|
+
class OutOfMemoryError < CommandError
|
30
|
+
end
|
31
|
+
|
23
32
|
# Base error for connection related errors.
|
24
33
|
class BaseConnectionError < BaseError
|
25
34
|
end
|
@@ -40,58 +49,14 @@ class Redis
|
|
40
49
|
class InheritedError < BaseConnectionError
|
41
50
|
end
|
42
51
|
|
52
|
+
# Generally raised during Redis failover scenarios
|
53
|
+
class ReadOnlyError < BaseConnectionError
|
54
|
+
end
|
55
|
+
|
43
56
|
# Raised when client options are invalid.
|
44
57
|
class InvalidClientOptionError < BaseError
|
45
58
|
end
|
46
59
|
|
47
|
-
class
|
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
|
60
|
+
class SubscriptionError < BaseError
|
96
61
|
end
|
97
62
|
end
|
data/lib/redis/hash_ring.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'zlib'
|
4
|
+
require 'digest/md5'
|
4
5
|
|
5
6
|
class Redis
|
6
7
|
class HashRing
|
@@ -25,7 +26,7 @@ class Redis
|
|
25
26
|
def add_node(node)
|
26
27
|
@nodes << node
|
27
28
|
@replicas.times do |i|
|
28
|
-
key =
|
29
|
+
key = server_hash_for("#{node.id}:#{i}")
|
29
30
|
@ring[key] = node
|
30
31
|
@sorted_keys << key
|
31
32
|
end
|
@@ -35,7 +36,7 @@ class Redis
|
|
35
36
|
def remove_node(node)
|
36
37
|
@nodes.reject! { |n| n.id == node.id }
|
37
38
|
@replicas.times do |i|
|
38
|
-
key =
|
39
|
+
key = server_hash_for("#{node.id}:#{i}")
|
39
40
|
@ring.delete(key)
|
40
41
|
@sorted_keys.reject! { |k| k == key }
|
41
42
|
end
|
@@ -43,47 +44,46 @@ class Redis
|
|
43
44
|
|
44
45
|
# get the node in the hash ring for this key
|
45
46
|
def get_node(key)
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
def get_node_pos(key)
|
50
|
-
return [nil, nil] if @ring.empty?
|
51
|
-
|
52
|
-
crc = Zlib.crc32(key)
|
53
|
-
idx = HashRing.binary_search(@sorted_keys, crc)
|
54
|
-
[@ring[@sorted_keys[idx]], idx]
|
47
|
+
hash = hash_for(key)
|
48
|
+
idx = binary_search(@sorted_keys, hash)
|
49
|
+
@ring[@sorted_keys[idx]]
|
55
50
|
end
|
56
51
|
|
57
52
|
def iter_nodes(key)
|
58
53
|
return [nil, nil] if @ring.empty?
|
59
54
|
|
60
|
-
|
55
|
+
crc = hash_for(key)
|
56
|
+
pos = binary_search(@sorted_keys, crc)
|
61
57
|
@ring.size.times do |n|
|
62
58
|
yield @ring[@sorted_keys[(pos + n) % @ring.size]]
|
63
59
|
end
|
64
60
|
end
|
65
61
|
|
62
|
+
private
|
63
|
+
|
64
|
+
def hash_for(key)
|
65
|
+
Zlib.crc32(key)
|
66
|
+
end
|
67
|
+
|
68
|
+
def server_hash_for(key)
|
69
|
+
Digest::MD5.digest(key).unpack1("L>")
|
70
|
+
end
|
71
|
+
|
66
72
|
# Find the closest index in HashRing with value <= the given value
|
67
|
-
def
|
68
|
-
upper = ary.size
|
73
|
+
def binary_search(ary, value)
|
74
|
+
upper = ary.size
|
69
75
|
lower = 0
|
70
|
-
idx = 0
|
71
|
-
|
72
|
-
while lower <= upper
|
73
|
-
idx = (lower + upper) / 2
|
74
|
-
comp = ary[idx] <=> value
|
75
76
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
upper =
|
77
|
+
while lower < upper
|
78
|
+
mid = (lower + upper) / 2
|
79
|
+
if ary[mid] > value
|
80
|
+
upper = mid
|
80
81
|
else
|
81
|
-
lower =
|
82
|
+
lower = mid + 1
|
82
83
|
end
|
83
84
|
end
|
84
85
|
|
85
|
-
upper
|
86
|
-
upper
|
86
|
+
upper - 1
|
87
87
|
end
|
88
88
|
end
|
89
89
|
end
|