nodeway-raft
Version:
It is an implementation of the Raft consensus algorithm in Nodeway.
125 lines (114 loc) • 3.24 kB
JavaScript
const readline = require('readline');
const require2 = require('require-from-url/async');
const colors = require('colors/safe');
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
terminal: true
}), ctor = {}, node = {}, related = {}
async function joint(...args) {
let url = args.shift(),
op = args.shift();
if(!ctor[url]) {
let [API] = await require2([url]);
ctor[url] = new API;
}
return ctor[url][op](...args);
}
async function _join(url) {
try {
await joint(url[0], 'join', url[1]);
console.log(url[0], '->', url[1], 'ok');
}
catch(e) {
console.log(url[0], '->', url[1], colors.red(e.message));
}
}
async function join(url) {
let o = await joint(url, 'info');
for(let v of o.polls) related[v.uuid] = url;
for(let id in node)
if(node[id].url===url) id!==o.id && delete node[id];
else {
o.polls.find(v => id===v.link) || await _join([url, node[id].url]);
o.pushs.find(v => id===v.link) || await _join([node[id].url, url]);
}
node[o.id] = Object.assign({url}, o);
}
async function _leave(url) {
try {
await joint(url[0], 'leave', url[1]);
console.log(url[0], 'x>', url[2], 'ok');
}
catch(e) {
console.log(url[0], 'x>', url[2], colors.red(e.message));
}
}
async function leave(url) {
let ID = Object.keys(node).find(id => node[id].url===url);
if(!ID) console.log(url, colors.green('not in consensus'));
else {
delete node[ID];
for(let id in node) {
await _leave([url, id, node[id].url]);
await _leave([node[id].url, ID, url]);
}
}
}
rl.prompt();
rl.on('line', async line => {
try {
let word = line.split(/\s+/);
switch(word[0]) {
case 'show':
for(let id in node) await join(node[id].url);
for(let id in node) show(node[id]);
break;
case 'join':
await join(word[1]);
break;
case 'leave':
await leave(word[1]);
break;
case 'follower':
case 'weakened':
await joint(word[1], word[0]);
break;
case 'client':
let result = await joint(word[1], 'client', JSON.parse(word.slice(2).join(' ')));
console.log('result:', result);
case '':
break;
case 'quit':
process.exit(0);
default:
help();
}
}
catch(e) {
console.log(colors.red(e.message));
}
rl.prompt();
}).on('close', () => {
console.log('quit');
process.exit(0);
});
function help() {
console.log('show');
console.log('join <url>');
console.log('leave <url>');
console.log('follower <url>');
console.log('weakened <url>');
console.log('client <url> <command>');
console.log('quit');
}
function show(o) {
console.log([
o.url+' ['+o.state+']',
' poll:',
...o.polls.map(v => ' '+v.url+' ['+!!v.link+']'),
' push:',
...o.pushs.map(v => ' '+related[v.uuid]+' ['+!!v.link+']')
].join('\n'));
}