UNPKG

afd

Version:

Accrual Failure Detector

289 lines (238 loc) 6.01 kB
/** * 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");