euthanasia
Version:
Gracefully exits a Node.js process when memory or CPU usage exceeds defined thresholds. Supports customizable exit logic via async hooks.
91 lines (76 loc) • 2.9 kB
JavaScript
/**
* @typedef {Object} ResourceUsage
* @property {number | undefined} memory - Current memory usage in MB (if memory limit was exceeded)
* @property {number | undefined} cpu - Current CPU usage percentage (if CPU limit was exceeded)
*/
/**
* @typedef {Object} EuthanasiaOptions
* @property {number} [memory] - Memory usage limit in MB
* @property {number} [cpu] - CPU usage limit as a percentage (0–100 per core)
* @property {number} [interval] - Interval between checks, in milliseconds
* @property {(usage: ResourceUsage) => Promise<boolean>} [ready] - Called when memory or CPU usage exceeds limits
*/
const TO_MEGABYTE = 1 / 1024 / 1024;
const DEFAULT_MEMORY = 0;
const DEFAULT_INTERVAL = 60_000;
const DEFAULT_READY = async ({ memory, cpu }) => {
if (memory) {
console.info(`[OOM] Memory usage exceeded: ${memory} MB`);
}
if (cpu) {
console.info(`[CPU] CPU usage exceeded: ${cpu.toFixed(2)}%`);
}
return true;
};
/**
* Returns current heap memory usage in MB.
* @returns {number}
*/
function getHeapMemoryUsageMb() {
return Math.round(process.memoryUsage().heapUsed * TO_MEGABYTE);
}
/**
* Calculates average CPU usage percentage over a time interval.
* @param {{user: number, system: number}} startUsage - CPU usage at start of interval
* @param {{user: number, system: number}} endUsage - CPU usage at end of interval
* @param {number} intervalMs - Interval length in milliseconds
* @returns {number} CPU usage as a percentage of a single core
*/
function calculateCpuUsagePercent(startUsage, endUsage, intervalMs) {
const userDiff = endUsage.user - startUsage.user; // microseconds
const systemDiff = endUsage.system - startUsage.system; // microseconds
const totalDiffMicros = userDiff + systemDiff;
const intervalMicros = intervalMs * 1000;
return (totalDiffMicros / intervalMicros) * 100;
}
/**
* Monitors memory and CPU usage and terminates the process if limits are exceeded.
* @param {EuthanasiaOptions} options - Monitoring configuration
*/
function euthanasia({
memory = DEFAULT_MEMORY,
cpu = 0,
interval = DEFAULT_INTERVAL,
ready = DEFAULT_READY
} = {}) {
let lastCpuUsage = process.cpuUsage();
const check = async () => {
const currentMemoryMb = getHeapMemoryUsageMb();
const currentCpuPercent =
cpu > 0
? calculateCpuUsagePercent(lastCpuUsage, process.cpuUsage(), interval)
: 0;
lastCpuUsage = process.cpuUsage();
const memoryExceeded = memory > 0 && currentMemoryMb > memory;
const cpuExceeded = cpu > 0 && currentCpuPercent > cpu;
/** @type {ResourceUsage} */
const usage = {};
if (memoryExceeded) usage.memory = currentMemoryMb;
if (cpuExceeded) usage.cpu = currentCpuPercent;
if ((memoryExceeded || cpuExceeded) && (await ready(usage))) {
process.exit(0);
}
};
setInterval(check, interval);
}
module.exports = euthanasia;