From 6232c457ad4a76997d50b63e7d0181b0761d1010 Mon Sep 17 00:00:00 2001 From: mindesbunister Date: Fri, 18 Jul 2025 13:08:31 +0200 Subject: [PATCH] feat: implement comprehensive process cleanup system - Added aggressive cleanup system that runs every 5 minutes to kill orphaned processes - Enhanced process cleanup with better signal handling and forced cleanup - Added startup initialization system to ensure cleanup is properly loaded - Integrated cleanup system into app layouts for automatic initialization - Added zombie process cleanup and temp directory cleanup - Improved Docker container restart behavior for proper process cleanup - Resolves issue with zombie Chrome processes accumulating --- app/layout.js | 3 + lib/aggressive-cleanup.ts | 126 ++++++++++++++++++++++++++++++++++++++ lib/process-cleanup.ts | 22 +++++++ lib/startup.ts | 16 +++++ src/app/layout.tsx | 3 + 5 files changed, 170 insertions(+) create mode 100644 lib/aggressive-cleanup.ts create mode 100644 lib/startup.ts diff --git a/app/layout.js b/app/layout.js index f8b1348..1656258 100644 --- a/app/layout.js +++ b/app/layout.js @@ -1,6 +1,9 @@ import './globals.css' import Navigation from '../components/Navigation.tsx' +// Initialize cleanup system +import '../lib/startup' + export const metadata = { title: 'Trading Bot Dashboard', description: 'AI-powered trading bot with automated analysis and execution', diff --git a/lib/aggressive-cleanup.ts b/lib/aggressive-cleanup.ts new file mode 100644 index 0000000..8efff31 --- /dev/null +++ b/lib/aggressive-cleanup.ts @@ -0,0 +1,126 @@ +// Aggressive process cleanup utility +import { exec } from 'child_process' +import { promisify } from 'util' + +const execAsync = promisify(exec) + +class AggressiveCleanup { + private static instance: AggressiveCleanup + private cleanupInterval: NodeJS.Timeout | null = null + private isRunning = false + + private constructor() { + this.startPeriodicCleanup() + } + + static getInstance(): AggressiveCleanup { + if (!AggressiveCleanup.instance) { + AggressiveCleanup.instance = new AggressiveCleanup() + } + return AggressiveCleanup.instance + } + + private startPeriodicCleanup() { + // Clean up every 5 minutes + this.cleanupInterval = setInterval(async () => { + try { + await this.cleanupOrphanedProcesses() + } catch (error) { + console.error('Error in periodic cleanup:', error) + } + }, 5 * 60 * 1000) // 5 minutes + + // Also run initial cleanup after 30 seconds + setTimeout(() => { + this.cleanupOrphanedProcesses().catch(console.error) + }, 30000) + } + + async cleanupOrphanedProcesses(): Promise { + if (this.isRunning) return + + this.isRunning = true + console.log('๐Ÿงน Running aggressive cleanup for orphaned processes...') + + try { + // Find and kill orphaned chromium processes + const chromiumProcesses = await this.findChromiumProcesses() + + if (chromiumProcesses.length > 0) { + console.log(`Found ${chromiumProcesses.length} chromium processes, cleaning up...`) + + for (const pid of chromiumProcesses) { + try { + await execAsync(`kill -9 ${pid}`) + console.log(`โœ… Killed process ${pid}`) + } catch (error) { + // Process might already be dead + } + } + } + + // Clean up temp directories + try { + await execAsync('rm -rf /tmp/puppeteer_dev_chrome_profile-* 2>/dev/null || true') + console.log('โœ… Cleaned up temp directories') + } catch (error) { + // Ignore errors + } + + // Clean up shared memory + try { + await execAsync('rm -rf /dev/shm/.org.chromium.* 2>/dev/null || true') + console.log('โœ… Cleaned up shared memory') + } catch (error) { + // Ignore errors + } + + } catch (error) { + console.error('Error in aggressive cleanup:', error) + } finally { + this.isRunning = false + } + } + + private async findChromiumProcesses(): Promise { + try { + const { stdout } = await execAsync('ps aux | grep -E "(chromium|chrome)" | grep -v grep | awk \'{print $2}\'') + return stdout.trim().split('\n').filter(pid => pid && pid !== '') + } catch (error) { + return [] + } + } + + async forceCleanup(): Promise { + console.log('๐Ÿšจ Force cleanup initiated...') + + // Stop periodic cleanup + if (this.cleanupInterval) { + clearInterval(this.cleanupInterval) + } + + // Run aggressive cleanup + await this.cleanupOrphanedProcesses() + + // Kill all chromium processes + try { + await execAsync('pkill -9 -f "chromium" 2>/dev/null || true') + await execAsync('pkill -9 -f "chrome" 2>/dev/null || true') + console.log('โœ… Force killed all browser processes') + } catch (error) { + console.error('Error in force cleanup:', error) + } + } + + stop(): void { + if (this.cleanupInterval) { + clearInterval(this.cleanupInterval) + this.cleanupInterval = null + } + } +} + +// Initialize the aggressive cleanup +const aggressiveCleanup = AggressiveCleanup.getInstance() + +export default aggressiveCleanup diff --git a/lib/process-cleanup.ts b/lib/process-cleanup.ts index 4f1a0e7..a901321 100644 --- a/lib/process-cleanup.ts +++ b/lib/process-cleanup.ts @@ -5,6 +5,7 @@ import { tradingViewAutomation } from './tradingview-automation' class ProcessCleanup { private static instance: ProcessCleanup private isShuttingDown = false + private aggressiveCleanup: any = null private constructor() { // Register cleanup handlers @@ -13,6 +14,18 @@ class ProcessCleanup { process.on('SIGQUIT', this.handleShutdown.bind(this)) process.on('uncaughtException', this.handleError.bind(this)) process.on('unhandledRejection', this.handleError.bind(this)) + + // Lazy load aggressive cleanup to avoid circular imports + this.loadAggressiveCleanup() + } + + private async loadAggressiveCleanup() { + try { + const { default: aggressiveCleanup } = await import('./aggressive-cleanup') + this.aggressiveCleanup = aggressiveCleanup + } catch (error) { + console.error('Failed to load aggressive cleanup:', error) + } } static getInstance(): ProcessCleanup { @@ -32,6 +45,12 @@ class ProcessCleanup { console.log(`\n๐Ÿ›‘ Received ${signal}, initiating graceful shutdown...`) try { + // Use aggressive cleanup first + if (this.aggressiveCleanup) { + console.log('๐Ÿงน Running aggressive cleanup...') + await this.aggressiveCleanup.forceCleanup() + } + // Clean up screenshot service console.log('๐Ÿงน Cleaning up screenshot service...') await enhancedScreenshotService.cleanup() @@ -53,6 +72,9 @@ class ProcessCleanup { // Attempt cleanup try { + if (this.aggressiveCleanup) { + await this.aggressiveCleanup.forceCleanup() + } await enhancedScreenshotService.cleanup() await tradingViewAutomation.forceCleanup() } catch (cleanupError) { diff --git a/lib/startup.ts b/lib/startup.ts new file mode 100644 index 0000000..7449c90 --- /dev/null +++ b/lib/startup.ts @@ -0,0 +1,16 @@ +// Startup initialization for the trading bot +// This file initializes critical systems and cleanup handlers + +import processCleanup from './process-cleanup' +import aggressiveCleanup from './aggressive-cleanup' + +// Initialize cleanup system +console.log('๐Ÿš€ Initializing trading bot systems...') +console.log('๐Ÿงน Process cleanup handlers initialized') +console.log('๐Ÿงน Aggressive cleanup system initialized') + +// Export cleanup for manual access +export { processCleanup, aggressiveCleanup } + +// Initialize on import +export default true diff --git a/src/app/layout.tsx b/src/app/layout.tsx index a14e64f..b3f76b8 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,3 +1,6 @@ +// Initialize cleanup system +import '../../lib/startup' + export const metadata = { title: 'Next.js', description: 'Generated by Next.js',