or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-hashring

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

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/hashring@3.2.x

To install, run

npx @tessl/cli install tessl/npm-hashring@3.2.0

index.mddocs/

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