CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-hashring

A consistent hashring compatible with ketama hashing and python's hash_ring

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

HashRing

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.

Package Information

  • Package Name: hashring
  • Package Type: npm
  • Language: JavaScript
  • Installation: npm install hashring

Core Imports

const 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.

Basic Usage

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');

Architecture

HashRing implements consistent hashing using the ketama algorithm with the following key components:

  • Hash Ring: Circular hash space where servers are mapped to multiple points (virtual nodes)
  • Virtual Nodes (vnodes): Multiple hash points per server for better distribution (default: 40)
  • Replicas: Hash ring points per vnode for redundancy (default: 4, or 3 for hash_ring compatibility)
  • LRU Cache: Performance optimization for frequent key lookups (default: 5000 entries)
  • Connection Parsing: Flexible server specification with weights and custom vnodes

Capabilities

Constructor

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;
}

Key-to-Server Mapping

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'

Range Queries

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');

Server Management

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');

Ring Inspection

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 value

Lifecycle Management

Reset 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();

Hash Ring Management

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'));

Static Properties

Access package version information.

/**
 * Package version string
 * @type {string}
 */
HashRing.version;

Usage:

console.log(HashRing.version); // '3.2.0'

Types

/** 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;
}

Error Handling

HashRing handles common error scenarios gracefully:

  • Empty ring: get() returns undefined when no servers are configured
  • Invalid servers: Malformed server strings are parsed with sensible defaults
  • Zero replicas: Automatically normalized to 1 to prevent infinite loops
  • Duplicate servers: add() ignores servers that already exist in the ring

Advanced Usage

Custom Hash Algorithms

// 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();
});

Ketama vs Python hash_ring Compatibility

// 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'
});

Performance Optimization

// 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 }
});
Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/hashring@3.2.x
Publish Source
CLI
Badge
tessl/npm-hashring badge