feat: implement robust browser process cleanup system
- Add cleanup-chromium.sh script for manual zombie process cleanup - Add docker-entrypoint.sh with signal handlers for graceful shutdown - Add lib/process-cleanup.ts for automatic cleanup on app termination - Enhanced forceCleanup() method in tradingview-automation.ts: - Individual page closing before browser termination - Force kill remaining processes with SIGKILL - Reset operation locks after cleanup - Improved browser launch args to prevent zombie processes: - Better crash reporter handling - Enhanced background process management - Removed problematic --single-process flag - Updated Dockerfile to use new entrypoint with cleanup handlers - Set DOCKER_ENV environment variable for container detection - Add proper signal handling (SIGINT, SIGTERM, SIGQUIT) - Automatic cleanup of temporary Puppeteer profiles Resolves zombie Chromium process accumulation issue
This commit is contained in:
74
lib/process-cleanup.ts
Normal file
74
lib/process-cleanup.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
// Process cleanup utility for graceful shutdown
|
||||
import { enhancedScreenshotService } from './enhanced-screenshot'
|
||||
import { tradingViewAutomation } from './tradingview-automation'
|
||||
|
||||
class ProcessCleanup {
|
||||
private static instance: ProcessCleanup
|
||||
private isShuttingDown = false
|
||||
|
||||
private constructor() {
|
||||
// Register cleanup handlers
|
||||
process.on('SIGINT', this.handleShutdown.bind(this))
|
||||
process.on('SIGTERM', this.handleShutdown.bind(this))
|
||||
process.on('SIGQUIT', this.handleShutdown.bind(this))
|
||||
process.on('uncaughtException', this.handleError.bind(this))
|
||||
process.on('unhandledRejection', this.handleError.bind(this))
|
||||
}
|
||||
|
||||
static getInstance(): ProcessCleanup {
|
||||
if (!ProcessCleanup.instance) {
|
||||
ProcessCleanup.instance = new ProcessCleanup()
|
||||
}
|
||||
return ProcessCleanup.instance
|
||||
}
|
||||
|
||||
private async handleShutdown(signal: string): Promise<void> {
|
||||
if (this.isShuttingDown) {
|
||||
console.log('Already shutting down, forcing exit...')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
this.isShuttingDown = true
|
||||
console.log(`\n🛑 Received ${signal}, initiating graceful shutdown...`)
|
||||
|
||||
try {
|
||||
// Clean up screenshot service
|
||||
console.log('🧹 Cleaning up screenshot service...')
|
||||
await enhancedScreenshotService.cleanup()
|
||||
|
||||
// Clean up trading view automation
|
||||
console.log('🧹 Cleaning up TradingView automation...')
|
||||
await tradingViewAutomation.forceCleanup()
|
||||
|
||||
console.log('✅ Graceful shutdown completed')
|
||||
process.exit(0)
|
||||
} catch (error) {
|
||||
console.error('❌ Error during shutdown:', error)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
private async handleError(error: Error): Promise<void> {
|
||||
console.error('❌ Unhandled error:', error)
|
||||
|
||||
// Attempt cleanup
|
||||
try {
|
||||
await enhancedScreenshotService.cleanup()
|
||||
await tradingViewAutomation.forceCleanup()
|
||||
} catch (cleanupError) {
|
||||
console.error('❌ Error during error cleanup:', cleanupError)
|
||||
}
|
||||
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
// Manual cleanup method
|
||||
async cleanup(): Promise<void> {
|
||||
await this.handleShutdown('MANUAL')
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the cleanup handler
|
||||
const processCleanup = ProcessCleanup.getInstance()
|
||||
|
||||
export default processCleanup
|
||||
Reference in New Issue
Block a user