redis 4.5.1 → 5.0.6

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +120 -0
  3. data/README.md +82 -153
  4. data/lib/redis/client.rb +77 -615
  5. data/lib/redis/commands/bitmaps.rb +66 -0
  6. data/lib/redis/commands/cluster.rb +28 -0
  7. data/lib/redis/commands/connection.rb +53 -0
  8. data/lib/redis/commands/geo.rb +84 -0
  9. data/lib/redis/commands/hashes.rb +254 -0
  10. data/lib/redis/commands/hyper_log_log.rb +37 -0
  11. data/lib/redis/commands/keys.rb +437 -0
  12. data/lib/redis/commands/lists.rb +285 -0
  13. data/lib/redis/commands/pubsub.rb +54 -0
  14. data/lib/redis/commands/scripting.rb +114 -0
  15. data/lib/redis/commands/server.rb +188 -0
  16. data/lib/redis/commands/sets.rb +214 -0
  17. data/lib/redis/commands/sorted_sets.rb +818 -0
  18. data/lib/redis/commands/streams.rb +402 -0
  19. data/lib/redis/commands/strings.rb +314 -0
  20. data/lib/redis/commands/transactions.rb +115 -0
  21. data/lib/redis/commands.rb +237 -0
  22. data/lib/redis/distributed.rb +140 -70
  23. data/lib/redis/errors.rb +15 -41
  24. data/lib/redis/hash_ring.rb +26 -26
  25. data/lib/redis/pipeline.rb +66 -120
  26. data/lib/redis/subscribe.rb +23 -15
  27. data/lib/redis/version.rb +1 -1
  28. data/lib/redis.rb +106 -3736
  29. metadata +26 -53
  30. data/lib/redis/cluster/command.rb +0 -81
  31. data/lib/redis/cluster/command_loader.rb +0 -33
  32. data/lib/redis/cluster/key_slot_converter.rb +0 -72
  33. data/lib/redis/cluster/node.rb +0 -108
  34. data/lib/redis/cluster/node_key.rb +0 -31
  35. data/lib/redis/cluster/node_loader.rb +0 -37
  36. data/lib/redis/cluster/option.rb +0 -93
  37. data/lib/redis/cluster/slot.rb +0 -86
  38. data/lib/redis/cluster/slot_loader.rb +0 -49
  39. data/lib/redis/cluster.rb +0 -291
  40. data/lib/redis/connection/command_helper.rb +0 -41
  41. data/lib/redis/connection/hiredis.rb +0 -67
  42. data/lib/redis/connection/registry.rb +0 -13
  43. data/lib/redis/connection/ruby.rb +0 -428
  44. data/lib/redis/connection/synchrony.rb +0 -146
  45. data/lib/redis/connection.rb +0 -11
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "hash_ring"
3
+ require "redis/hash_ring"
4
4
 
5
5
  class Redis
6
6
  class Distributed
@@ -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
@@ -115,13 +121,13 @@ class Redis
115
121
  end
116
122
 
117
123
  # Set a key's time to live in seconds.
118
- def expire(key, seconds)
119
- node_for(key).expire(key, seconds)
124
+ def expire(key, seconds, **kwargs)
125
+ node_for(key).expire(key, seconds, **kwargs)
120
126
  end
121
127
 
122
128
  # Set the expiration for a key as a UNIX timestamp.
123
- def expireat(key, unix_time)
124
- node_for(key).expireat(key, unix_time)
129
+ def expireat(key, unix_time, **kwargs)
130
+ node_for(key).expireat(key, unix_time, **kwargs)
125
131
  end
126
132
 
127
133
  # Get the time to live (in seconds) for a key.
@@ -130,13 +136,13 @@ class Redis
130
136
  end
131
137
 
132
138
  # Set a key's time to live in milliseconds.
133
- def pexpire(key, milliseconds)
134
- node_for(key).pexpire(key, milliseconds)
139
+ def pexpire(key, milliseconds, **kwarg)
140
+ node_for(key).pexpire(key, milliseconds, **kwarg)
135
141
  end
136
142
 
137
143
  # Set the expiration for a key as number of milliseconds from UNIX Epoch.
138
- def pexpireat(key, ms_unix_time)
139
- node_for(key).pexpireat(key, ms_unix_time)
144
+ def pexpireat(key, ms_unix_time, **kwarg)
145
+ node_for(key).pexpireat(key, ms_unix_time, **kwarg)
140
146
  end
141
147
 
142
148
  # Get the time to live (in milliseconds) for a key.
@@ -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,27 +185,16 @@ class Redis
177
185
 
178
186
  # Determine if a key exists.
179
187
  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
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)
196
192
  end
197
193
  end
198
194
 
199
195
  # Determine if any of the keys exists.
200
196
  def exists?(*args)
197
+ args.flatten!(1)
201
198
  keys_per_node = args.group_by { |key| node_for(key) }
202
199
  keys_per_node.each do |node, keys|
203
200
  return true if node.exists?(*keys)
@@ -215,6 +212,13 @@ class Redis
215
212
  node_for(key).move(key, db)
216
213
  end
217
214
 
215
+ # Copy a value from one key to another.
216
+ def copy(source, destination, **options)
217
+ ensure_same_node(:copy, [source, destination]) do |node|
218
+ node.copy(source, destination, **options)
219
+ end
220
+ end
221
+
218
222
  # Return a random key from the keyspace.
219
223
  def randomkey
220
224
  raise CannotDistribute, :randomkey
@@ -294,7 +298,7 @@ class Redis
294
298
  end
295
299
 
296
300
  # Set multiple keys to multiple values.
297
- def mset(*_args)
301
+ def mset(*)
298
302
  raise CannotDistribute, :mset
299
303
  end
300
304
 
@@ -303,7 +307,7 @@ class Redis
303
307
  end
304
308
 
305
309
  # Set multiple keys to multiple values, only if none of the keys exist.
306
- def msetnx(*_args)
310
+ def msetnx(*)
307
311
  raise CannotDistribute, :msetnx
308
312
  end
309
313
 
@@ -328,11 +332,13 @@ class Redis
328
332
 
329
333
  # Get the values of all the given keys as an Array.
330
334
  def mget(*keys)
335
+ keys.flatten!(1)
331
336
  mapped_mget(*keys).values_at(*keys)
332
337
  end
333
338
 
334
339
  # Get the values of all the given keys as a Hash.
335
340
  def mapped_mget(*keys)
341
+ keys.flatten!(1)
336
342
  keys.group_by { |k| node_for k }.inject({}) do |results, (node, subkeys)|
337
343
  results.merge! node.mapped_mget(*subkeys)
338
344
  end
@@ -370,8 +376,9 @@ class Redis
370
376
 
371
377
  # Perform a bitwise operation between strings and store the resulting string in a key.
372
378
  def bitop(operation, destkey, *keys)
379
+ keys.flatten!(1)
373
380
  ensure_same_node(:bitop, [destkey] + keys) do |node|
374
- node.bitop(operation, destkey, *keys)
381
+ node.bitop(operation, destkey, keys)
375
382
  end
376
383
  end
377
384
 
@@ -460,22 +467,15 @@ class Redis
460
467
  timeout = if args.last.is_a?(Hash)
461
468
  options = args.pop
462
469
  options[:timeout]
463
- elsif args.last.respond_to?(:to_int)
464
- # Issue deprecation notice in obnoxious mode...
465
- args.pop.to_int
466
- end
467
-
468
- if args.size > 1
469
- # Issue deprecation notice in obnoxious mode...
470
470
  end
471
471
 
472
- keys = args.flatten
472
+ args.flatten!(1)
473
473
 
474
- ensure_same_node(cmd, keys) do |node|
474
+ ensure_same_node(cmd, args) do |node|
475
475
  if timeout
476
- node.__send__(cmd, keys, timeout: timeout)
476
+ node.__send__(cmd, args, timeout: timeout)
477
477
  else
478
- node.__send__(cmd, keys)
478
+ node.__send__(cmd, args)
479
479
  end
480
480
  end
481
481
  end
@@ -486,6 +486,18 @@ class Redis
486
486
  _bpop(:blpop, args)
487
487
  end
488
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
+
489
501
  # Remove and get the last element in a list, or block until one is
490
502
  # available.
491
503
  def brpop(*args)
@@ -494,9 +506,9 @@ class Redis
494
506
 
495
507
  # Pop a value from a list, push it to another list and return it; or block
496
508
  # until one is available.
497
- def brpoplpush(source, destination, deprecated_timeout = 0, **options)
509
+ def brpoplpush(source, destination, **options)
498
510
  ensure_same_node(:brpoplpush, [source, destination]) do |node|
499
- node.brpoplpush(source, destination, deprecated_timeout, **options)
511
+ node.brpoplpush(source, destination, **options)
500
512
  end
501
513
  end
502
514
 
@@ -536,13 +548,23 @@ class Redis
536
548
  end
537
549
 
538
550
  # Add one or more members to a set.
539
- def sadd(key, member)
540
- node_for(key).sadd(key, member)
551
+ def sadd(key, *members)
552
+ node_for(key).sadd(key, *members)
553
+ end
554
+
555
+ # Add one or more members to a set.
556
+ def sadd?(key, *members)
557
+ node_for(key).sadd?(key, *members)
558
+ end
559
+
560
+ # Remove one or more members from a set.
561
+ def srem(key, *members)
562
+ node_for(key).srem(key, *members)
541
563
  end
542
564
 
543
565
  # Remove one or more members from a set.
544
- def srem(key, member)
545
- node_for(key).srem(key, member)
566
+ def srem?(key, *members)
567
+ node_for(key).srem?(key, *members)
546
568
  end
547
569
 
548
570
  # Remove and return a random member from a set.
@@ -589,43 +611,49 @@ class Redis
589
611
 
590
612
  # Subtract multiple sets.
591
613
  def sdiff(*keys)
614
+ keys.flatten!(1)
592
615
  ensure_same_node(:sdiff, keys) do |node|
593
- node.sdiff(*keys)
616
+ node.sdiff(keys)
594
617
  end
595
618
  end
596
619
 
597
620
  # Subtract multiple sets and store the resulting set in a key.
598
621
  def sdiffstore(destination, *keys)
599
- ensure_same_node(:sdiffstore, [destination] + keys) do |node|
600
- node.sdiffstore(destination, *keys)
622
+ keys.flatten!(1)
623
+ ensure_same_node(:sdiffstore, [destination].concat(keys)) do |node|
624
+ node.sdiffstore(destination, keys)
601
625
  end
602
626
  end
603
627
 
604
628
  # Intersect multiple sets.
605
629
  def sinter(*keys)
630
+ keys.flatten!(1)
606
631
  ensure_same_node(:sinter, keys) do |node|
607
- node.sinter(*keys)
632
+ node.sinter(keys)
608
633
  end
609
634
  end
610
635
 
611
636
  # Intersect multiple sets and store the resulting set in a key.
612
637
  def sinterstore(destination, *keys)
613
- ensure_same_node(:sinterstore, [destination] + keys) do |node|
614
- node.sinterstore(destination, *keys)
638
+ keys.flatten!(1)
639
+ ensure_same_node(:sinterstore, [destination].concat(keys)) do |node|
640
+ node.sinterstore(destination, keys)
615
641
  end
616
642
  end
617
643
 
618
644
  # Add multiple sets.
619
645
  def sunion(*keys)
646
+ keys.flatten!(1)
620
647
  ensure_same_node(:sunion, keys) do |node|
621
- node.sunion(*keys)
648
+ node.sunion(keys)
622
649
  end
623
650
  end
624
651
 
625
652
  # Add multiple sets and store the resulting set in a key.
626
653
  def sunionstore(destination, *keys)
627
- ensure_same_node(:sunionstore, [destination] + keys) do |node|
628
- node.sunionstore(destination, *keys)
654
+ keys.flatten!(1)
655
+ ensure_same_node(:sunionstore, [destination].concat(keys)) do |node|
656
+ node.sunionstore(destination, keys)
629
657
  end
630
658
  end
631
659
 
@@ -666,11 +694,19 @@ class Redis
666
694
  node_for(key).zmscore(key, *members)
667
695
  end
668
696
 
669
- # Return a range of members in a sorted set, by index.
697
+ # Return a range of members in a sorted set, by index, score or lexicographical ordering.
670
698
  def zrange(key, start, stop, **options)
671
699
  node_for(key).zrange(key, start, stop, **options)
672
700
  end
673
701
 
702
+ # Select a range of members in a sorted set, by index, score or lexicographical ordering
703
+ # and store the resulting sorted set in a new key.
704
+ def zrangestore(dest_key, src_key, start, stop, **options)
705
+ ensure_same_node(:zrangestore, [dest_key, src_key]) do |node|
706
+ node.zrangestore(dest_key, src_key, start, stop, **options)
707
+ end
708
+ end
709
+
674
710
  # Return a range of members in a sorted set, by index, with scores ordered
675
711
  # from high to low.
676
712
  def zrevrange(key, start, stop, **options)
@@ -716,26 +752,54 @@ class Redis
716
752
 
717
753
  # Get the intersection of multiple sorted sets
718
754
  def zinter(*keys, **options)
755
+ keys.flatten!(1)
719
756
  ensure_same_node(:zinter, keys) do |node|
720
- node.zinter(*keys, **options)
757
+ node.zinter(keys, **options)
721
758
  end
722
759
  end
723
760
 
724
761
  # Intersect multiple sorted sets and store the resulting sorted set in a new
725
762
  # key.
726
- def zinterstore(destination, keys, **options)
727
- ensure_same_node(:zinterstore, [destination] + keys) do |node|
763
+ def zinterstore(destination, *keys, **options)
764
+ keys.flatten!(1)
765
+ ensure_same_node(:zinterstore, [destination].concat(keys)) do |node|
728
766
  node.zinterstore(destination, keys, **options)
729
767
  end
730
768
  end
731
769
 
770
+ # Return the union of multiple sorted sets.
771
+ def zunion(*keys, **options)
772
+ keys.flatten!(1)
773
+ ensure_same_node(:zunion, keys) do |node|
774
+ node.zunion(keys, **options)
775
+ end
776
+ end
777
+
732
778
  # Add multiple sorted sets and store the resulting sorted set in a new key.
733
- def zunionstore(destination, keys, **options)
734
- ensure_same_node(:zunionstore, [destination] + keys) do |node|
779
+ def zunionstore(destination, *keys, **options)
780
+ keys.flatten!(1)
781
+ ensure_same_node(:zunionstore, [destination].concat(keys)) do |node|
735
782
  node.zunionstore(destination, keys, **options)
736
783
  end
737
784
  end
738
785
 
786
+ # Return the difference between the first and all successive input sorted sets.
787
+ def zdiff(*keys, **options)
788
+ keys.flatten!(1)
789
+ ensure_same_node(:zdiff, keys) do |node|
790
+ node.zdiff(keys, **options)
791
+ end
792
+ end
793
+
794
+ # Compute the difference between the first and all successive input sorted sets
795
+ # and store the resulting sorted set in a new key.
796
+ def zdiffstore(destination, *keys, **options)
797
+ keys.flatten!(1)
798
+ ensure_same_node(:zdiffstore, [destination] + keys) do |node|
799
+ node.zdiffstore(destination, keys, **options)
800
+ end
801
+ end
802
+
739
803
  # Get the number of fields in a hash.
740
804
  def hlen(key)
741
805
  node_for(key).hlen(key)
@@ -757,7 +821,7 @@ class Redis
757
821
  end
758
822
 
759
823
  def mapped_hmset(key, hash)
760
- node_for(key).hmset(key, *hash.to_a.flatten)
824
+ node_for(key).hmset(key, hash)
761
825
  end
762
826
 
763
827
  # Get the value of a hash field.
@@ -767,16 +831,23 @@ class Redis
767
831
 
768
832
  # Get the values of all the given hash fields.
769
833
  def hmget(key, *fields)
770
- node_for(key).hmget(key, *fields)
834
+ fields.flatten!(1)
835
+ node_for(key).hmget(key, fields)
771
836
  end
772
837
 
773
838
  def mapped_hmget(key, *fields)
774
- Hash[*fields.zip(hmget(key, *fields)).flatten]
839
+ fields.flatten!(1)
840
+ node_for(key).mapped_hmget(key, fields)
841
+ end
842
+
843
+ def hrandfield(key, count = nil, **options)
844
+ node_for(key).hrandfield(key, count, **options)
775
845
  end
776
846
 
777
847
  # Delete one or more hash fields.
778
848
  def hdel(key, *fields)
779
- node_for(key).hdel(key, *fields)
849
+ fields.flatten!(1)
850
+ node_for(key).hdel(key, fields)
780
851
  end
781
852
 
782
853
  # Determine if a hash field exists.
@@ -833,7 +904,7 @@ class Redis
833
904
 
834
905
  # Stop listening for messages posted to the given channels.
835
906
  def unsubscribe(*channels)
836
- raise "Can't unsubscribe if not subscribed." unless subscribed?
907
+ raise SubscriptionError, "Can't unsubscribe if not subscribed." unless subscribed?
837
908
 
838
909
  @subscribed_node.unsubscribe(*channels)
839
910
  end
@@ -880,9 +951,7 @@ class Redis
880
951
  def multi(&block)
881
952
  raise CannotDistribute, :multi unless @watch_key
882
953
 
883
- result = node_for(@watch_key).multi(&block)
884
- @watch_key = nil if block_given?
885
- result
954
+ node_for(@watch_key).multi(&block)
886
955
  end
887
956
 
888
957
  # Execute all commands issued after MULTI.
@@ -972,7 +1041,8 @@ class Redis
972
1041
  end
973
1042
 
974
1043
  def key_tag(key)
975
- key.to_s[@tag, 1] if @tag
1044
+ key = key.to_s
1045
+ key[@tag, 1] if key.match?(@tag)
976
1046
  end
977
1047
 
978
1048
  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 < RuntimeError
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,49 +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 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
60
+ class SubscriptionError < BaseError
87
61
  end
88
62
  end
@@ -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 = Zlib.crc32("#{node.id}:#{i}")
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 = Zlib.crc32("#{node.id}:#{i}")
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
- get_node_pos(key)[0]
47
- end
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
- _, pos = get_node_pos(key)
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 self.binary_search(ary, value)
68
- upper = ary.size - 1
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
- if comp == 0
77
- return idx
78
- elsif comp > 0
79
- upper = idx - 1
77
+ while lower < upper
78
+ mid = (lower + upper) / 2
79
+ if ary[mid] > value
80
+ upper = mid
80
81
  else
81
- lower = idx + 1
82
+ lower = mid + 1
82
83
  end
83
84
  end
84
85
 
85
- upper = ary.size - 1 if upper < 0
86
- upper
86
+ upper - 1
87
87
  end
88
88
  end
89
89
  end