afd
Version:
Accrual Failure Detector
289 lines (238 loc) • 6.01 kB
JavaScript
/**
* Require the given path.
*
* @param {String} path
* @return {Object} exports
* @api public
*/
function require(p, parent, orig){
var path = require.resolve(p)
, mod = require.modules[path];
// lookup failed
if (null == path) {
orig = orig || p;
parent = parent || 'root';
throw new Error('failed to require "' + orig + '" from "' + parent + '"');
}
// perform real require()
// by invoking the module's
// registered function
if (!mod.exports) {
mod.exports = {};
mod.client = mod.component = true;
mod.call(this, mod, mod.exports, require.relative(path));
}
return mod.exports;
}
/**
* Registered modules.
*/
require.modules = {};
/**
* Registered aliases.
*/
require.aliases = {};
/**
* Resolve `path`.
*
* Lookup:
*
* - PATH/index.js
* - PATH.js
* - PATH
*
* @param {String} path
* @return {String} path or null
* @api private
*/
require.resolve = function(path){
var orig = path
, reg = path + '.js'
, regJSON = path + '.json'
, index = path + '/index.js'
, indexJSON = path + '/index.json';
return require.modules[reg] && reg
|| require.modules[regJSON] && regJSON
|| require.modules[index] && index
|| require.modules[indexJSON] && indexJSON
|| require.modules[orig] && orig
|| require.aliases[index];
};
/**
* Normalize `path` relative to the current path.
*
* @param {String} curr
* @param {String} path
* @return {String}
* @api private
*/
require.normalize = function(curr, path) {
var segs = [];
if ('.' != path.charAt(0)) return path;
curr = curr.split('/');
path = path.split('/');
for (var i = 0; i < path.length; ++i) {
if ('..' == path[i]) {
curr.pop();
} else if ('.' != path[i] && '' != path[i]) {
segs.push(path[i]);
}
}
return curr.concat(segs).join('/');
};
/**
* Register module at `path` with callback `fn`.
*
* @param {String} path
* @param {Function} fn
* @api private
*/
require.register = function(path, fn){
require.modules[path] = fn;
};
/**
* Alias a module definition.
*
* @param {String} from
* @param {String} to
* @api private
*/
require.alias = function(from, to){
var fn = require.modules[from];
if (!fn) throw new Error('failed to alias "' + from + '", it does not exist');
require.aliases[to] = from;
};
/**
* Return a require function relative to the `parent` path.
*
* @param {String} parent
* @return {Function}
* @api private
*/
require.relative = function(parent) {
var p = require.normalize(parent, '..');
/**
* lastIndexOf helper.
*/
function lastIndexOf(arr, obj){
var i = arr.length;
while (i--) {
if (arr[i] === obj) return i;
}
return -1;
}
/**
* The relative require() itself.
*/
function fn(path){
var orig = path;
path = fn.resolve(path);
return require(path, parent, orig);
}
/**
* Resolve relative to the parent.
*/
fn.resolve = function(path){
// resolve deps by returning
// the dep in the nearest "deps"
// directory
if ('.' != path.charAt(0)) {
var segs = parent.split('/');
var i = lastIndexOf(segs, 'deps') + 1;
if (!i) i = 0;
path = segs.slice(0, i + 1).join('/') + '/deps/' + path;
return path;
}
return require.normalize(p, path);
};
/**
* Check if module is defined at `path`.
*/
fn.exists = function(path){
return !! require.modules[fn.resolve(path)];
};
return fn;
};require.register("afd/lib/afd.js", function(module, exports, require){
/**
* Accrual Failure Detector
* @module afd
*/
/**
* @constructor
* @param {Number} {ws=100} How many heartbeat intervals we keep track of (window size)
* @param {Number} {last=Date.now()} Last report timestamp
*/
var afd = function (ws, last) {
if(!exists(last)) last = Date.now()
if(!exists(ws)) ws = 100
this._ws = ws
this._last = last
this._intervals = Array()
this._variance = -1
this._mean = -1
}
/**
* Report a received heartbeat
* @param {Number} when
*/
afd.prototype.report = function (when) {
if(this._intervals.length > this._ws) this.intervals.shift()
if(!when) when = Date.now()
var interval = when - this._last
this._intervals.push(interval)
this._last = when
if(!this._intervals.length) return
this._variance = this.variance()
this._mean = this.mean()
}
/**
* Get the current phi to determine failure
* See {@link https://issues.apache.org/jira/browse/CASSANDRA-2597 CASSANDRA-2597} for an explanation of the math at work here.
* Using 1 as a threshold means the likeliness of mistaken failure is 10%
* Using 2 as a threshold means the likeliness of mistaken failure is 1%
* Using 3 as a threshold means the likeliness of mistaken failure is 0.1%
*
* @param {Number} when
*/
afd.prototype.phi = function (when) {
// We haven't yet got any data via report.
if(this._mean === -1) return -1
if(!when) when = Date.now()
var interval = when - this._last
// If it's only 5 seconds from the start - let's give the system some
// slack and allow it to fuck up.
if((this._intervals.length < 3) && (interval < 5000)) return 0
var probability = Math.pow(Math.E, -1 * interval / this._mean)
return (-1) * (Math.log(probability) / Math.LN10)
}
/**
* Get the mean interval
* @returns mean
*/
afd.prototype.mean = function () {
return this._intervals.reduce(function (prev, curr) {
return prev + curr
}) / this._intervals.length
}
/**
* Get the variance
* @returns variance
*/
afd.prototype.variance = function () {
var mean = this._mean
return this._intervals.reduce(function (prev, curr) {
var x = curr - mean
return prev + (x * x)
}) / this._intervals.length
}
/**
* Exports the afd constructor
*/
module.exports = function (ws, last) {
return new afd(ws, last)
}
var exists = function (e) {
return !((typeof e === 'undefined') || (e === null))
}
});
require.alias("afd/lib/afd.js", "afd/index.js");