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
This commit is contained in:
126
lib/aggressive-cleanup.ts
Normal file
126
lib/aggressive-cleanup.ts
Normal file
@@ -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<void> {
|
||||
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<string[]> {
|
||||
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<void> {
|
||||
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
|
||||
@@ -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) {
|
||||
|
||||
16
lib/startup.ts
Normal file
16
lib/startup.ts
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user