HashRing provides consistent hashing that is compatible with the original libketama library and Python's hash_ring module. It enables distributed systems to consistently map keys to servers in a way that minimizes redistribution when servers are added or removed from the ring.
npm install hashringconst HashRing = require('hashring');For ES6 modules (with transpilation tools like Babel):
import HashRing from 'hashring';Note: HashRing uses CommonJS exports. ES6 import syntax requires transpilation or bundling tools.
const HashRing = require('hashring');
// Create a hash ring with multiple servers
const ring = new HashRing([
'127.0.0.1:11211',
'127.0.0.2:11211',
'127.0.0.3:11211'
]);
// Find the server for a specific key
const server = ring.get('mykey');
console.log(server); // Returns server address like '127.0.0.1:11211'
// Get multiple servers for replication
const servers = ring.range('mykey', 2);
console.log(servers); // Returns array of server addresses
// Add or remove servers dynamically
ring.add('127.0.0.4:11211').remove('127.0.0.1:11211');HashRing implements consistent hashing using the ketama algorithm with the following key components:
Create a new HashRing instance with servers and optional configuration.
/**
* HashRing constructor for consistent hashing
* @param {string|Array|Object} servers - Server(s) to add to the ring
* @param {string|Function} algorithm - Hash algorithm ('md5' default) or custom hasher
* @param {Object} options - Configuration options
*/
function HashRing(servers, algorithm, options);Server Formats:
// Single server string
new HashRing('127.0.0.1:11211');
// Array of servers
new HashRing(['127.0.0.1:11211', '127.0.0.2:11211']);
// Object with weights
new HashRing({
'127.0.0.1:11211': 200,
'127.0.0.2:11211': { weight: 300 },
'127.0.0.3:11211': { vnodes: 50 }
});Configuration Options:
interface HashRingOptions {
/** Number of virtual nodes per server (default: 40) */
'vnode count'?: number;
/** Compatibility mode: 'hash_ring' for Python compatibility */
compatibility?: string;
/** Number of replicas per vnode (default: 4, or 3 for hash_ring) */
replicas?: number;
/** LRU cache size for key lookups (default: 5000) */
'max cache size'?: number;
/** Default port to exclude from hashing for ketama compatibility */
'default port'?: number;
}Find the correct server for a given key using consistent hashing.
/**
* Find the server for a key using consistent hashing
* @param {string} key - Key to find server for
* @returns {string|undefined} Server address or undefined if no servers
*/
get(key);Usage:
const server = ring.get('user:123');
// Returns: '127.0.0.1:11211'Get multiple servers for replication or load distribution.
/**
* Get multiple servers for a key, useful for replication
* @param {string} key - Key to find servers for
* @param {number} size - Number of servers to return (default: all servers)
* @param {boolean} unique - Return only unique servers (default: true)
* @returns {Array<string>} Array of server addresses
*/
range(key, size, unique);Usage:
// Get 3 servers for replication
const servers = ring.range('user:123', 3);
// Returns: ['127.0.0.1:11211', '127.0.0.2:11211', '127.0.0.3:11211']
// Get all available servers
const allServers = ring.range('user:123');Add new servers to the ring without full reinitialization.
/**
* Add servers to the ring
* @param {string|Array|Object} servers - Server(s) to add (same format as constructor)
* @returns {HashRing} Returns this for method chaining
*/
add(servers);Remove servers from the ring.
/**
* Remove a server from the ring
* @param {string} server - Server to remove
* @returns {HashRing} Returns this for method chaining
*/
remove(server);Replace one server with another without redistributing other servers.
/**
* Hot-swap one server with another
* @param {string} from - Server to replace
* @param {string} to - Replacement server
* @returns {HashRing} Returns this for method chaining
*/
swap(from, to);Usage:
// Add servers
ring.add(['127.0.0.4:11211', '127.0.0.5:11211']);
// Remove a server
ring.remove('127.0.0.1:11211');
// Hot-swap servers (preserves distribution for other servers)
ring.swap('127.0.0.2:11211', '127.0.0.6:11211');
// Method chaining
ring.add('127.0.0.7:11211').remove('127.0.0.1:11211');Check if a server exists in the ring.
/**
* Check if a server exists in the ring
* @param {string} server - Server to check for
* @returns {boolean} True if server exists in ring
*/
has(server);Get hash ring points for servers.
/**
* Get hash ring points per server
* @param {Array<string>} [servers] - Optional servers to filter (default: all servers)
* @returns {Object} Mapping of server to array of hash points
*/
points(servers);Find position of a hash value in the ring.
/**
* Find position of hash value in ring (advanced usage)
* @param {number} hashValue - Hash value to find position for
* @returns {number} Position index in the ring
*/
find(hashValue);Usage:
// Check server existence
const exists = ring.has('127.0.0.1:11211');
// Get hash points for all servers
const allPoints = ring.points();
// Returns: { '127.0.0.1:11211': [123456, 789012, ...], ... }
// Get points for specific servers
const specificPoints = ring.points(['127.0.0.1:11211']);
// Advanced: find hash position (rarely needed)
// Note: hashValue is computed internally, find() is mainly for debugging
const position = ring.find(12345); // Use a computed hash valueReset the ring and clear cache.
/**
* Reset the ring and clear cache
* @returns {HashRing} Returns this for method chaining
*/
reset();End the ring and clean up all references.
/**
* End the ring and clean up all references
* @returns {HashRing} Returns this for method chaining
*/
end();Usage:
// Clear ring but keep server configuration
ring.reset();
// Complete cleanup
ring.end();Rebuild the hash ring continuum (advanced usage).
/**
* Regenerate the hash ring continuum based on current servers
* @returns {HashRing} Returns this for method chaining
*/
continuum();Usage:
// Manually rebuild the ring (usually called automatically)
ring.continuum();Get the computed hash value for a key (advanced usage).
/**
* Get the hash value for a key (used internally for consistent hashing)
* @param {string} key - Key to compute hash for
* @returns {number} Computed hash value
*/
hashValue(key);Usage:
// Get hash value for debugging or advanced usage
const hashVal = ring.hashValue('mykey');
console.log(hashVal); // Returns numeric hash value like 3446378249
// Often used with find() for advanced ring inspection
const position = ring.find(ring.hashValue('mykey'));Access package version information.
/**
* Package version string
* @type {string}
*/
HashRing.version;Usage:
console.log(HashRing.version); // '3.2.0'/** HashRing instance properties (read-only) */
interface HashRingInstance {
/** Array of parsed server objects */
servers: Array<{
string: string;
host: string;
port: number;
weight: number;
}>;
/** Array of hash ring nodes */
ring: Array<{
value: number;
server: string;
}>;
/** Number of nodes in the ring */
size: number;
/** Server to vnode count mapping */
vnodes: { [server: string]: number };
/** Hash algorithm being used */
algorithm: string | Function;
/** Default number of virtual nodes per server */
vnode: number;
/** Number of replicas per hash */
replicas: number;
/** LRU cache instance */
cache: Object;
}HashRing handles common error scenarios gracefully:
get()undefinedadd()// Use a different crypto algorithm
const ring = new HashRing(servers, 'sha1');
// Use a custom hash function
const ring = new HashRing(servers, function customHash(key) {
// Your custom hashing logic
return crypto.createHash('sha256').update(key).digest();
});// Default ketama mode (4 replicas per vnode)
const ketamaRing = new HashRing(servers);
// Python hash_ring compatibility (3 replicas per vnode)
const pythonRing = new HashRing(servers, 'md5', {
compatibility: 'hash_ring'
});// Larger cache for high-traffic scenarios
const ring = new HashRing(servers, 'md5', {
'max cache size': 50000
});
// Custom vnode distribution for uneven server capacity
const ring = new HashRing({
'small-server:11211': { vnodes: 20 },
'medium-server:11211': { vnodes: 40 },
'large-server:11211': { vnodes: 80 }
});