consistent-hashing 0.0.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.
@@ -0,0 +1,9 @@
1
+ # RubyMine
2
+ .idea
3
+ *.gemspec
4
+
5
+ # gem/bones
6
+ announcement.txt
7
+ coverage
8
+ doc
9
+ pkg
@@ -0,0 +1,3 @@
1
+ == 0.0.1 / 2012-04-15
2
+
3
+ * Birthday!
@@ -0,0 +1,60 @@
1
+ consistent-hashing
2
+ ==================
3
+
4
+ A generic implementation of the Consistent Hashing algorithm.
5
+
6
+ Features
7
+ --------
8
+
9
+ * set number of replicas to create multiple virtual points in the ring for each node
10
+
11
+ Examples
12
+ --------
13
+
14
+ ```ruby
15
+ ring = ConsistentHashing::Ring.new
16
+ ring << "192.168.1.101" << "192.168.1.102"
17
+
18
+ ring.node_for("foobar") # => 192.168.1.101
19
+ ring.delete("192.168.1.101")
20
+
21
+ # after removing 192.168.1.101, all keys previously mapped to it move clockwise to
22
+ # the next node
23
+ ring.node_for("foobar") # => 192.168.1.102
24
+ ```
25
+
26
+ Install
27
+ -------
28
+
29
+ * `[sudo] gem install consistent-hashing`
30
+
31
+ Author
32
+ ------
33
+
34
+ Original author: Dominik Liebler <liebler.dominik@googlemail.com>
35
+
36
+ License
37
+ -------
38
+
39
+ (The MIT License)
40
+
41
+ Copyright (c) 2012 Dominik Liebler
42
+
43
+ Permission is hereby granted, free of charge, to any person obtaining
44
+ a copy of this software and associated documentation files (the
45
+ 'Software'), to deal in the Software without restriction, including
46
+ without limitation the rights to use, copy, modify, merge, publish,
47
+ distribute, sublicense, and/or sell copies of the Software, and to
48
+ permit persons to whom the Software is furnished to do so, subject to
49
+ the following conditions:
50
+
51
+ The above copyright notice and this permission notice shall be
52
+ included in all copies or substantial portions of the Software.
53
+
54
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
55
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
56
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
57
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
58
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
59
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
60
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+
3
+ begin
4
+ require 'bones'
5
+ rescue LoadError
6
+ abort '### Please install the "bones" gem ###'
7
+ end
8
+
9
+ task :default => 'test:run'
10
+ task 'gem:release' => 'test:run'
11
+
12
+ Bones {
13
+ name 'consistent-hashing'
14
+ authors 'Dominik Liebler'
15
+ email 'liebler.dominik@googlemail.com'
16
+ url 'https://github.com/domnikl/consistent-hashing'
17
+ ignore_file '.gitignore'
18
+ }
@@ -0,0 +1,15 @@
1
+ # Main module
2
+ #
3
+ module ConsistentHashing
4
+ LIBPATH = ::File.expand_path('..', __FILE__) + ::File::SEPARATOR
5
+ PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
6
+ VERSION = ::File.read(PATH + 'version.txt').strip
7
+
8
+ # Internal: loads all necessary lib files
9
+ #
10
+ def self.load_lib
11
+ require File.join(LIBPATH, 'consistent_hashing', 'ring')
12
+ end
13
+ end
14
+
15
+ ConsistentHashing.load_lib
@@ -0,0 +1,78 @@
1
+ require 'digest/md5'
2
+
3
+ module ConsistentHashing
4
+ class Ring
5
+ attr_reader :ring
6
+
7
+ # Public: returns a new ring object
8
+ def initialize(nodes = [], replicas = 3)
9
+ @replicas = replicas
10
+ @sorted_keys = []
11
+ @ring = Hash.new
12
+
13
+ nodes.each { |node| add(node) }
14
+ end
15
+
16
+ # Public: returns the (virtual) points in the hash ring
17
+ #
18
+ # Returns: a Fixnum
19
+ def length
20
+ @ring.length
21
+ end
22
+
23
+ # Public: adds a new node into the hash ring
24
+ #
25
+ def add(node)
26
+ @replicas.times do |i|
27
+ # generate the key of this (virtual) point in the hash
28
+ key = hash_key(node, i)
29
+
30
+ @ring[key] = node
31
+ @sorted_keys << key
32
+ end
33
+
34
+ @sorted_keys.sort!
35
+
36
+ self
37
+ end
38
+ alias :<< :add
39
+
40
+ # Public: removes a node from the hash ring
41
+ #
42
+ def delete(node)
43
+ @replicas.times do |i|
44
+ key = hash_key(node, i)
45
+
46
+ @ring.delete key
47
+ @sorted_keys.delete key
48
+ end
49
+
50
+ self
51
+ end
52
+
53
+ # Public: gets the node for an arbitrary key
54
+ #
55
+ #
56
+ def node_for(key)
57
+ return [nil, 0] if @ring.empty?
58
+
59
+ key = hash_key(key)
60
+
61
+ @sorted_keys.each do |i|
62
+ return [@ring[i], i] if key <= i
63
+ end
64
+
65
+ [@ring[@sorted_keys[0]], 0]
66
+ end
67
+
68
+ protected
69
+
70
+ # Internal: hashes the key
71
+ #
72
+ # Returns: a String
73
+ def hash_key(key, index = nil)
74
+ key = "#{key}:#{index}" if index
75
+ Digest::MD5.hexdigest(key.to_s)[0..16].hex
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,48 @@
1
+ require File.join(File.dirname(__FILE__), %w{ .. test_consistent_hashing})
2
+
3
+ class TestRing < ConsistentHashing::TestCase
4
+ def setup
5
+ @ring = ConsistentHashing::Ring.new %w{A B C}
6
+
7
+ @examples = {
8
+ "A" => "foobar",
9
+ "B" => "123",
10
+ "C" => "baz",
11
+ "not_found" => 0
12
+ }
13
+ end
14
+
15
+ def test_init
16
+ ring = ConsistentHashing::Ring.new
17
+ assert_equal 0, ring.length
18
+ end
19
+
20
+ def test_length
21
+ ring = ConsistentHashing::Ring.new([], 3)
22
+ assert_equal 0, ring.length
23
+
24
+ ring << "A" << "B"
25
+ assert_equal 6, ring.length
26
+ end
27
+
28
+ def test_get_node
29
+ assert_equal "A", @ring.node_for(@examples["A"])[0]
30
+ assert_equal "B", @ring.node_for(@examples["B"])[0]
31
+ assert_equal "C", @ring.node_for(@examples["C"])[0]
32
+ end
33
+
34
+ # should fall back to the first node, if key > last node
35
+ def test_get_node_fallback_to_first
36
+ ring = ConsistentHashing::Ring.new ["A"], 1
37
+
38
+ assert_equal "A", ring.node_for(@examples["not_found"])[0]
39
+ assert_equal 0, ring.node_for(@examples["not_found"])[1]
40
+ end
41
+
42
+ # if I remove node C, all keys previously mapped to C should be moved clockwise to
43
+ # the next node. That's a virtual point of B here
44
+ def test_remove_node
45
+ @ring.delete("C")
46
+ assert_equal "B", @ring.node_for(@examples["C"])[0]
47
+ end
48
+ end
@@ -0,0 +1,15 @@
1
+ $:.unshift(File.join(File.dirname(__FILE__), %w{.. lib}))
2
+
3
+ require 'simplecov'
4
+ SimpleCov.start
5
+
6
+ require 'consistent_hashing'
7
+ require 'test/unit'
8
+
9
+ module ConsistentHashing
10
+ class TestCase < Test::Unit::TestCase
11
+ def test_module
12
+ assert_not_nil ConsistentHashing::VERSION
13
+ end
14
+ end
15
+ end
@@ -0,0 +1 @@
1
+ 0.0.1
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: consistent-hashing
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Dominik Liebler
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-04-15 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bones
16
+ requirement: &70330988436160 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 3.8.0
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70330988436160
25
+ description: A generic implementation of the Consistent Hashing algorithm.
26
+ email: liebler.dominik@googlemail.com
27
+ executables: []
28
+ extensions: []
29
+ extra_rdoc_files:
30
+ - History.txt
31
+ files:
32
+ - .gitignore
33
+ - History.txt
34
+ - README.md
35
+ - Rakefile
36
+ - lib/consistent_hashing.rb
37
+ - lib/consistent_hashing/ring.rb
38
+ - test/consistent_hashing/test_ring.rb
39
+ - test/test_consistent_hashing.rb
40
+ - version.txt
41
+ homepage: https://github.com/domnikl/consistent-hashing
42
+ licenses: []
43
+ post_install_message:
44
+ rdoc_options:
45
+ - --main
46
+ - README.md
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ! '>='
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ requirements: []
62
+ rubyforge_project: consistent-hashing
63
+ rubygems_version: 1.8.16
64
+ signing_key:
65
+ specification_version: 3
66
+ summary: A generic implementation of the Consistent Hashing algorithm.
67
+ test_files:
68
+ - test/consistent_hashing/test_ring.rb
69
+ - test/test_consistent_hashing.rb