consistent-hashing 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +6 -0
- data/README.md +6 -0
- data/lib/consistent_hashing.rb +1 -0
- data/lib/consistent_hashing/ring.rb +32 -7
- data/lib/consistent_hashing/virtual_point.rb +18 -0
- data/test/consistent_hashing/test_ring.rb +42 -6
- data/test/consistent_hashing/test_virtual_point.rb +18 -0
- data/version.txt +1 -1
- metadata +6 -3
data/History.txt
CHANGED
data/README.md
CHANGED
@@ -7,11 +7,14 @@ Features
|
|
7
7
|
--------
|
8
8
|
|
9
9
|
* set number of replicas to create multiple virtual points in the ring for each node
|
10
|
+
* nodes can be arbitrary data (e.g. a Memcache client instance)
|
10
11
|
|
11
12
|
Examples
|
12
13
|
--------
|
13
14
|
|
14
15
|
```ruby
|
16
|
+
require 'consistent_hashing'
|
17
|
+
|
15
18
|
ring = ConsistentHashing::Ring.new
|
16
19
|
ring << "192.168.1.101" << "192.168.1.102"
|
17
20
|
|
@@ -21,6 +24,9 @@ ring.delete("192.168.1.101")
|
|
21
24
|
# after removing 192.168.1.101, all keys previously mapped to it move clockwise to
|
22
25
|
# the next node
|
23
26
|
ring.node_for("foobar") # => 192.168.1.102
|
27
|
+
|
28
|
+
ring.nodes # => ["192.168.1.101", "192.168.1.102"]
|
29
|
+
ring.points # => [#<ConsistentHashing::VirtualPoint>, #<ConsistentHashing::VirtualPoint>, ...]
|
24
30
|
```
|
25
31
|
|
26
32
|
Install
|
data/lib/consistent_hashing.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
1
|
require 'digest/md5'
|
2
|
+
require 'set'
|
2
3
|
|
3
4
|
module ConsistentHashing
|
5
|
+
|
6
|
+
# Public: the hash ring containing all configured nodes
|
7
|
+
#
|
4
8
|
class Ring
|
5
|
-
attr_reader :ring
|
6
9
|
|
7
10
|
# Public: returns a new ring object
|
8
11
|
def initialize(nodes = [], replicas = 3)
|
@@ -27,7 +30,7 @@ module ConsistentHashing
|
|
27
30
|
# generate the key of this (virtual) point in the hash
|
28
31
|
key = hash_key(node, i)
|
29
32
|
|
30
|
-
@ring[key] = node
|
33
|
+
@ring[key] = VirtualPoint.new(node, key)
|
31
34
|
@sorted_keys << key
|
32
35
|
end
|
33
36
|
|
@@ -50,19 +53,41 @@ module ConsistentHashing
|
|
50
53
|
self
|
51
54
|
end
|
52
55
|
|
53
|
-
# Public: gets the
|
56
|
+
# Public: gets the point for an arbitrary key
|
54
57
|
#
|
55
58
|
#
|
56
|
-
def
|
57
|
-
return
|
59
|
+
def point_for(key)
|
60
|
+
return nil if @ring.empty?
|
58
61
|
|
59
62
|
key = hash_key(key)
|
60
63
|
|
61
64
|
@sorted_keys.each do |i|
|
62
|
-
return
|
65
|
+
return @ring[i] if key <= i
|
63
66
|
end
|
64
67
|
|
65
|
-
|
68
|
+
@ring[@sorted_keys[0]]
|
69
|
+
end
|
70
|
+
|
71
|
+
# Public: gets the node where to store the key
|
72
|
+
#
|
73
|
+
# Returns: the node Object
|
74
|
+
def node_for(key)
|
75
|
+
point_for(key).node
|
76
|
+
end
|
77
|
+
|
78
|
+
# Public: get all nodes in the ring
|
79
|
+
#
|
80
|
+
# Returns: an Array of the nodes in the ring
|
81
|
+
def nodes
|
82
|
+
nodes = points.map { |point| point.node }
|
83
|
+
nodes.uniq
|
84
|
+
end
|
85
|
+
|
86
|
+
# Public: gets all points in the ring
|
87
|
+
#
|
88
|
+
# Returns: an Array of the points in the ring
|
89
|
+
def points
|
90
|
+
@ring.map { |point| point[1] }
|
66
91
|
end
|
67
92
|
|
68
93
|
protected
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ConsistentHashing
|
2
|
+
|
3
|
+
# Public: represents a virtual point on the hash ring
|
4
|
+
#
|
5
|
+
class VirtualPoint
|
6
|
+
attr_reader :node, :index
|
7
|
+
|
8
|
+
def initialize(node, index)
|
9
|
+
@node = node
|
10
|
+
@index = index.to_i
|
11
|
+
end
|
12
|
+
|
13
|
+
# Public: set a new index for the virtual point. Useful if the point gets duplicated
|
14
|
+
def index=(index)
|
15
|
+
@index = index.to_i
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -26,23 +26,59 @@ class TestRing < ConsistentHashing::TestCase
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def test_get_node
|
29
|
-
assert_equal "A", @ring.
|
30
|
-
assert_equal "B", @ring.
|
31
|
-
assert_equal "C", @ring.
|
29
|
+
assert_equal "A", @ring.point_for(@examples["A"]).node
|
30
|
+
assert_equal "B", @ring.point_for(@examples["B"]).node
|
31
|
+
assert_equal "C", @ring.point_for(@examples["C"]).node
|
32
32
|
end
|
33
33
|
|
34
34
|
# should fall back to the first node, if key > last node
|
35
35
|
def test_get_node_fallback_to_first
|
36
36
|
ring = ConsistentHashing::Ring.new ["A"], 1
|
37
37
|
|
38
|
-
|
39
|
-
|
38
|
+
point = ring.point_for(@examples["not_found"])
|
39
|
+
|
40
|
+
assert_equal "A", point.node
|
41
|
+
assert_not_equal 0, point.index
|
40
42
|
end
|
41
43
|
|
42
44
|
# if I remove node C, all keys previously mapped to C should be moved clockwise to
|
43
45
|
# the next node. That's a virtual point of B here
|
44
46
|
def test_remove_node
|
45
47
|
@ring.delete("C")
|
46
|
-
assert_equal "B", @ring.
|
48
|
+
assert_equal "B", @ring.point_for(@examples["C"]).node
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_point_for
|
52
|
+
assert_equal "C", @ring.node_for(@examples["C"])
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_arbitrary_node_data
|
56
|
+
nodes = [
|
57
|
+
{"host" => "192.168.1.101"},
|
58
|
+
{"host" => "192.168.1.102"},
|
59
|
+
{"host" => "192.168.1.103"}
|
60
|
+
]
|
61
|
+
|
62
|
+
ring = ConsistentHashing::Ring.new(nodes)
|
63
|
+
assert_equal 9, ring.length
|
64
|
+
|
65
|
+
ring_nodes = ring.nodes
|
66
|
+
assert_equal 3, ring_nodes.length
|
67
|
+
assert_equal "192.168.1.102", ring_nodes[1]['host']
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_points
|
71
|
+
nodes = [
|
72
|
+
{"host" => "192.168.1.101"},
|
73
|
+
{"host" => "192.168.1.102"},
|
74
|
+
{"host" => "192.168.1.103"}
|
75
|
+
]
|
76
|
+
|
77
|
+
ring = ConsistentHashing::Ring.new(nodes)
|
78
|
+
points = ring.points
|
79
|
+
|
80
|
+
assert_equal 9, points.length
|
81
|
+
assert_not_equal 0, points[0].index
|
82
|
+
assert_equal "192.168.1.101", points[0].node['host']
|
47
83
|
end
|
48
84
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w{ .. test_consistent_hashing})
|
2
|
+
|
3
|
+
class TestVirtualPoint < ConsistentHashing::TestCase
|
4
|
+
def setup
|
5
|
+
@node = {'host' => '192.168.1.101'}
|
6
|
+
@point = ConsistentHashing::VirtualPoint.new(@node, "1")
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_init
|
10
|
+
assert_equal @node, @point.node
|
11
|
+
assert_equal 1, @point.index
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_set_index
|
15
|
+
@point.index = "2"
|
16
|
+
assert_equal 2, @point.index
|
17
|
+
end
|
18
|
+
end
|
data/version.txt
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0
|
1
|
+
0.1.0
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: consistent-hashing
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ date: 2012-04-15 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bones
|
16
|
-
requirement: &
|
16
|
+
requirement: &70147185310840 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,7 +21,7 @@ dependencies:
|
|
21
21
|
version: 3.8.0
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70147185310840
|
25
25
|
description: A generic implementation of the Consistent Hashing algorithm.
|
26
26
|
email: liebler.dominik@googlemail.com
|
27
27
|
executables: []
|
@@ -35,7 +35,9 @@ files:
|
|
35
35
|
- Rakefile
|
36
36
|
- lib/consistent_hashing.rb
|
37
37
|
- lib/consistent_hashing/ring.rb
|
38
|
+
- lib/consistent_hashing/virtual_point.rb
|
38
39
|
- test/consistent_hashing/test_ring.rb
|
40
|
+
- test/consistent_hashing/test_virtual_point.rb
|
39
41
|
- test/test_consistent_hashing.rb
|
40
42
|
- version.txt
|
41
43
|
homepage: https://github.com/domnikl/consistent-hashing
|
@@ -66,4 +68,5 @@ specification_version: 3
|
|
66
68
|
summary: A generic implementation of the Consistent Hashing algorithm.
|
67
69
|
test_files:
|
68
70
|
- test/consistent_hashing/test_ring.rb
|
71
|
+
- test/consistent_hashing/test_virtual_point.rb
|
69
72
|
- test/test_consistent_hashing.rb
|