🔧 Implement robust cleanup system for Chromium process management
Major fixes for browser automation resource management: - Chromium processes accumulating over time during automated trading - Resource consumption growing after extended automation cycles - Incomplete cleanup during analysis operations New Components: - lib/enhanced-screenshot-robust.ts: Screenshot service with guaranteed cleanup - lib/automated-cleanup-service.ts: Background process monitoring - lib/auto-trading-service.ts: Comprehensive trading automation - ROBUST_CLEANUP_IMPLEMENTATION.md: Complete documentation - Finally blocks guarantee cleanup execution even during errors - Active session tracking prevents orphaned browser instances - Multiple kill strategies (graceful → force → process cleanup) - Timeout protection prevents hanging cleanup operations - Background monitoring every 30s catches missed processes - lib/aggressive-cleanup.ts: Improved with multiple cleanup strategies - app/api/enhanced-screenshot/route.js: Added finally block guarantees - lib/automation-service.ts: Updated for integration - validate-robust-cleanup.js: Implementation validation - test-robust-cleanup.js: Comprehensive cleanup testing The Chromium process accumulation issue is now resolved with guaranteed cleanup!
This commit is contained in:
128
lib/automated-cleanup-service.ts
Normal file
128
lib/automated-cleanup-service.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
import { exec } from 'child_process'
|
||||
import { promisify } from 'util'
|
||||
|
||||
const execAsync = promisify(exec)
|
||||
|
||||
export class AutomatedCleanupService {
|
||||
private cleanupInterval: NodeJS.Timeout | null = null
|
||||
private isRunning = false
|
||||
|
||||
start(intervalMs: number = 60000) { // Default: every minute
|
||||
if (this.isRunning) {
|
||||
console.log('⚠️ Cleanup service already running')
|
||||
return
|
||||
}
|
||||
|
||||
this.isRunning = true
|
||||
console.log(`🚀 Starting automated cleanup service (interval: ${intervalMs}ms)`)
|
||||
|
||||
this.cleanupInterval = setInterval(async () => {
|
||||
await this.performCleanup()
|
||||
}, intervalMs)
|
||||
|
||||
// Run initial cleanup
|
||||
this.performCleanup().catch(console.error)
|
||||
}
|
||||
|
||||
stop() {
|
||||
if (this.cleanupInterval) {
|
||||
clearInterval(this.cleanupInterval)
|
||||
this.cleanupInterval = null
|
||||
}
|
||||
this.isRunning = false
|
||||
console.log('🛑 Automated cleanup service stopped')
|
||||
}
|
||||
|
||||
private async performCleanup(): Promise<void> {
|
||||
try {
|
||||
console.log('🧹 Running periodic browser cleanup...')
|
||||
|
||||
// Check for chromium processes
|
||||
const { stdout } = await execAsync('ps aux | grep -E "(chromium|chrome)" | grep -v grep | wc -l')
|
||||
const processCount = parseInt(stdout.trim(), 10)
|
||||
|
||||
if (processCount > 0) {
|
||||
console.log(`🔍 Found ${processCount} browser processes running`)
|
||||
|
||||
// Get process list for logging
|
||||
try {
|
||||
const { stdout: processList } = await execAsync('ps aux | grep -E "(chromium|chrome)" | grep -v grep | head -10')
|
||||
console.log('📋 Current browser processes:')
|
||||
console.log(processList)
|
||||
} catch (listError) {
|
||||
console.log('Could not list processes:', listError)
|
||||
}
|
||||
|
||||
// Kill old/stuck processes
|
||||
const killCommands = [
|
||||
// Graceful shutdown first
|
||||
'pkill -TERM -f "chromium.*--remote-debugging-port" 2>/dev/null || true',
|
||||
'pkill -TERM -f "chromium.*--user-data-dir" 2>/dev/null || true',
|
||||
|
||||
// Wait a bit
|
||||
'sleep 2',
|
||||
|
||||
// Force kill stubborn processes
|
||||
'pkill -KILL -f "chromium.*--remote-debugging-port" 2>/dev/null || true',
|
||||
'pkill -KILL -f "chromium.*--user-data-dir" 2>/dev/null || true',
|
||||
'pkill -KILL -f "/usr/lib/chromium/chromium" 2>/dev/null || true',
|
||||
|
||||
// Clean up zombies
|
||||
'pkill -9 -f "chromium.*defunct" 2>/dev/null || true'
|
||||
]
|
||||
|
||||
for (const command of killCommands) {
|
||||
try {
|
||||
if (command === 'sleep 2') {
|
||||
await new Promise(resolve => setTimeout(resolve, 2000))
|
||||
} else {
|
||||
await execAsync(command)
|
||||
}
|
||||
} catch (error) {
|
||||
// Ignore errors from kill commands
|
||||
}
|
||||
}
|
||||
|
||||
// Check results
|
||||
const { stdout: afterCleanup } = await execAsync('ps aux | grep -E "(chromium|chrome)" | grep -v grep | wc -l')
|
||||
const remainingProcesses = parseInt(afterCleanup.trim(), 10)
|
||||
|
||||
if (remainingProcesses < processCount) {
|
||||
console.log(`✅ Cleanup successful: ${processCount - remainingProcesses} processes terminated`)
|
||||
} else {
|
||||
console.log(`⚠️ No processes were terminated, ${remainingProcesses} still running`)
|
||||
}
|
||||
|
||||
// Clean up temp files
|
||||
try {
|
||||
await execAsync('rm -rf /tmp/.org.chromium.Chromium.* 2>/dev/null || true')
|
||||
await execAsync('rm -rf /tmp/puppeteer_dev_chrome_profile-* 2>/dev/null || true')
|
||||
console.log('🗑️ Cleaned up temporary files')
|
||||
} catch (tempCleanupError) {
|
||||
console.log('⚠️ Could not clean temp files:', tempCleanupError)
|
||||
}
|
||||
|
||||
} else {
|
||||
console.log('✅ No browser processes found - system clean')
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Error during periodic cleanup:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// Force cleanup method for immediate use
|
||||
async forceCleanup(): Promise<void> {
|
||||
console.log('🔧 Running force cleanup...')
|
||||
await this.performCleanup()
|
||||
}
|
||||
}
|
||||
|
||||
// Create singleton instance
|
||||
export const automatedCleanupService = new AutomatedCleanupService()
|
||||
|
||||
// Auto-start in Docker environment
|
||||
if (process.env.DOCKER_ENV === 'true') {
|
||||
console.log('🐳 Docker environment detected - starting automated cleanup service')
|
||||
automatedCleanupService.start(30000) // Every 30 seconds in Docker
|
||||
}
|
||||
Reference in New Issue
Block a user