#2951 pm2 reload timestamped lockfile

This commit is contained in:
Unitech 2017-06-27 23:53:52 +02:00
parent 823635e713
commit ce16728235
8 changed files with 128 additions and 15 deletions

View File

@ -1,5 +1,6 @@
## 2.5.1
- #2951 pm2 reload command locker via timestamped lock file
- force reverse interaction reconnection on internet discovery
- `--instances -1` when having a 1 cpu is no-longer spawning no processes #2953
- refactor the context retrieving from error

View File

@ -61,6 +61,7 @@ var csts = {
REMOTE_REVERSE_PORT : isNaN(parseInt(process.env.KEYMETRICS_REVERSE_PORT)) ? 43554 : parseInt(process.env.KEYMETRICS_REVERSE_PORT),
REMOTE_HOST : 's1.keymetrics.io',
SEND_INTERVAL : 1000,
RELOAD_LOCK_TIMEOUT : parseInt(process.env.PM2_RELOAD_LOCK_TIMEOUT) || 30000,
GRACEFUL_TIMEOUT : parseInt(process.env.PM2_GRACEFUL_TIMEOUT) || 8000,
GRACEFUL_LISTEN_TIMEOUT : parseInt(process.env.PM2_GRACEFUL_LISTEN_TIMEOUT) || 3000,
LOGS_BUFFER_SIZE : 8,

View File

@ -5,6 +5,6 @@
"instances": 2,
"exec_mode": "cluster",
"wait_ready": true,
"listen_timeout": 16000
"listen_timeout": 1000
}]
}

View File

@ -320,8 +320,9 @@ API.prototype.start = function(cmd, opts, cb) {
if (Common.isConfigFile(cmd) || (typeof(cmd) === 'object'))
that._startJson(cmd, opts, 'restartProcessId', cb);
else
else {
that._startScript(cmd, opts, cb);
}
};
/**
@ -449,12 +450,30 @@ API.prototype.reload = function(process_name, opts, cb) {
opts = {};
}
if (Common.lockReload() == false) {
Common.printError(conf.PREFIX_MSG_ERR + 'Reload already in progress, please try again later');
return cb ? cb(new Error('Reload in progress')) : that.exitCli(conf.ERROR_EXIT);
}
if (Common.isConfigFile(process_name))
that._startJson(process_name, opts, 'reloadProcessId');
that._startJson(process_name, opts, 'reloadProcessId', function(err, apps) {
Common.unlockReload();
if (err)
return cb ? cb(err) : that.exitCli(conf.ERROR_EXIT);
return cb ? cb(null, apps) : that.exitCli(conf.SUCCESS_EXIT);;
});
else {
if (opts && !opts.updateEnv)
Common.printOut(IMMUTABLE_MSG);
that._operate('reloadProcessId', process_name, opts, cb);
that._operate('reloadProcessId', process_name, opts, function(err, apps) {
Common.unlockReload();
if (err)
return cb ? cb(err) : that.exitCli(conf.ERROR_EXIT);
return cb ? cb(null, apps) : that.exitCli(conf.SUCCESS_EXIT);;
});
}
};

View File

@ -14,7 +14,7 @@ var shelljs = require('shelljs');
var chalk = require('chalk');
var fclone = require('fclone');
var semver = require('semver');
var moment = require('moment');
var isBinary = require('./tools/isbinaryfile.js');
var cst = require('../constants.js');
var extItps = require('./API/interpreter.json');
@ -69,6 +69,34 @@ function resolveCWD(custom_path, default_path) {
return target_cwd;
}
Common.lockReload = function() {
try {
var t1 = fs.readFileSync(cst.PM2_RELOAD_LOCKFILE).toString();
// Check if content and if time < 30 return locked
// Else if content detected (lock file staled), allow and rewritte
if (t1 && t1 != '' && moment().diff(parseInt(t1), 'seconds') < cst.RELOAD_LOCK_TIMEOUT) {
return false;
}
} catch(e) {}
try {
// Write latest timestamp
fs.writeFileSync(cst.PM2_RELOAD_LOCKFILE, moment.now());
return true;
} catch(e) {
console.error(e.message || e);
}
};
Common.unlockReload = function() {
try {
fs.writeFileSync(cst.PM2_RELOAD_LOCKFILE, '');
} catch(e) {
console.error(e.message || e);
}
};
/**
* Resolve app paths and replace missing values with defaults.
* @method prepareAppConf

View File

@ -41,6 +41,8 @@ module.exports = function(PM2_HOME) {
PM2_LOG_FILE_PATH : p.resolve(PM2_HOME, 'pm2.log'),
PM2_PID_FILE_PATH : p.resolve(PM2_HOME, 'pm2.pid'),
PM2_RELOAD_LOCKFILE : p.resolve(PM2_HOME, 'reload.lock'),
DEFAULT_PID_PATH : p.resolve(PM2_HOME, 'pids'),
DEFAULT_LOG_PATH : p.resolve(PM2_HOME, 'logs'),
DEFAULT_MODULE_PATH : p.resolve(PM2_HOME, 'node_modules'),

View File

@ -1,7 +1,10 @@
process.chdir(__dirname);
process.env.PM2_RELOAD_LOCK_TIMEOUT = 1000;
var PM2 = require('../..');
var should = require('should');
describe('Lazy API usage', function() {
@ -10,10 +13,7 @@ describe('Lazy API usage', function() {
});
it('should start a script without passing any args', function(done) {
PM2.start('./../fixtures/child.js');
setTimeout(function() {
done();
}, 300);
PM2.start('./../fixtures/child.js', done);
});
it('should list one process', function(done) {
@ -24,10 +24,10 @@ describe('Lazy API usage', function() {
});
it('should fail to start script', function(done) {
PM2.start('./../fixtures/child.js');
setTimeout(function() {
PM2.start('./../fixtures/child.js', function(err) {
should.exists(err);
done();
}, 300);
});
});
it('should list one process', function(done) {
@ -39,10 +39,10 @@ describe('Lazy API usage', function() {
it('should reload', function(done) {
PM2.reload('./../fixtures/child.js');
setTimeout(function() {
PM2.reload('child', function(err) {
should.not.exists(err);
done();
}, 300);
});
});
it('should process been restarted', function(done) {

View File

@ -0,0 +1,62 @@
process.env.NODE_ENV = 'test';
process.env.PM2_RELOAD_LOCK_TIMEOUT = 2000;
var PM2 = require('../..');
var should = require('should');
var path = require('path');
var Plan = require('../helpers/plan.js');
var fs = require('fs');
var cst = require('../../constants.js');
process.chdir(__dirname);
describe('Reload locker system', function() {
this.timeout(5000);
var pm2 = new PM2.custom({
cwd : '../fixtures'
});
after(function(done) {
pm2.kill(done)
});
it('should start app', function(done) {
pm2.start({
script : './echo.js',
instances : 4
}, function(err, data) {
should(err).be.null();
pm2.list(function(err, ret) {
should(err).be.null();
ret.length.should.eql(4);
done();
});
});
});
it('should trigger one reload and forbid the second', function(done) {
pm2.reload('all', function() {
})
setTimeout(function() {
fs.statSync(cst.PM2_RELOAD_LOCKFILE);
var dt = fs.readFileSync(cst.PM2_RELOAD_LOCKFILE);
console.log(dt.toString());
pm2.reload('all', function(err) {
console.log(arguments);
if (err) {
done(err)
}
});
}, 100);
});
});