├── .gitignore ├── index.js ├── example.js ├── package.json ├── README.md └── lib └── memory_limiter.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea 3 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/memory_limiter'); -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | var memLimit = require('./lib/memory_limiter'); 2 | 3 | // leak object contructor 4 | function MemoryLeakObject () { 5 | } 6 | 7 | // The main function to be used. 8 | function mainFunction() { 9 | var everGrowingArray = []; 10 | setInterval(function () { 11 | for (var i=0; i < 1000; ++i) { 12 | everGrowingArray.push(new MemoryLeakObject) 13 | } 14 | },2000); 15 | } 16 | 17 | memLimit.limitProcessMemory (3000 /*check every three seconds */, 18 | 100000000 /*max process memory size in bytes*/, 19 | mainFunction , 20 | function processTerminated(){ console.log("process killed")}); 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "memory-limiter", 3 | "version": "0.1.1", 4 | "description": "A small library for restarting a process once it reachs a certain memory size", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git@github.com:DoryZi/memory_limiter.git" 9 | }, 10 | "keywords": [ 11 | "restart", 12 | "memory limit", 13 | "cluster", 14 | "restart process", 15 | "memory consumption" 16 | ], 17 | "author": "Dory Zidon ", 18 | "licenses": [ 19 | { 20 | "type": "MIT", 21 | "url": "https://github.com/brentertz/scapegoat/blob/master/LICENSE-MIT" 22 | } 23 | ], 24 | 25 | "bugs": { 26 | "url": "https://github.com/DoryZi/memory_limiter/issues" 27 | }, 28 | "dependencies": { 29 | "usage" : "*", 30 | "cluster" : "*" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | memory_limiter.js 2 | ============= 3 | 4 | Memory Limiter is a little utility library that restarts a node.js process once it reaches a certain size. 5 | It is mainly interested for Heroku and the likes of it, once you have a resource leak and need a quick fix. 6 | This should enable your multi dyno server to continue running while you're trying to find the leak itself and fix it. 7 | 8 | 9 | Installing 10 | ---------- 11 | Installing Memory Limter is easy. Simply download the package from npm. 12 | 13 | npm install memory_limit 14 | 15 | Usage 16 | ----- 17 | It's very simple to use as well. 18 | You just warp all your code that runs on load in a function and pass that function the memory limiter. 19 | 20 | var memLimit = require('./lib/memory_limiter'); 21 | 22 | then call memLimit only exported function: 23 | 24 | 25 | memLimit.limitProcessMemory (3000 /*check every three seconds */, 26 | 100000000 /*max process memory size in bytes*/, 27 | mainFunction /*the main function that runs your code*/ 28 | cleanupFunction /*after each process termination cleanupFunction will get called and have 30 seconds to finish cleaning up the process*/ 29 | ); 30 | 31 | 32 | -------------------------------------------------------------------------------- /lib/memory_limiter.js: -------------------------------------------------------------------------------- 1 | /****** 2 | * module to that uses the cluster and status to check memory of a process and restart it once reaching a certain size. 3 | * This module is a patch for finding resource / memory leaks. Will not work on windows. only status supports systems. 4 | * @type {*|exports} 5 | */ 6 | 7 | var displayInfo = process.env.MEM_LIMIT_SHOW_INFO; 8 | var cluster = require('cluster'); 9 | module.exports = { 10 | /** 11 | * is this is the master process will create the observing process otherwise will create a child process. 12 | * When the process memory reachs the limit ti will kill itself gracefully, meaning will run any cleanup code 13 | * and allow the process 30 seconds to finish that code. 14 | * @param memoryCheckIntervalTimeout - how oftern in miliseconds should the process check it's memory 15 | * @param memoryLimitInBytes - the limit afterwhich the process should restart in bytes 16 | * @param mainCodeFunction - the main child process code 17 | * @param exitCleanupCodeFunction - the cleanup code that should be run when reaching the max memory 18 | */ 19 | limitProcessMemory : function (memoryCheckIntervalTimeout, memoryLimitInBytes, mainCodeFunction ,exitCleanupCodeFunction) { 20 | if (cluster.isMaster) { 21 | console.log("running main process"); 22 | cluster.fork(); 23 | //if the worker dies, restart it. 24 | cluster.on('disconnect', function(worker){ 25 | console.log('MEMORY: Worker ' + worker.id + ' disconnected, launch a new process..'); 26 | cluster.fork(); 27 | }); 28 | } else { 29 | mainCodeFunction(); 30 | // only exist in production / staging 31 | 32 | var usage = require('usage'); 33 | var dying = false; 34 | setInterval(function () { 35 | var pid = process.pid // you can use any valid PID instead 36 | usage.lookup(pid, function(err, result) { 37 | if (result === null || result === undefined) { 38 | console.log("MEMORY memory check result is null"); 39 | return; 40 | } 41 | if (displayInfo) console.log ("MEMORY IS" + result.memory); 42 | if (parseInt(result.memory) > memoryLimitInBytes && dying === false) { 43 | console.log("MEMORY exceeded kill the process"); 44 | // run time, so we will have time to cleanup. 45 | var killtimer = setTimeout(function() { 46 | console.log("MEMORY process dying NOW!") 47 | process.exit(1); 48 | }, 30000); 49 | // But don't keep the process open just for that! 50 | killtimer.unref(); 51 | if (exitCleanupCodeFunction) exitCleanupCodeFunction(); 52 | try { 53 | if (cluster.worker.state !== "disconnected" && cluster.worker.state !== "dead") { 54 | cluster.worker.disconnect(); 55 | } 56 | 57 | } catch (err) {}; 58 | dying = true; 59 | } 60 | }); 61 | },memoryCheckIntervalTimeout); 62 | } 63 | 64 | } 65 | } 66 | --------------------------------------------------------------------------------