diff --git a/.eslintrc b/.eslintrc index cc11d4c2b..43661fd9d 100644 --- a/.eslintrc +++ b/.eslintrc @@ -69,7 +69,7 @@ "dot-notation": [ 2, {"allowKeywords": false} - ], + ], "no-new-func": 0, "no-new-wrappers": 0, "no-invalid-this": 0, @@ -78,23 +78,23 @@ "never" ], "keyword-spacing": [ - 2, - { - "before": false, - "after": false, - "overrides": { - "return": {"before": true, "after": true} - } - } + 2, + { + "before": false, + "after": false, + "overrides": { + "return": {"before": true, "after": true} + } + } ], "space-before-blocks": [ - 2, - "never" - ], - "space-in-parens": [ - 2, - "never" - ], + 2, + "never" + ], + "space-in-parens": [ + 2, + "never" + ], "space-before-function-paren": [ 2, "never" @@ -110,9 +110,9 @@ "no-mixed-spaces-and-tabs": 2, "no-trailing-spaces": 2, "block-spacing": [ - 2, - "always" - ], + 2, + "always" + ], "yoda": [ 2, "never" diff --git a/actions/status.js b/actions/status.js index d949eef0c..7bfdcb412 100644 --- a/actions/status.js +++ b/actions/status.js @@ -1,3 +1,11 @@ +var path = require('path'); +var packageJSON = require(path.normalize(__dirname + path.sep + '..' + path.sep + 'package.json')); + +// These values are probably good starting points, but you should expect to tweak them for your application +var maxEventLoopDelay = process.env.eventLoopDelay || 5; +var maxMemoryAlloted = process.env.maxMemoryAlloted || 200; +var maxResqueQueueLength = process.env.maxResqueQueueLength || 1000; + exports.status = { name: 'status', description: 'I will return some basic information about the API', @@ -8,11 +16,66 @@ exports.status = { 'uptime':10469 }, + checkRam: function(api, data, callback){ + var consumedMemoryMB = Math.round(process.memoryUsage().heapUsed / 1024 / 1024 * 100) / 100; + data.response.consumedMemoryMB = consumedMemoryMB; + if(consumedMemoryMB > maxMemoryAlloted){ + data.response.status = 'Unhealthy'; + data.response.problems.push('Using more than 200MP of RAM/HEAP'); + } + + callback(); + }, + + checkEventLoop: function(api, data, callback){ + api.utils.eventLoopDelay(10000, function(error, eventLoopDelay){ + data.response.eventLoopDelay = eventLoopDelay; + if(eventLoopDelay > maxEventLoopDelay){ + data.response.status = 'Unhealthy'; + data.response.problems.push('EventLoop Blocked for more than 5ms'); + } + + callback(); + }); + }, + + checkResqueQueues: function(api, data, callback){ + api.tasks.details(function(error, details){ + if(error){ return callback(error); } + var length = 0; + Object.keys(details.queues).forEach(function(q){ + length += details.queues[q].length; + }); + + if(length > maxResqueQueueLength){ + data.response.status = 'Unhealthy'; + data.response.problems.push('Resque Queues filling up'); + } + + callback(); + }); + }, + run: function(api, data, next){ + data.response.status = 'Healthy'; + data.response.problems = []; + data.response.id = api.id; data.response.actionheroVersion = api.actionheroVersion; data.response.uptime = new Date().getTime() - api.bootTime; + data.response.name = packageJSON.name; + data.response.description = packageJSON.description; + data.response.version = packageJSON.version; - next(); + var self = this; + self.checkRam(api, data, function(error){ + if(error){ return next(error); } + self.checkEventLoop(api, data, function(error){ + if(error){ return next(error); } + self.checkResqueQueues(api, data, function(error){ + next(error); + }); + }); + }); } }; diff --git a/initializers/utils.js b/initializers/utils.js index 19ae805b5..b03024a7f 100755 --- a/initializers/utils.js +++ b/initializers/utils.js @@ -2,6 +2,7 @@ var fs = require('fs'); var path = require('path'); +var async = require('async'); module.exports = { loadPriority: 0, @@ -244,6 +245,32 @@ module.exports = { return {host: host, port: parseInt(port, 10)}; }; + api.utils.eventLoopDelay = function(itterations, callback){ + var intervalJobs = []; + var intervalTimes = []; + + var i = 0; + while(i < itterations){ + intervalJobs.push(function(intervalDone){ + var start = process.hrtime(); + process.nextTick(function(){ + var delta = process.hrtime(start); + var ms = delta[1] / 1000000; + intervalTimes.push(ms); + intervalDone(); + }); + }); + i++; + } + + async.series(intervalJobs, function(){ + var sum = 0; + intervalTimes.forEach(function(t){ sum += t; }); + var avg = Math.round(sum / intervalTimes.length * 10000) / 1000; + return callback(null, avg); + }); + }; + next(); } };