feat: register signal handlers in constructor for earlier cleanup protection

Addresses review feedback to register signal handlers earlier in the
lifecycle. Previously handlers were registered in start() after HTTP
server initialization, leaving a vulnerability window.

Benefits:
- Signal handlers now active immediately after WorkerService construction
- Protects against signals received during initialization (DB setup,
  HTTP server binding, background initialization)
- Prevents orphaned chroma-mcp processes even if killed during startup
- shutdown() method is defensive and safe to call at any stage

This closes the gap where a SIGTERM/SIGINT during initializeBackground()
could leave chroma-mcp subprocess running without cleanup.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Alex Newman
2025-12-29 16:17:58 -05:00
parent 9eedbd4fbd
commit 2bb07dd41a
2 changed files with 32 additions and 23 deletions
File diff suppressed because one or more lines are too long
+30 -21
View File
@@ -316,6 +316,36 @@ export class WorkerService {
this.setupMiddleware();
this.setupRoutes();
// Register signal handlers early to ensure cleanup even if start() hasn't completed
// The shutdown() method is defensive and safe to call at any initialization stage
this.registerSignalHandlers();
}
/**
* Register signal handlers for graceful shutdown
* Called in constructor to ensure cleanup even if start() hasn't completed
*/
private registerSignalHandlers(): void {
const handleShutdown = async (signal: string) => {
if (this.isShuttingDown) {
logger.warn('SYSTEM', `Received ${signal} but shutdown already in progress`);
return;
}
this.isShuttingDown = true;
logger.info('SYSTEM', `Received ${signal}, shutting down...`);
try {
await this.shutdown();
process.exit(0);
} catch (error) {
logger.error('SYSTEM', 'Error during shutdown', {}, error as Error);
process.exit(1);
}
};
process.on('SIGTERM', () => handleShutdown('SIGTERM'));
process.on('SIGINT', () => handleShutdown('SIGINT'));
}
/**
@@ -596,27 +626,6 @@ export class WorkerService {
logger.info('SYSTEM', 'Worker started', { host, port, pid: process.pid });
// Register signal handlers to ensure cleanup on exit
const handleShutdown = async (signal: string) => {
if (this.isShuttingDown) {
logger.warn('SYSTEM', `Received ${signal} but shutdown already in progress`);
return;
}
this.isShuttingDown = true;
logger.info('SYSTEM', `Received ${signal}, shutting down...`);
try {
await this.shutdown();
process.exit(0);
} catch (error) {
logger.error('SYSTEM', 'Error during shutdown', {}, error as Error);
process.exit(1);
}
};
process.on('SIGTERM', () => handleShutdown('SIGTERM'));
process.on('SIGINT', () => handleShutdown('SIGINT'));
// Do slow initialization in background (non-blocking)
this.initializeBackground().catch((error) => {
logger.error('SYSTEM', 'Background initialization failed', {}, error as Error);