Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

fash

Package Overview
Dependencies
Maintainers
1
Versions
27
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

fash

A consistent hashing library for node

  • 1.1.1
  • Source
  • npm
  • Socket score

Version published
Weekly downloads
1
decreased by-83.33%
Maintainers
1
Weekly downloads
 
Created
Source

fash: consistent hashing library for node.js

This module provides a consistent hashing library. Notably, this module the ability to deterministically generate the same hash ring topology across a set of distributed hosts. Fash also handles collisions of nodes on the ring and ensures no two nodes will share the same spot on the ring. Additionally, fash provides the ability to add, remove, or remap physical nodes on the ring -- useful when a particular physical node has hit its scaling bottleneck.

Design

Fash consists of a mapping of a set of fixed virtual nodes (vnodes) -- usually a large number, say 1000000 -- distributed across the hash ring. It is then possible to map these virtual nodes to a set of physical nodes (pnodes). In practice, pnodes are usuall physical shards or servers in a distributed system. This gives the flexibility of mutating the hashspace of pnodes and the number of pnodes by re-mapping the vnode assignments.

Example

Boostrapping a New Hash Ring

var fash = require('fash');
var Logger = require('bunyan');

var LOG = new Logger({
    name: 'fash',
    level: 'info'
});

var chash = fash.create({
    log: LOG, // optional [bunyan](https://github.com/trentm/node-bunyan) log object.
    algorithm: fash.ALGORITHMS.SHA256, // Can be any algorithm supported by openssl.
    pnodes: ['A', 'B', 'C', 'D', 'E'], // The set of physical nodes to insert into the ring.
    vnodes: 1000000 // The virtual nodes to place onto the ring. Once set, this can't be changed for the lifetime of the ring.
});

var node = chash.getNode('someKeyToHash');
console.log('key hashes to pnode', node);

If the config used to bootstrap fash is the same across all clients, then the ring toplogy will be the same as well. By default, fash will evenly distribute vnodes across the set of pnodes. If you wish to have a custom mapping of pnodes to vnodes, see the later section on serialization.

Remapping Pnodes in the Ring

Fash gives you the ability to add and rebalance the pnodes in the ring by using the remapNode() function, which will emit an 'update' event when done, 'error' event on error, and also return an optional callback.

You can also remove pnodes from the ring, but you must first rebalance the ring by reassigning its vnodes to other pnodes via remapVnode(). Then you can invoke removeNode(), which will emit an 'update' event when done, an 'error' even on error, and also return an optional callback.

You can assign an arbitrary number of vnodes to the new vnode -- also -- the pnode can be a new node, or an existing one. Again, as long as the order of removes and remaps is consistent across all clients, the ring toplogy will be consistent as well.

var fash = require('fash');
var Logger = require('bunyan');

var LOG = new Logger({
    name: 'fash',
    level: 'info'
});

var chash = fash.create({
    log: LOG,
    algorithm: fash.ALGORITHMS.SHA256,
    pnodes: ['A', 'B', 'C', 'D', 'E'],
    vnodes: 100000
});

// get vnodes from A
var aVnodes = chash.getVnodes('A');
aVnodes = aVnodes.slice(aVnodes.length / 2);

// remap some of A's vnodes to B
chash.remapVnode('B', aVnodes, function(err, ring, pnodes) {
    console.log('new ring topology', ring);
    console.log('changed pnode->vnode mappings', pnodes);
});

Adding More Pnodes to the Ring

You can add additional pnodes to the ring after fash has been initialized by invoking remapVnode(). Which will emit an 'update' event when done, 'error' event if there's an error, and also optionally returns a callback.

var fash = require('fash');
var Logger = require('bunyan');

var LOG = new Logger({
    name: 'fash',
    level: 'info'
});

var chash = fash.create({
    log: LOG,
    algorithm: fash.ALGORITHMS.SHA256,
    pnodes: ['A', 'B', 'C', 'D', 'E'],
    vnodes: 100000
});
// specify the set of virtual nodes to assign to the new physical node.
var vnodes = [0, 1, 2, 3, 4];

/* wait for an update event, returns the current topology of the ring, as
 * well as a map of the removed pnode->vnode[] mappings.
 */
chash.once('update', function(ring, removedMapping) {
    console.log('ring topology updated', ring);
    console.log('removed mappings', removedMapping);
});

// add the physical node 'F' to the ring.
chash.remapVnode('F', vnodes);

Fash will remove the vnodes from their previously mapped physical nodes, and map them to the new pnode.

Removing Pnodes from the Ring

You can remove physical nodes from the ring by first remapping the pnode's vnodes to another pnode, and then removing the pnode.

var fash = require('fash');
var Logger = require('bunyan');

var LOG = new Logger({
  name: 'fash',
  level: 'info'
});

var chash = fash.create({
        log: LOG,
        algorithm: fash.ALGORITHMS.SHA256,
        pnodes: ['A', 'B', 'C', 'D', 'E'],
        vnodes: 10000
});

// get the vnodes that map to B
var vnodes = chash.getVnodes('B');
// rebalance them to A
chash.remapVnode('A', vnodes, function(err, ring, removedMap) {
    // remove B
    chash.removePnode('B', function(err, ring, pnode) {
        if (!err) {
            console.log('removed pnode %s', pnode);
        }
    });
});

Adding Optional Data to a Virtual Node

Sometimes, it might be helpful to associate state with a set of vnodes. An example of this would be during routine system maintenance, an administrator may want to set a certain set of vnodes to read only, and then set them back to write mode after the maintenance has completed.

Fash gives enables you to add arbitrary objects to vnodes by invoking the addData function.

chash.addData(10, 'foo');

Subsequence chash.getNode() invocations which map to vnode 10 will return:

{
    pnode: 'A',
    vnode: 10,
    data: 'foo'
}

The data associated with a virtual node is persistent across serializations and remaps.

Serializing and Persisting the Ring Toplogy

At any time, the ring toplogy can be accessed by:

chash.serialize();

Which returns the ring topology, which is a JSON serialized object string which looks like

{
    pnodeToVnodeMap: {
        A: {0, 1, 2},
        ...
    }, // the pnode to vnode mapings.
    vnode // the total number of vnodes in the ring.
}

Additionally, anytime remapVnode() is invoked, fash emits an update event which contains an updated version of this object that is not JSON serialized. Fash can be instantiated given a topology object instead of a list of nodes. This also allows you to specify a custom pnode to vnode topology -- as mentioned in the earlier bootstrapping section.

var fash = require('fash');
var Logger = require('bunyan');

var LOG = new Logger({
    name: 'fash',
    level: 'info'
});

var chash = fash.create({
   log: LOG,
   algorithm: fash.ALGORITHMS.SHA256,
   pnodes: ['A', 'B', 'C', 'D', 'E'],
   vnodes: 10000
});

var topology = chash.serialize();

var chash2 = fash.deserialize({
    log: LOG,
    topology: topology
});

That's it, chash and chash2 now contain the same ring toplogy.

Copyright (c) 2013 Yunong Xiao

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Keywords

FAQs

Package last updated on 07 May 2013

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc