Skip to content
Browse files

first commit

  • Loading branch information...
0 parents commit a8dfe9c94e7f4216edc19d11a56e7a50e6c96d16 @dakatsuka committed Aug 14, 2011
Showing with 253 additions and 0 deletions.
  1. +1 −0 .gitignore
  2. +19 −0 LICENSE
  3. +7 −0 README.md
  4. +22 −0 examples/app.js
  5. +1 −0 index.js
  6. +111 −0 lib/consistent_hashing.js
  7. +1 −0 lib/index.js
  8. +24 −0 package.json
  9. +67 −0 test/consistent_hashing.test.js
1 .gitignore
@@ -0,0 +1 @@
+node_modules
19 LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2011 Dai Akatsuka
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
7 README.md
@@ -0,0 +1,7 @@
+# node-consistent-hashing
+
+This module is Consistent Hashing for Node.js
+
+## About
+
+A pure JavaScript implementation of Consistent Hashing for Node.js.
22 examples/app.js
@@ -0,0 +1,22 @@
+var ConsistentHashing = require('..');
+var cons = new ConsistentHashing(["node1", "node2", "node3", "node4", "node5"]);
+
+var nodes = {};
+var chars = [
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
+ 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
+ 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
+];
+
+chars.forEach(function(c) {
+ var node = cons.getNode(c);
+
+ if (nodes[node]) {
+ nodes[node].push(c);
+ } else {
+ nodes[node] = [];
+ nodes[node].push(c);
+ }
+});
+
+console.log(nodes);
1 index.js
@@ -0,0 +1 @@
+module.exports = require('./lib');
111 lib/consistent_hashing.js
@@ -0,0 +1,111 @@
+var crypto = require('crypto');
+
+
+var ConsistentHashing = function(nodes, options) {
+ this.replicas = 160;
+ this.algorithm = 'md5'
+ this.ring = {};
+ this.keys = [];
+ this.nodes = [];
+
+ if (options && options.replicas) this.replicas = options.replicas;
+ if (options && options.algorithm) this.algorithm = options.algorithm;
+
+ for (var i = 0; i < nodes.length; i++) {
+ this.addNode(nodes[i]);
+ }
+};
+
+
+ConsistentHashing.prototype.addNode = function(node) {
+ this.nodes.push(node);
+
+ for (var i = 0; i < this.replicas; i++) {
+ var key = this.crypto((node.id || node) + ':' + i);
+
+ this.keys.push(key);
+ this.ring[key] = node;
+ }
+
+ this.keys.sort();
+};
+
+
+ConsistentHashing.prototype.removeNode = function(node) {
+ for (var i = 0; i < this.nodes.length; i++) {
+ if (this.nodes[i] == node) {
+ this.nodes.splice(i, 1);
+ i--;
+ }
+ }
+
+ for (var i = 0; i < this.replicas; i++) {
+ var key = this.crypto((node.id || node) + ':' + i);
+ delete this.ring[key];
+
+ for (var j = 0; j < this.keys.length; j++) {
+ if (this.keys[j] == key) {
+ this.keys.splice(j, 1);
+ j--;
+ }
+ }
+ }
+};
+
+
+ConsistentHashing.prototype.getNode = function(key) {
+ if (this.getRingLength() == 0) return 0;
+
+ var hash = this.crypto(key);
+ var pos = this.getNodePosition(hash);
+
+ return this.ring[this.keys[pos]];
+};
+
+
+ConsistentHashing.prototype.getNodePosition = function(hash) {
+ var upper = this.getRingLength();
+ var lower = 0;
+ var idx = 0;
+
+ if (upper == 0) return 0;
+
+ while (lower <= upper) {
+ idx = Math.floor((lower + upper) / 2);
+ comp = this.compare(this.keys[idx], hash);
+
+ if (comp == 0) {
+ return idx;
+ } else if (comp > 0) {
+ upper = idx - 1;
+ } else {
+ lower = idx + 1;
+ }
+ }
+
+ if (upper < 0) {
+ upper = this.getRingLength();
+ }
+
+ return upper;
+};
+
+
+ConsistentHashing.prototype.getRingLength = function() {
+ var i = 0;
+ for (var key in this.ring) { i++ }
+ return i;
+};
+
+
+ConsistentHashing.prototype.compare = function(v1, v2) {
+ return v1 > v2 ? 1 : v1 < v2 ? -1 : 0;
+};
+
+
+ConsistentHashing.prototype.crypto = function(str) {
+ return crypto.createHash(this.algorithm).update(str).digest('hex');
+};
+
+
+module.exports = ConsistentHashing;
1 lib/index.js
@@ -0,0 +1 @@
+module.exports = require('./consistent_hashing');
24 package.json
@@ -0,0 +1,24 @@
+{
+ "name": "consistent-hashing",
+ "version": "0.1.0",
+ "description": "A pure JavaScript implementation of Consistent Hashing",
+ "author": "Dai Akatsuka <d.akatsuka@gmail.com>",
+ "main": "./index.js",
+ "scripts": {
+ "test": "nodeunit ./test/consistent_hashing.test.js"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/dakatsuka/node-consistent-hashing.git"
+ },
+ "directories": { "lib": "./lib" },
+ "bugs": {
+ "web": "http://github.com/dakatsuka/node-cosistent-hashing/issues"
+ },
+ "licenses": [
+ {
+ "type": "MIT",
+ "url": "http://github.com/dakatsuka/node-consistent-hashing/raw/master/LICENSE"
+ }
+ ]
+}
67 test/consistent_hashing.test.js
@@ -0,0 +1,67 @@
+var testCase = require('nodeunit').testCase,
+ ConsistentHashing = require('..');
+
+var nodes = ["node1", "node2", "node3"];
+
+module.exports = testCase({
+ setUp: function(callback) {
+ Array.prototype.contains = function(value) {
+ for (var i in this) {
+ if (this.hasOwnProperty(i) && this[i] == value) {
+ return true;
+ }
+ }
+ return false;
+ };
+ callback();
+ },
+
+ 'should be set default values': function(test) {
+ var cons = new ConsistentHashing(nodes);
+ test.equal(cons.replicas, 160);
+ test.equal(cons.algorithm, 'md5');
+ test.done();
+ },
+
+ 'should be able to change value of replicas': function(test) {
+ var cons = new ConsistentHashing(nodes, { replicas: 300 });
+ test.equal(cons.replicas, 300);
+ test.done();
+ },
+
+ 'should be able to change algorithm': function(test) {
+ var cons = new ConsistentHashing(nodes, { algorithm: 'sha1' });
+ test.equal(cons.algorithm, 'sha1');
+ test.done();
+ },
+
+ 'should add nodes': function(test) {
+ var cons = new ConsistentHashing(nodes);
+ cons.addNode("node4");
+ test.equal(cons.nodes.contains("node4"), true);
+ test.equal(cons.keys.length, cons.replicas * 4);
+ test.done();
+ },
+
+ 'should remove node': function(test) {
+ var cons = new ConsistentHashing(nodes);
+ cons.removeNode("node1");
+ test.equal(cons.nodes.contains("node1"), false);
+ test.equal(cons.keys.length, cons.replicas * 2);
+ test.done();
+ },
+
+ 'should create an array for sort': function(test) {
+ var cons = new ConsistentHashing(nodes);
+ test.equal(cons.keys.length, cons.replicas * nodes.length);
+ test.done();
+ },
+
+ 'should get a node from key': function(test) {
+ var cons = new ConsistentHashing(nodes);
+ test.equal(cons.getNode('A'), "node3");
+ test.equal(cons.getNode('B'), "node1");
+ test.equal(cons.getNode('C'), "node1");
+ test.done();
+ }
+});

0 comments on commit a8dfe9c

Please sign in to comment.
Something went wrong with that request. Please try again.