qgenutils
Version:
A security-first Node.js utility library providing authentication, HTTP operations, URL processing, validation, datetime formatting, and template rendering. Designed as a lightweight alternative to heavy npm packages with comprehensive error handling and
109 lines (91 loc) • 3.27 kB
JavaScript
/**
* Simple Graceful Shutdown for Basic Server Applications
*
* RATIONALE: Many applications need basic graceful shutdown without the complexity
* of a full shutdown manager. This function provides a simple way to close servers
* and perform basic cleanup when shutdown signals are received.
*
* @param {object} server - Server instance with close() method (Express, HTTP, etc.)
* @param {function} cleanup - Optional async cleanup function to run before exit
* @param {number} timeout - Maximum time to wait for graceful shutdown (default: 10000ms)
* @returns {void} Function sets up signal handlers, doesn't return meaningful value
* @throws Never throws - all errors are caught and logged
*/
const { qerrors } = require('qerrors');
const logger = require('../../logger');
function gracefulShutdown(server, cleanup = null, timeout = 10000) {
// Validate server parameter
if (!server || typeof server.close !== 'function') {
const error = new Error('Server must have a close() method');
qerrors(error, 'gracefulShutdown-setup');
return;
}
// Validate cleanup parameter
if (cleanup !== null && typeof cleanup !== 'function') {
const error = new Error('Cleanup must be a function or null');
qerrors(error, 'gracefulShutdown-setup');
return;
}
// Validate timeout parameter
if (typeof timeout !== 'number' || timeout <= 0) {
timeout = 10000;
}
let isShuttingDown = false;
/**
* Execute the graceful shutdown sequence
*/
async function shutdown(signal) {
if (isShuttingDown) {
logger.warn('Shutdown already in progress, ignoring signal', { signal });
return;
}
isShuttingDown = true;
const shutdownStart = Date.now();
logger.info('Graceful shutdown initiated', { signal });
try {
// Set up timeout protection
const timeoutId = setTimeout(() => {
logger.error('Shutdown timeout exceeded, forcing exit', { timeout });
process.exit(1);
}, timeout);
// Close server
await new Promise((resolve, reject) => {
server.close((err) => {
if (err) {
logger.error('Error closing server', { error: err.message });
reject(err);
} else {
logger.info('Server closed successfully');
resolve();
}
});
});
// Run cleanup function if provided
if (cleanup) {
logger.info('Running cleanup function');
await cleanup();
logger.info('Cleanup completed');
}
// Clear timeout
clearTimeout(timeoutId);
const shutdownDuration = Date.now() - shutdownStart;
logger.info('Graceful shutdown completed', {
signal,
duration: shutdownDuration
});
process.exit(0);
} catch (error) {
logger.error('Error during graceful shutdown', {
error: error.message,
stack: error.stack
});
qerrors(error, 'gracefulShutdown-error', { signal });
process.exit(1);
}
}
// Register signal handlers
process.on('SIGTERM', () => shutdown('SIGTERM'));
process.on('SIGINT', () => shutdown('SIGINT'));
logger.info('Graceful shutdown handlers registered', { timeout });
}
module.exports = gracefulShutdown;