feat: auto-cleanup orphaned chroma-mcp processes on worker startup
Enhancement to process leak fix from v7.1.9 - automatically detects and
kills orphaned chroma-mcp processes when the worker starts.
Changes:
- Added cleanupOrphanedProcesses() method to WorkerService
- Scans for existing chroma-mcp processes on startup
- Kills all found processes before creating new ones
- Logs cleanup activity (process count and PIDs)
- Non-fatal error handling (continues on cleanup failure)
Benefits:
- Automatically recovers from pre-7.1.9 process leaks
- Ensures clean slate on every worker restart
- No manual intervention needed to cleanup orphans
- Prevents accumulation even if v7.1.9 close() fails
Verified working in logs:
[INFO] [SYSTEM] Cleaning up orphaned chroma-mcp processes {count=2, pids=33753,33750}
[INFO] [SYSTEM] Orphaned processes cleaned up {count=2}
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -13,6 +13,10 @@ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
||||
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
||||
import { getWorkerPort, getWorkerHost } from '../shared/worker-utils.js';
|
||||
import { logger } from '../utils/logger.js';
|
||||
import { exec } from 'child_process';
|
||||
import { promisify } from 'util';
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
// Import composed domain services
|
||||
import { DatabaseManager } from './worker/DatabaseManager.js';
|
||||
@@ -149,6 +153,52 @@ export class WorkerService {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clean up orphaned chroma-mcp processes from previous worker sessions
|
||||
* Prevents process accumulation and memory leaks
|
||||
*/
|
||||
private async cleanupOrphanedProcesses(): Promise<void> {
|
||||
try {
|
||||
// Find all chroma-mcp processes
|
||||
const { stdout } = await execAsync('ps aux | grep "chroma-mcp" | grep -v grep || true');
|
||||
|
||||
if (!stdout.trim()) {
|
||||
logger.debug('SYSTEM', 'No orphaned chroma-mcp processes found');
|
||||
return;
|
||||
}
|
||||
|
||||
const lines = stdout.trim().split('\n');
|
||||
const pids: number[] = [];
|
||||
|
||||
for (const line of lines) {
|
||||
const parts = line.trim().split(/\s+/);
|
||||
if (parts.length > 1) {
|
||||
const pid = parseInt(parts[1], 10);
|
||||
if (!isNaN(pid)) {
|
||||
pids.push(pid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pids.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
logger.info('SYSTEM', 'Cleaning up orphaned chroma-mcp processes', {
|
||||
count: pids.length,
|
||||
pids
|
||||
});
|
||||
|
||||
// Kill all found processes
|
||||
await execAsync(`kill ${pids.join(' ')}`);
|
||||
|
||||
logger.info('SYSTEM', 'Orphaned processes cleaned up', { count: pids.length });
|
||||
} catch (error) {
|
||||
// Non-fatal - log and continue
|
||||
logger.warn('SYSTEM', 'Failed to cleanup orphaned processes', {}, error as Error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the worker service
|
||||
*/
|
||||
@@ -173,6 +223,9 @@ export class WorkerService {
|
||||
* Background initialization - runs after HTTP server is listening
|
||||
*/
|
||||
private async initializeBackground(): Promise<void> {
|
||||
// Clean up any orphaned chroma-mcp processes BEFORE starting our own
|
||||
await this.cleanupOrphanedProcesses();
|
||||
|
||||
// Initialize database (once, stays open)
|
||||
await this.dbManager.initialize();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user