-max-memory-restart option - clean README - fix tests - node_args option added to JSON declaration - desc show node args - fix #487 #490

This commit is contained in:
tknew2 2014-08-14 15:40:30 +02:00
parent fc7b14568a
commit c865318d2b
23 changed files with 189 additions and 129 deletions

View File

@ -1,7 +1,9 @@
# 0.10.0 - PM2 Hellfire release
- PM2 Battle testing (https://docs.google.com/spreadsheets/d/1z7hJwI_TBQslkIS07YjgCGlpu8eHy2Wc0Bd5qDNnICE/edit#gid=925366936)
- PM2 is now Battle tested (https://docs.google.com/spreadsheets/d/1z7hJwI_TBQslkIS07YjgCGlpu8eHy2Wc0Bd5qDNnICE/edit#gid=925366936)
- Auto restart memory limit feature via --max-memory-restart (and max_memory_restart via JSON) (https://github.com/Unitech/pm2#max-memory-restart)
- Remove timestamps bu default with pm2 logs
- Coffeescript not enabled by default anymore (enhance memory usage)
- PM2 Programmatic interface enhanced
- Daemon fork system enhanced
@ -15,6 +17,7 @@
- Faster monitoring system
- AXM actions unification
- Socket errors handled
- PM2_NODE_OPTIONS deprecation (use --node-args instead)
# 0.9.6 - 0.9.5 - 0.9.4

View File

@ -12,6 +12,7 @@ pm2 is perfect when you need to spread your stateless Node.js code across all CP
- Script daemonization
- 0s downtime reload for Node apps
- Generate SystemV/SystemD startup scripts (Ubuntu, Centos...)
- Set memory limit for process to restart
- Pause unstable process (avoid infinite loop)
- Restart on file change with `--watch`
- Monitoring in console
@ -62,6 +63,7 @@ Thanks in advance and we hope that you like pm2!
- [Transitional state of apps](#a4)
- [Process listing](#a6)
- [Automatic restart process based on memory](#a6)
- [Monitoring CPU/Memory usage](#a7)
- [Logs management](#a9)
- [Clustering](#a5)
@ -179,6 +181,7 @@ $ pm2 delete all # Will remove all processes from pm2 list
# Misc
$ pm2 reset <process> # Reset meta data (restarted time...)
$ pm2 updatePM2 # Update in memory pm2
$ pm2 ping # Ensure pm2 daemon has been launched
$ pm2 sendSignal SIGUSR2 my-app # Send system signal to script
@ -327,6 +330,25 @@ To get more details about a specific process:
$ pm2 describe 0
```
<a name="max-memory-restart"/>
## Automatic restart process based on memory
Value passed is in megaoctets. Internally it uses the V8 flag `--max-old-space-size=MEM` to make a process exit when memory exceed a certain amount of RAM used.
CLI:
```bash
$ pm2 start big-array.js --max-memory-restart 20
```
JSON:
```json
{
"name" : "max_mem",
"script" : "big-array.js",
"max_memory_restart" : "20"
}
```
<a name="a7"/>
## Monitoring CPU/Memory usage
@ -528,7 +550,6 @@ To watch specifics paths, please use a JSON app declaration, `watch` can take a
"watch": ["server", "client"],
"ignoreWatch" : ["node_modules", "client/img"]
}
```
<a name="a10"/>
@ -545,6 +566,7 @@ You can define parameters for your apps in `processes.json`:
"log_date_format" : "YYYY-MM-DD HH:mm Z",
"ignoreWatch" : ["[\\/\\\\]\\./", "node_modules"],
"watch" : "true",
"node_args" : "--harmony",
"cwd" : "/this/is/a/path/to/start/script",
"env": {
"NODE_ENV": "production",
@ -613,6 +635,7 @@ Note that if you execute `pm2 start node-app-2` again, it will spawn an addition
"cwd" : "/srv/node-app/current",
"args" : "['--toto=heya coco', '-d', '1']",
"script" : "bin/app.js",
"node_args" : "--harmony",
"log_date_format" : "YYYY-MM-DD HH:mm Z",
"error_file" : "/var/log/node-app/node-app.stderr.log",
"out_file" : "log/node-app.stdout.log",
@ -915,7 +938,6 @@ PM2_BIND_ADDR
PM2_API_PORT
PM2_GRACEFUL_TIMEOUT
PM2_MODIFY_REQUIRE
PM2_NODE_OPTIONS
```
@ -928,36 +950,21 @@ $ pm2 web
<a name="a66"/>
## Enabling Harmony ES6
### Enable by default for all processes
You can enable Harmony ES6 by setting `PM2_NODE_OPTIONS='--harmony'` environment variable option when you start pm2 (pm2 should not be already daemonized).
To pass this option by default, you can edit `~/.pm2/custom_options.sh` and add:
```bash
export PM2_NODE_OPTIONS='--harmony'
```
Then:
```bash
$ pm2 dump
$ pm2 exit
$ pm2 resurrect
```
If ES6 has been enabled you should see this message at the beginning of each pm2 command:
```
● ES6 mode
```
### Enable for specific processes
The `--node-args` option permit to launch script with V8 flags, so to enable harmony for a process just do this:
```bash
$ pm2 start my_app.js --node-args="--harmony"
```
And with JSON declaration:
```bash
[{
"name" : "ES6",
"script" : "es6.js",
"node_args" : "--harmony"
}]
```
<a name="a19"/>
## CoffeeScript

14
bin/pm2
View File

@ -25,6 +25,7 @@ commander.version(pkg.version)
.option('-o --output <path>', 'specify out log file')
.option('-e --error <path>', 'specify error log file')
.option('-p --pid <pid>', 'specify pid file')
.option('--max-memory-restart <memory>', 'specify max memory amount used to autorestart (in megaoctets)')
.option('--env <environment_name>', 'specify environment to get specific env variables (for JSON declaration)')
.option('-x --execute-command', 'execute a program using fork system')
.option('-u --user <username>', 'define user when generating startup script')
@ -507,16 +508,3 @@ process.once('satan:client:ready', function() {
}
});
})();
// (function testHarmony() {
// //
// // Harmony test
// //
// try {
// var assert = require('assert')
// , s = new Set();
// s.add('a');
// assert.ok(s.has('a'));
// console.log('● ES6 mode'.green);
// } catch(e) {}
// })();

View File

@ -43,17 +43,22 @@ CLI.start = function(script, opts, cb) {
opts = {};
}
if (opts.nodeArgs)
if (opts.nodeArgs) {
//maintain backwards compat for space delimited string args
if (Array.isArray(opts.nodeArgs)){
appConf['nodeArgs'] = opts.nodeArgs;
appConf['node_args'] = opts.nodeArgs;
} else {
appConf['nodeArgs'] = opts.nodeArgs.split(' ');
appConf['node_args'] = opts.nodeArgs.split(' ');
}
} else {
appConf.node_args = [];
}
if (opts.scriptArgs)
appConf['args'] = JSON.stringify(opts.scriptArgs);
if (opts.name)
appConf['name'] = opts.name;
if (opts.maxMemoryRestart)
appConf.max_memory_restart = opts.maxMemoryRestart;
if (opts.instances)
appConf['instances'] = opts.instances;
if (opts.error)

View File

@ -58,6 +58,7 @@ UX.describeTable = function(process) {
{ 'out log path' : pm2_env.pm_out_log_path },
{ 'pid path' : pm2_env.pm_pid_path },
{ 'mode' : pm2_env.exec_mode },
{ 'node v8 arguments' : pm2_env.node_args },
{ 'watch & reload' : pm2_env.watch ? chalk.green.bold('✔') : '✘' },
{ 'interpreter' : pm2_env.exec_interpreter },
{ 'restarts' : pm2_env.restart_time },

View File

@ -63,6 +63,18 @@ Common.resolveAppPaths = function(app, cwd, outputter) {
app["pm_exec_path"] = path.resolve(cwd, app.script);
delete app.script;
if (app.node_args && !Array.isArray(app.node_args))
app.node_args = app.node_args.split(' ');
if (!app.node_args)
app.node_args = [];
if (app.max_memory_restart &&
!isNaN(parseInt(app.max_memory_restart)) &&
Array.isArray(app.node_args)) {
app.node_args.push('--max-old-space-size=' + app.max_memory_restart);
}
if (!app["name"]) {
app["name"] = p.basename(app["pm_exec_path"]);
}

View File

@ -471,17 +471,17 @@ module.exports = function(God) {
* @return
*/
God.killMe = function(env, cb) {
God.deleteAll({}, function(err, processes) {
console.log('pm2 has been killed by command line');
God.bus.emit('pm2:kill', {
status : 'killed',
msg : 'pm2 has been killed via CLI'
});
setTimeout(function() {
cb(null, {msg : 'pm2 killed'});
process.exit(cst.SUCCESS_EXIT);
}, 800);
console.log('PM2 has been killed by user command');
God.bus.emit('pm2:kill', {
status : 'killed',
msg : 'pm2 has been killed via CLI'
});
setTimeout(function() {
cb(null, {msg : 'pm2 killed'});
}, 200);
setTimeout(function() {
process.exit(cst.SUCCESS_EXIT);
}, 300);
};
@ -559,10 +559,16 @@ module.exports = function(God) {
processIds.forEach(function (id) {
var cluster = God.clusters_db[id];
if (cluster.pm2_env.exec_mode == 'cluster_mode')
cluster.send({type:'log:reload'});
else
cluster._reloadLogs(function() {
else // Fork mode
cluster._reloadLogs(function(err) {
if (err) {
God.logAndGenerateError(err);
return cb(new Error(err));
};
return false;
});
console.log('Reloading logs for process id %d', id);
});

View File

@ -36,8 +36,8 @@ module.exports = function ClusterMode(God) {
console.log('Entering in node wrap logic (cluster_mode) for script %s', env_copy.pm_exec_path);
if (env_copy.nodeArgs && Array.isArray(env_copy.nodeArgs)) {
cluster.settings.execArgv = env_copy.nodeArgs;
if (env_copy.node_args && Array.isArray(env_copy.node_args)) {
cluster.settings.execArgv = env_copy.node_args;
}
try {
@ -71,7 +71,7 @@ module.exports = function ClusterMode(God) {
}
});
//delete clu.process._handle.owner;
return cb(null, clu);

View File

@ -40,8 +40,8 @@ module.exports = function ForkMode(God) {
if (interpreter !== 'none') {
command = interpreter;
if (pm2_env.nodeArgs && Array.isArray(pm2_env.nodeArgs)) {
args = args.concat(pm2_env.nodeArgs);
if (pm2_env.node_args && Array.isArray(pm2_env.node_args)) {
args = args.concat(pm2_env.node_args);
}
if (process.env.PM2_NODE_OPTIONS) {
@ -86,15 +86,31 @@ module.exports = function ForkMode(God) {
function startLogging(cb) {
stdout = fs.createWriteStream(outFile, { flags : 'a' });
stdout.on('error', function(e) {
God.logAndGenerateError(e);
return cb(e);
});
stdout.on('open', function() {
stderr = fs.createWriteStream(errFile, { flags : 'a' });
stderr.on('error', function(e) {
God.logAndGenerateError(e);
return cb(e);
});
stderr.on('open', function() {
return cb();
return cb(null);
});
});
}
startLogging(function() {
startLogging(function(err) {
if (err) {
God.logAndGenerateError(err);
return cb(err);
};
getugid(function(e, uid, gid){
if(e){
God.logAndGenerateError(e);

View File

@ -2,6 +2,10 @@
// Child wrapper. Redirect output to files, assign pid & co.
// by Strzelewicz Alexandre
// Rename process
if (process.env.name != null)
process.title = 'pm2: ' + process.env.name;
var fs = require('fs');
var p = require('path');
var cst = require('../constants');
@ -30,9 +34,6 @@ var cst = require('../constants');
if (process.env.args != null)
process.argv = process.argv.concat(eval(process.env.args));
// Rename process
if (process.env.name != null)
process.title = 'pm2: ' + process.env.name;
exec(script, outFile, errFile);
@ -79,7 +80,7 @@ function exec(script, outFile, errFile) {
process.chdir(process.env.PWD || p.dirname(script));
var stderr, stdout;
if(p.extname(script) == '.coffee') {
require('coffee-script/register');
}

View File

@ -7,9 +7,6 @@ echo -e "\033[1mRunning tests:\033[0m"
cd $file_path
$pm2 kill
spec "kill daemon"
#
# Different way to stop process
#
@ -137,11 +134,7 @@ spec "Should get the right JSON with HttpInterface file launched"
# Restart only one process
#
$pm2 restart 1
sleep 0.3
$http_get -q http://localhost:9615/ -O $JSON_FILE
OUT=`cat $JSON_FILE | grep -o "restart_time\":1" | wc -l`
[ $OUT -eq 1 ] || fail "$1"
success "$1"
should 'should has restarted process' 'restart_time: 1' 1
#
# Restart all processes

View File

@ -21,7 +21,7 @@ $pm2 restart echo.js
spec "Should restart an app by script.js (TRANSITIONAL STATE)"
###############
$pm2 kill
$pm2 delete all
echo "---- BY_NAME Start an app, stop it, if state stopped and started, restart stopped app"
@ -33,30 +33,13 @@ $pm2 restart gege
should 'should app be online once restart called' 'online' 1
###############
$pm2 kill
$pm2 delete all
echo "Start an app, start it one more time, if started, throw message"
$pm2 start echo.js
$pm2 start echo.js
ispec "Should not re start app"
###############
$pm2 kill
cd ../..
echo "Change path try to exec"
$pm2 start test/fixtures/echo.js
should 'should app be online' 'online' 1
$pm2 stop test/fixtures/echo.js
should 'should app be stopped' 'stopped' 1
$pm2 start test/fixtures/echo.js
should 'should app be online' 'online' 1
cd -
########### DELETED STUFF BY ID
$pm2 kill
@ -65,14 +48,14 @@ $pm2 delete 0
should 'should has been deleted process by id' "name: 'echo'" 0
########### DELETED STUFF BY NAME
$pm2 kill
$pm2 delete all
$pm2 start echo.js --name test
$pm2 delete test
should 'should has been deleted process by name' "name: 'test'" 0
########### DELETED STUFF BY SCRIPT
$pm2 kill
$pm2 delete all
$pm2 start echo.js
$pm2 delete echo.js
@ -81,7 +64,7 @@ should 'should has been deleted process by script' "name: 'echo'" 0
########### OPTIONS OUTPUT FILES
$pm2 kill
$pm2 delete all
$pm2 start echo.js -o outech.log -e errech.log --name gmail -i 10
sleep 0.5

View File

@ -6,8 +6,6 @@ source "${SRC}/include.sh"
cd $file_path
########### Fork mode
$pm2 kill
$pm2 start echo.js -x
should 'should has forked app' 'fork_mode' 1

View File

@ -8,7 +8,6 @@ cd $file_path
echo "################## GRACEFUL RELOAD ###################"
###############
$pm2 kill
echo "Launching"
$pm2 start graceful-exit.js -i 4 --name="graceful" -o "grace.log" -e "grace-err.log"

View File

@ -8,7 +8,6 @@ cd $file_path
echo "################## GRACEFUL RELOAD 3 ###################"
###############
$pm2 kill
echo "Launching"
$pm2 start graceful-exit-send.js -i 2 --name="graceful3" -o "grace3.log" -e "grace-err3.log"

View File

@ -11,33 +11,29 @@ echo "################ HARMONY ES6"
$pm2 start harmony.js
sleep 2
$pm2 list
ishould 'should not fail when passing harmony option to V8' 'restart_time: 0' 1
should 'should FAIL when not passing harmony option to V8' 'restart_time: 0' 0
$pm2 list
$pm2 kill
PM2_NODE_OPTIONS='--harmony' `pwd`/../../bin/pm2 start harmony.js
sleep 2
$pm2 list
should 'should not fail when passing harmony option to V8' 'restart_time: 0' 1
$pm2 kill
$pm2 delete all
$pm2 start harmony.js --node-args="--harmony"
sleep 2
$pm2 list
should 'should not fail when passing node-args=harmony opts' 'restart_time: 0' 1
$pm2 kill
should 'should not fail when passing node-args=harmony opts in CLUSTERMODE' 'restart_time: 0' 1
$pm2 delete all
echo "################ HARMONY / NODEARGS ES6 FORK MODE"
$pm2 start harmony.js --node-args="--harmony" -x
sleep 2
$pm2 list
should 'should not fail when passing node-args=harmony opts' 'restart_time: 0' 1
$pm2 kill
should 'should not fail when passing node-args=harmony opts in FORKMODE' 'restart_time: 0' 1
$pm2 delete all
echo "################## NODE ARGS VIA JSON"
PM2_NODE_OPTIONS='--harmony' $pm2 start harmony.js -x
$pm2 start harmony.json
sleep 2
$pm2 list
should 'should not fail when passing node-args=harmony opts' 'restart_time: 0' 1
$pm2 kill
should 'should not fail when passing harmony option to V8 via node_args in JSON files' 'restart_time: 0' 1
$pm2 delete all

View File

@ -22,7 +22,7 @@ file_path="test/fixtures"
$pm2 kill
# Determine wget / curl
which wget
which wget > /dev/null
if [ $? -eq 0 ]
then
http_get="wget"
@ -31,8 +31,6 @@ else
exit 1;
fi
echo $http_get
function fail {
echo -e "######## \033[31m ✘ $1\033[0m"
exit 1

View File

@ -8,10 +8,6 @@ cd $file_path
echo "Starting infinite loop tests"
$pm2 kill
$pm2 start killtoofast.js --name unstable-process
echo -n "Waiting for process to restart too many times and pm2 to stop it"

View File

@ -7,8 +7,27 @@ cd $file_path
echo -e "\033[1mRunning tests:\033[0m"
#
# Max memory auto restart option
#
# -max-memory-restart option && maxMemoryRestart (via JSON file)
#
$pm2 start big-array.js --max-memory-restart 19
sleep 7
$pm2 list
should 'process should been restarted' 'restart_time: 0' 0
$pm2 kill
$pm2 delete all
#
# Via JSON
#
$pm2 start max-mem.json
sleep 7
$pm2 list
should 'process should been restarted' 'restart_time: 0' 0
$pm2 delete all
$pm2 start env.js
@ -27,7 +46,7 @@ else
fail "environment defined ? wtf ?"
fi
$pm2 kill
$pm2 delete all
$pm2 start env.json
@ -37,7 +56,7 @@ sleep 1
OUT=`cat $OUT_LOG | head -n 1`
if [ $OUT = "undefined" ]
if [ "$OUT" = "undefined" ]
then
fail "environment variable hasnt been defined"
else
@ -79,10 +98,9 @@ ispec 'file outmerge-0.log should not exist'
rm outmerge*
########### coffee cluster test
$pm2 kill
$pm2 delete all
$pm2 start echo.coffee
should 'process should not have been restarted' 'restart_time: 0' 1
should 'process should be online' "status: 'online'" 1

23
test/fixtures/big-array-es6.js vendored Normal file
View File

@ -0,0 +1,23 @@
var obj = {};
var i = 0;
setInterval(function() {
obj[i] = Array.apply(null, new Array(99999)).map(String.prototype.valueOf,"hi");
i++;
}, 2);
(function testHarmony() {
//
// Harmony test
//
try {
var assert = require('assert')
, s = new Set();
s.add('a');
assert.ok(s.has('a'));
console.log('● ES6 mode'.green);
} catch(e) {}
})();

8
test/fixtures/big-array.js vendored Normal file
View File

@ -0,0 +1,8 @@
var obj = {};
var i = 0;
setInterval(function() {
obj[i] = Array.apply(null, new Array(99999)).map(String.prototype.valueOf,"hi");
i++;
}, 2);

5
test/fixtures/harmony.json vendored Normal file
View File

@ -0,0 +1,5 @@
{
"name" : "ES6",
"script" : "harmony.js",
"node_args" : "--harmony"
}

5
test/fixtures/max-mem.json vendored Normal file
View File

@ -0,0 +1,5 @@
{
"name" : "max_mem",
"script" : "big-array.js",
"max_memory_restart" : "19"
}