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:
mindesbunister
2025-07-18 13:08:31 +02:00
parent 186cb6355c
commit 6232c457ad
5 changed files with 170 additions and 0 deletions

126
lib/aggressive-cleanup.ts Normal file
View 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