Home

Awesome

Skiff

Build Status ![Dependencies] (https://david-dm.org/pgte/skiff-algorithm.png) ![Gitter](https://badges.gitter.im/Join Chat.svg)

Abstract Node.js implementation of the Raft Consensus Algorithm.

If you're looking for a directly usable module, take a look at skiff (on top of LevelDB + Msgpack).

Contents:

Install

$ node install skiff --save

Require

var Node = require('skiff');

Create a node

var node = Node();

or, with options:

var options = {
  // ...
};
var node = Node(options);

Node create options

Node API

.listen(options, listener)

Makes the peer listen for peer communications. Takes the following arguments:

.join(peer, [peerMetadata], cb)

Joins a peer into the cluster.

node.join(peer, cb);

The peer is a string describing the peer. The description depends on the transport you're using.

.leave(peer, cb)

Removes a peer from the cluster,

node.leave(peer, cb);

The peer is a string describing the peer. The description depends on the transport you're using.

.command(command[, options], callback)

Appends a command to the leader log. If node is not the leader, callback gets invoked with an error. Example:

node.command('some command', function(err) {
  if (err) {
    if (err.code == 'ENOTLEADER') {
       // redirect client to err.leader
    }
  } else {
    console.log('cluster agreed on this command');
  }
});

This command times out after the options.commandTimeout passes by, but you can override this by passing in some options:

node.command('some command', {timeout: 5000}, function(err) {
  if (err) {
    if (err.code == 'ENOTLEADER') {
       // redirect client to err.leader
    }
  } else {
    console.log('cluster agreed on this command');
  }
});

Command options are:

.peerMeta(url)

Returns the peer metadata if the peer is known.

Events

A node emits the following events that may or not be interesting to you:

Plugins

Skiff if failry high-level and doesn't implement the network transport or the persistence layers. Instead, you have to provide an implementation for these.

Transport provider API

The node transport option accepts a provider object that implements the following interface:

Connection API

The connection API implements the following interface:

The connection object is an EventEmitter, emitting the following events:

Persistence provider API

The node persistence option accepts a provider object that implements the following interface:

Cluster Setup

Setting up a Skiff cluster can be kind of tricky. To avoid partitions you will need to start with a node that will become leader and then add the followers in the standby mode. Mind you that you can only send join commands to a leader node (to avoid partitions — it's all explained in detail in the Raft paper). Once this is done and persisted you should never need to do this again since the nodes will know each other and elect a leader at random if leader goes down.

So typically the bootstrap code for the leader would be something like:

var Node = require('skiff');
var leader = Node({
  transport: transport,
  persistence: persistence
});

leader.listen(address);

/// wait for the leader node to actually become a leader of it's one node
leader.once('leader', function() {
  leader.join('node1');
  leader.join('node2');
});

leader.on('joined', function(peer) {
  console.log('leader joined %s', peer.id);
});

The follower bootstrapping code would look something like this:

var Node = require('skiff');
var node = Node({
  transport: transport,
  persistence: persistence,
  standby: true // important
});

node.listen(address);

This makes the follower start in the standby mode.

As mentioned, once the cluster enters stationary mode you just need to bootstrap all the nodes in the same way:

var Node = require('skiff');
var node = Node({
  transport: transport,
  persistence: persistence,
});

node.listen(address);

License

ISC

© Pedro Teixeira