A hash ring implementation using libketama-like hashing.
npm install ketama
Ketama is a JavaScript/TypeScript implementation of the libketama hash ring. The primary advantage of ketama-style hashing is that it reduces the amount of work which is shuffled to other servers when the hashring membership changes.
npm install --save ketama
Then go forth with confidence:
``js
// alternatively: const { HashRing } = require('ketama');
import { HashRing } from 'ketama';
const ring = new HashRing();
ring.addNode('127.0.0.1'); // add with default weight
ring.addNode('127.0.0.2', 2); // weight of 2 = twice as likely to choose this one
export function onDoWork(input) {
// hashes the input and choses a server based on it:
const server = ring.getNode(input);
doWorkOn(server);
}
export function doWorkOnMultipleServers(input) {
// picks two "replica" servers from the ring to do work on
for (const server of ring.getNodes(input, 2)) {
doWorkOn(server);
}
}
`
- [HashRing(\[nodes\], \[hashFunction\])](#hashringnodes-hashfunction)
- [hashRing.addNode(node\[, weight\])](#hashringaddnodenode-weight)
- hashRing.removeNode(node)
- hashRing.getNode(input)
- hashRing.getNodes(input, replicas)
Creates a new HashRing, optionally with the initial set of nodes. The nodes can be _either_ a string, or an object with a string property "key" which will be used internally to hash against, for example:
`js`
new HashRing(['127.0.0.1', { key: '127.0.0.2', port: 1337 }]);
When giving the nodes in the constructor, you can specify a weight by providing an object with the node and its weight. Weights are proportional; a node with a weight of 2 is twice as likely to be chosen than one with the default weight of 1. For example:
`js`
new HashRing([
{ node: '127.0.0.1', weight: 2 },
{ node: { key: '127.0.0.2', port: 1337 }, weight: 3
]);
Finally, you can also specify the hashing function to use. This can be either the name of an algorithm the require('crypto').createHash implements, or a custom function that returns an int32 given a Buffer. Defaults to sha1. For example:
`js`
new HashRing([], 'md5'); // hash with md5 instead
new HashRing([], buf => myCustomHash().readInt32BE()); // custom function
Adds a new node to the existing hashring. The node can be _either_ a string, or an object with a string property "key" which will be used internally to hash against.
`js`
ring.addNode('127.0.0.1');
ring.addNode({ key: '127.0.0.2', port: 1337 });
You can optionally specify a weight as the second argument. Weights are proportional; a node with a weight of 2 is twice as likely to be chosen than one with the default weight of 1.
If the node already exists, it is replaced; for example, you can call addNode multiple times to update a node's weight.
Removes a node previously added to the hash ring.
`js`
ring.removeNode('127.0.0.1');
No-op if the node is not in the ring.
Gets the node on which work should be done for the given input, which should be either a string or buffer. Returns the object or string originally given to addNode or new HashRing(), or returns undefined if the ring is empty.
`js`
const server = ring.getNode(inputData);
doWorkOn(server);
Gets the "replicas" number of nodes that should handle the input, which should be either a string or buffer. The returned array length wiill equal the number of replicas, except if there are fewer nodes available than replicas requested, in which case all nodes are returned.
`js``
for (const server of ring.getNodes(input, 2)) {
doWorkOn(server);
}