redis-shard-api
Version:
A pure Javascript port version of Python's RedisShardAPI
134 lines (114 loc) • 3.17 kB
JavaScript
var hashValue = require('../lib/HashValue');
var format = require('util').format;
/**
* Manages a hash ring.
*
* `nodes` is a list of object that have a toString representation.
* `replicas` indicates how many virtual points should be used per node.
*
* @param nodes
* @param replicas
* @constructor
*/
module.exports = HashRing;
function HashRing(nodes, options){
this.nodes = nodes || [];
this.options = options || (options = {replicas:128, algorithm:"crc32"});
this.replicas = options.replicas || 128;
this.algorithm = options.algorithm || "crc32";
this.ring = {};
this.ringCnt = 0;
this.sorted_keys = [];
//initialize
for (var n = 0; n < this.nodes.length; n++){
var node = this.nodes[n];
for(var i = 0; i < this.replicas; i++){
var nodeKey = format("%s:%d", node, i);
var crckey = hashValue.hash(nodeKey, this.algorithm);
this.ring[crckey] = node;
this.sorted_keys.push(crckey);
this.ringCnt++;
}
}
this.sorted_keys.sort(function(a,b){return a-b});
return this;
}
var HashRingProto = HashRing.prototype;
/** Adds a `node` to the hash ring(including a number of replicas.) */
HashRingProto.addNode = function(node){
var self = this;
self.nodes.push(node);
for(var i = 0; i < this.replicas; i++){
var crc = hashValue.hash(format("%s:%d", node, i), this.algorithm);
self.ring[crc] = node;
self.ringCnt++;
self.sorted_keys.push(crc);
}
self.sorted_keys.sort();
};
/** Removes `node` from the hash ring and its replicas. */
HashRingProto.removeNode = function(node){
var self = this;
var idx = self.nodes.indexOf(node);
self.nodes.splice(idx, 1); //remove
for (var i = 0; i < self.replicas; i++){
var crc = hashValue.hash(format("%s:%d", node, i), self.algorithm);
idx = self.sorted_keys.indexOf(crc);
delete self.ring[crc];
self.ringCnt--;
self.sorted_keys.splice(idx, 1); //remove
}
};
/**
* Given a string key a corresponding node in the hash ring is returned.
* If the hash ring is empty. undifined is returned.
*/
HashRingProto.getNode = function(key){
var nodeName = this.getNodePos(key)[0];
return nodeName;
};
/**
* Given a string key a corresponding node in the hash ring is returned
* along with it's position in the ring.
* @param key
* @returns {Array}
* TODO check it!
*/
HashRingProto.getNodePos = function(key){
var self = this;
if(self.ringCnt == 0)
return [null, null];
var crc = hashValue.hash(key, self.algorithm);
var idx = bisect(self.sorted_keys, crc);
idx = Math.min(idx, (self.replicas * self.nodes.length) - 1);
return [self.ring[self.sorted_keys[idx]], idx];
};
/**
* TODO implement iterator function
*/
HashRingProto.iterNodes = function(key){
};
bisect = function(a, x, lo, hi) {
lo || (lo = 0);
hi || (hi = a.length);
var mid;
while(lo < hi)
{
mid = (lo+hi) >> 1;
if(x < a[mid]) hi = mid;
else lo = mid+1;
}
return lo;
};
bisect_left = function(a, x, lo, hi) {
lo || (lo = 0);
hi || (hi = a.length);
var mid;
while(lo < hi)
{
mid = (lo+hi) >> 1;
if(a[mid] < x) lo = mid+1;
else hi = mid;
}
return lo;
};