Skip to content

Commit

Permalink
Add --spinSleepTime to throttle instead of killing spinning scripts
Browse files Browse the repository at this point in the history
  • Loading branch information
dustyleary committed May 30, 2011
1 parent 7634248 commit 6d93dcc
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 60 deletions.
63 changes: 37 additions & 26 deletions bin/forever
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,20 @@ var help = [
' cleanlogs [CAREFUL] Deletes all historical forever log files',
'',
'options:',
' -m MAX Only run the specified script MAX times',
' -l LOGFILE Logs the forever output to LOGFILE',
' -o OUTFILE Logs stdout from child script to OUTFILE',
' -e ERRFILE Logs stderr from child script to ERRFILE',
' -d SOURCEDIR The source directory for which SCRIPT is relative to',
' -p PATH Base path for all forever related files (pid files, etc.)',
' -c COMMAND COMMAND to execute (defaults to node)',
' --pidfile The pid file',
' -a, --append Append logs',
' -v, --verbose Turns on the verbose messages from Forever',
' -s, --silent Run the child script silencing stdout and stderr',
' -h, --help You\'re staring at it',
' -m MAX Only run the specified script MAX times',
' -l LOGFILE Logs the forever output to LOGFILE',
' -o OUTFILE Logs stdout from child script to OUTFILE',
' -e ERRFILE Logs stderr from child script to ERRFILE',
' -d SOURCEDIR The source directory for which SCRIPT is relative to',
' -p PATH Base path for all forever related files (pid files, etc.)',
' -c COMMAND COMMAND to execute (defaults to node)',
' --pidfile The pid file',
' -a, --append Append logs',
' --minUptime Minimum uptime (millis) for a script to not be considered "spinning"',
' --spinSleepTime Time to wait (millis) between launches of a spinning script. (defaults to killing a spinning script).',
' -v, --verbose Turns on the verbose messages from Forever',
' -s, --silent Run the child script silencing stdout and stderr',
' -h, --help You\'re staring at it',
'',
'[Long Running Process]',
' The forever process will continue to run outputting log messages to the console.',
Expand Down Expand Up @@ -71,20 +73,22 @@ if (argv.h || argv.help || (argv._.length === 0 && !isSimpleAction())
}

var mappings = {
'c': 'command',
'e': 'errFile',
'd': 'sourceDir',
'l': 'logFile',
'a': 'appendLog',
'append': 'appendLog',
'm': 'max',
'o': 'outFile',
'p': 'path',
'pidfile': 'pidFile',
's': 'silent',
'silent': 'silent',
'v': 'verbose',
'verbose': 'verbose'
'c': 'command',
'e': 'errFile',
'd': 'sourceDir',
'l': 'logFile',
'a': 'appendLog',
'append': 'appendLog',
'm': 'max',
'o': 'outFile',
'p': 'path',
'pidfile': 'pidFile',
's': 'silent',
'silent': 'silent',
'minUptime': 'minUptime',
'spinSleepTime': 'spinSleepTime',
'v': 'verbose',
'verbose': 'verbose'
};

//
Expand Down Expand Up @@ -122,6 +126,13 @@ if (typeof options['max'] === 'undefined') {
options.forever = true;
}

if (typeof options['minUptime'] !== 'undefined') {
options['minUptime'] = parseFloat(options['minUptime']);
}
if (typeof options['spinSleepTime'] !== 'undefined') {
options['spinSleepTime'] = parseFloat(options['spinSleepTime']);
}

if (!options.sourceDir) {
//
// Set the sourceDir of the options for graceful
Expand Down
70 changes: 44 additions & 26 deletions lib/forever/monitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,22 @@ var sys = require('sys'),
var Monitor = exports.Monitor = function (script, options) {
events.EventEmitter.call(this);

options = options || {};
this.silent = options.silent || false;
this.forever = options.forever || false;
this.command = options.command || 'node';
this.sourceDir = options.sourceDir;
this.minUptime = typeof options.minUptime !== 'number' ? 2000 : options.minUptime;
this.options = options.options || [];
this.spawnWith = options.spawnWith || null;
this.uid = options.uid || forever.randomString(24);
this.max = options.max;
this.logFile = options.logFile || path.join(forever.config.get('root'), this.uid + '.log');
this.pidFile = options.pidFile || path.join(forever.config.get('pidPath'), this.uid + '.pid');
this.outFile = options.outFile;
this.errFile = options.errFile;
this.logger = options.logger || new (winston.Logger)({
options = options || {};
this.silent = options.silent || false;
this.forever = options.forever || false;
this.command = options.command || 'node';
this.sourceDir = options.sourceDir;
this.minUptime = typeof options.minUptime !== 'number' ? 2000 : options.minUptime;
this.spinSleepTime = options.spinSleepTime || null;
this.options = options.options || [];
this.spawnWith = options.spawnWith || null;
this.uid = options.uid || forever.randomString(24);
this.max = options.max;
this.logFile = options.logFile || path.join(forever.config.get('root'), this.uid + '.log');
this.pidFile = options.pidFile || path.join(forever.config.get('pidPath'), this.uid + '.pid');
this.outFile = options.outFile;
this.errFile = options.errFile;
this.logger = options.logger || new (winston.Logger)({
transports: [new winston.transports.Console({ silent: this.silent })]
});

Expand Down Expand Up @@ -140,23 +141,40 @@ Monitor.prototype.start = function (restart) {
child.on('exit', function (code) {
var spinning = Date.now() - self.ctime < self.minUptime;
self.warn('Forever detected script exited with code: ' + code);

if ((self.forever || self.times < self.max) && !self.forceStop && !spinning) {

function letChildDie() {
self.running = false;

// If had to write to an stdout file, close it
if (self.stdout) self.stdout.end();
// If had to write to an stderr file, close it
if (self.stderr) self.stderr.end();

self.emit('exit', self, spinning);
}

function restartChild() {
self.times++;
process.nextTick(function () {
self.warn('Forever restarting script for ' + self.times + ' time');
self.start(true);
});
}

if(self.forceStop) {
letChildDie();
}
else if(spinning && typeof self.spinSleepTime !== 'number') {
letChildDie();
}
else if (!self.forever && self.times >= self.max) {
letChildDie();
}
else if(spinning) {
setTimeout(restartChild, self.spinSleepTime);
}
else {
this.running = false;

// If had to write to an stdout file, close it
if (self.stdout) self.stdout.end();
// If had to write to an stderr file, close it
if (self.stderr) self.stderr.end();

self.emit('exit', self, spinning);
restartChild();
}
});

Expand Down Expand Up @@ -289,4 +307,4 @@ Monitor.prototype.kill = function (forceStop) {
}

return this;
};
};
34 changes: 26 additions & 8 deletions test/spin-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,34 @@ var sys = require('sys'),
vows.describe('forever/spin-restart').addBatch({
"When using forever": {
"and spawning a script that spin restarts": {
topic: function () {
var script = path.join(__dirname, '..', 'examples', 'always-throw.js'),
child = new (forever.Forever)(script, { silent: true });
"with no spinSleepTime specified": {
topic: function () {
var script = path.join(__dirname, '..', 'examples', 'always-throw.js'),
child = new (forever.Forever)(script, { silent: true, max: 3 });

child.on('exit', this.callback.bind(null, null));
child.start();
child.on('exit', this.callback.bind({}, null));
child.start();
},
"should spawn both processes appropriately": function (err, child, spinning) {
assert.isTrue(spinning);
},
"should not restart spinning processes": function (err, child, spinning) {
assert.equal(child.times, 0);
}
},
"should spawn both processes appropriately": function (err, monitor, spinning) {
assert.isTrue(spinning);

"with a spinSleepTime specified": {
topic: function () {
var script = path.join(__dirname, '..', 'examples', 'always-throw.js'),
child = new (forever.Forever)(script, { silent: true, max: 3, spinSleepTime: 1 });

child.on('exit', this.callback.bind({}, null));
child.start();
},
"should restart spinning processes": function (err, child, spinning) {
assert.equal(child.times, 3);
}
}
}
},
}
}).export(module);

0 comments on commit 6d93dcc

Please sign in to comment.