From 9a2e9fd84dfe63f3562af24ce49a801b281a373f Mon Sep 17 00:00:00 2001 From: Bruno Michel Date: Wed, 10 Feb 2016 13:02:17 +0100 Subject: [PATCH] Add a cooldownInterval options When monitoring a script, it's handy to limit the maximum number of restart. But it's not the same if the script fails 10 times in 10 seconds and 10 times in 10 weeks. On the first case, there is probably an hard error and it's better to stop soon trying to restart this script. But on the second case, a max of 10 is not enough. Maybe it's just a crontab running once a week that makes it fail. The cooldownInterval is here to make max time sensitive. Forever-monitor keeps a counter with the number of failures of the script. When this counter reaches the max, it stops trying to restart it. With cooldownInterval, this counter is halfed at a regular interval defined by the number of seconds of cooldownInterval. For example, with cooldownInterval=300, if the counter is at 4 failures and in the next 5 minutes, the script won't fail, it will go to 2. And if the next 5 following minutes, it still run quiet, it will go to 1. And 5 more minutes, and will be reset to 0. --- README.md | 1 + lib/forever-monitor/monitor.js | 4 +++ lib/forever-monitor/plugins/cooldown.js | 28 ++++++++++++++++ lib/forever-monitor/plugins/index.js | 5 +-- test/plugins/cooldown-test.js | 43 +++++++++++++++++++++++++ 5 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 lib/forever-monitor/plugins/cooldown.js create mode 100644 test/plugins/cooldown-test.js diff --git a/README.md b/README.md index 72415bf..6e100bd 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ There are several options that you should be aware of when using forever. Most o 'uid': 'your-UID', // Custom uid for this forever process. (default: autogen) 'pidFile': 'path/to/a.pid', // Path to put pid information for the process(es) started 'max': 10, // Sets the maximum number of times a given script should run + 'cooldownInterval': 300, // Stop restarting if max is reached during this interval (in seconds) 'killTree': true, // Kills the entire child process tree on `exit` // diff --git a/lib/forever-monitor/monitor.js b/lib/forever-monitor/monitor.js index 02c75e6..391246b 100755 --- a/lib/forever-monitor/monitor.js +++ b/lib/forever-monitor/monitor.js @@ -35,6 +35,9 @@ var Monitor = exports.Monitor = function (script, options) { if (options.watch) { plugins.watch.attach.call(monitor, options); } + if (options.cooldownInterval) { + plugins.cooldown.attach.call(monitor, options); + } } var execPath = process.execPath, @@ -50,6 +53,7 @@ var Monitor = exports.Monitor = function (script, options) { this.id = options.id || false; this.pidFile = options.pidFile; this.max = options.max; + this.cooldownInterval = options.cooldownInterval || null; this.killTTL = options.killTTL; this.killSignal = options.killSignal || 'SIGKILL'; this.childExists = false; diff --git a/lib/forever-monitor/plugins/cooldown.js b/lib/forever-monitor/plugins/cooldown.js new file mode 100644 index 0000000..505c19c --- /dev/null +++ b/lib/forever-monitor/plugins/cooldown.js @@ -0,0 +1,28 @@ +/* + * watch.js: Plugin for `Monitor` instances which decreases the count of + * failures over time. It makes the max option time sensitive (ie it's not the + * same to have 10 failures in 10 seconds and 10 failures in 10 weeks). + * + * (C) 2010 Charlie Robbins & the Contributors + * MIT LICENCE + * + */ + +// +// Name the plugin +// +exports.name = 'cooldown'; + +// +// ### function attach (options) +// #### @options {Object} Options for attaching to `Monitor` +// +// Attaches functionality for logging stdout and stderr to `Monitor` instances. +// +exports.attach = function (options) { + var monitor = this; + + setInterval(function() { + monitor.times = Math.floor(monitor.times / 2); + }, options.cooldownInterval * 1000); +} diff --git a/lib/forever-monitor/plugins/index.js b/lib/forever-monitor/plugins/index.js index 24d3127..a2b5a26 100644 --- a/lib/forever-monitor/plugins/index.js +++ b/lib/forever-monitor/plugins/index.js @@ -6,5 +6,6 @@ * */ -exports.logger = require('./logger'); -exports.watch = require('./watch'); \ No newline at end of file +exports.logger = require('./logger'); +exports.watch = require('./watch'); +exports.cooldown = require('./cooldown') diff --git a/test/plugins/cooldown-test.js b/test/plugins/cooldown-test.js new file mode 100644 index 0000000..b81fc76 --- /dev/null +++ b/test/plugins/cooldown-test.js @@ -0,0 +1,43 @@ +var assert = require('assert') + path = require('path'), + vows = require('vows'), + fmonitor = require('../../lib'), + macros = require('../helpers/macros'); + +var examplesDir = path.join(__dirname, '..', '..', 'examples'); + +vows.describe('forever-monitor/plugins/logger').addBatch({ + 'When using the cooldown plugin': { + 'running error-on-timer sample one hundred times': { + topic: function () { + var script = path.join(examplesDir, 'error-on-timer.js'); + var options = { + max: 10, + cooldownInterval: 1, + silent: true, + outFile: 'test/fixtures/stdout.log', + errFile: 'test/fixtures/stderr.log', + args: [] + } + var child = new (fmonitor.Monitor)(script, options); + setTimeout(this.callback.bind({}, null, child), 2100); + child.start(); + }, + 'should have not reached max': function (err, child) { + assert.ok(child.times < 10); + } + }, + 'running error-on-timer sample three times': macros.assertTimes( + path.join(examplesDir, 'error-on-timer.js'), + 3, + { + cooldownInterval: 10, + minUptime: 200, + silent: true, + outFile: 'test/fixtures/stdout.log', + errFile: 'test/fixtures/stderr.log', + args: [] + } + ) + } +}).export(module);