Shutdown-Cleanup

Logo

NodeJS module to gracefully shutdown an application

View the Project on GitHub hypercliq/shutdown-cleanup

Developer Guide

This developer guide provides in-depth documentation on how to use the shutdown-cleanup module effectively in your Node.js applications, covering best practices, detailed examples, and advanced usage scenarios.

Default Signal Handling and Customization

By default, the module listens to common signals (SIGINT, SIGTERM, SIGHUP, and exit) to initiate the shutdown process. The module can also be customized to listen to additional signals or react to events.

Customize this behavior as needed:

Adding and Removing Signals

Customize your application’s response to system signals by adding or removing them:

addSignal('beforeExit')
removeSignal('SIGHUP')

Signal-Specific Handlers

Gain flexibility by registering handlers for specific signals or events, allowing for custom logic without necessitating a shutdown:

registerSignalHandler(
  'SIGUSR1',
  async (signal) => {
    console.log('Custom logic for SIGUSR1')
  },
  false, // <= false indicates that the application should not terminate
)

Handler Functions: Best Practices

Only asynchronous functions are accepted for handlers to ensure non-blocking operations and efficient shutdowns. Asynchronous handlers allow the shutdown process to wait for necessary cleanup tasks to complete without blocking the event loop. Attempting to register synchronous handlers will result in a TypeError.

Asynchronous Handlers

Asynchronous handlers prevent delays in the shutdown process, especially beneficial for I/O operations or lengthy tasks:

registerHandler(async (signal) => {
  await performComplexCleanup()
  console.log('Cleanup completed')
}, 'complexCleanup')

Advanced Configuration

Examples

Basic Shutdown Sequence

A simple setup for managing a graceful shutdown:

import { registerHandler } from '@hypercliq/shutdown-cleanup'

registerHandler(async () => console.log('Cleanup resources'))

Advanced Shutdown Sequence

A comprehensive example demonstrating how to set up a phased graceful shutdown sequence with custom signal handling:

// Import functions from shutdown-cleanup
import {
  addSignal,
  registerHandler,
  registerSignalHandler,
  setErrorHandlingStrategy,
  setCustomExitCode,
} from '@hypercliq/shutdown-cleanup'

// Register a phased shutdown handler which will be executed in the default (first) phase
registerHandler(
  async () => console.log('Phase 1: Release resources'),
  'releaseResources', // <= Specify a unique name for the handler
)

// Register a phased shutdown handler which will be executed in the second phase
registerHandler(
  async () => console.log('Phase 2: Close connections'),
  'closeConnections',
  2, // <= Specify the phase number
)

// Add extra lifecycle event to listen to, phased shutdown handlers will be executed for this event!
addSignal('beforeExit')

// Register a signal-specific handler for SIGUSR1
registerSignalHandler(
  'SIGUSR1',
  async (signal) => {
    console.log(`Handling ${signal} for debugging purposes`)
  },
  false, // <= false indicates that the application should not terminate
)

// Register a signal-specific handler for a custom signal raised somewhere in the application
registerSignalHandler(
  'customSignal', // <= Custom signal or event name
  async (signal) => {
    console.log(`Handling custom signal: ${signal}`)
  },
  true, // <= true indicates that the application should terminate
)

// Set the error handling strategy
setErrorHandlingStrategy('continue')

// Set a custom exit code
setCustomExitCode(42)

API

This section provides a detailed overview of the functions available in the shutdown-cleanup module, allowing for effective management of your application’s shutdown process.

addSignal(signal: string): boolean

Adds a new signal or event to the list of signals that will initiate the shutdown process.

addSignal('SIGUSR1')

removeSignal(signal: string): boolean

Removes a signal from the list, preventing it from initiating the shutdown process if received.

removeSignal('SIGUSR1')

registerHandler(handler: (signal: string) => Promise<void>, identifier?: string, phase?: number): void

Registers a shutdown handler that will be executed when the shutdown process begins. Handlers can be organized into phases. If no name is provided, the handler will be registered with a unique identifier. If no phase is specified, the handler will be executed in the default phase (1).

registerHandler(
  async (signal) => {
    console.log(`Handling shutdown for signal: ${signal}`)
  },
  'databaseCleanup',
  3, // <= Phase number, 1 is highest priority (default is 1)
)

removeHandler(identifier: string): boolean

Removes a previously registered handler by its identifier.

removeHandler('databaseCleanup')

registerSignalHandler(signal: string, handler: (signal: string) => Promise<void>, shouldTerminate: boolean): void

Registers a handler for a specific signal or event. This allows for custom logic to be executed without necessarily terminating the application.

registerSignalHandler(
  'SIGUSR1',
  async (signal) => {
    console.log(`Custom logic for ${signal}`)
  },
  false, // <= false indicates that the application should not terminate (default is true).
)

removeSignalHandler(signal: string): boolean

Removes a previously registered signal-specific handler.

removeSignalHandler('SIGUSR1')

setErrorHandlingStrategy(strategy: 'continue' | 'stop'): void

Sets the global error handling strategy for the shutdown process. If an error occurs in a handler, choose between continuing with remaining handlers after the error ('continue') or stopping the processing of handlers altogether ('stop').

setErrorHandlingStrategy('continue')

setCustomExitCode(code: number): void

Sets a custom exit code to be used when the application exits after the shutdown process is complete.

setCustomExitCode(42)

listHandlers(): object

Returns a list of all registered handlers, including both phased and signal-specific handlers.

const allHandlers = listHandlers()
console.log(allHandlers)

listSignals(): string[]

Provides a list of all signals and events that are currently being listened to by the module.

const signals = listSignals()
console.log(signals)

setShutdownTimeout(timeout: number): void

Specifies a timeout for the shutdown process. If the shutdown does not complete within this timeframe, the process is forcibly terminated.

setShutdownTimeout(20000) // 20 seconds - default is 30 seconds

Conclusion

The shutdown-cleanup module provides a robust and flexible solution for managing graceful shutdowns in Node.js applications. By following the best practices outlined in this guide and leveraging the advanced features of the module, you can ensure that your application gracefully handles shutdowns, even in complex scenarios.