From 74b0087f1747e89173fe400b0d68b6cd5143aeef Mon Sep 17 00:00:00 2001 From: mindesbunister Date: Fri, 18 Jul 2025 19:11:15 +0200 Subject: [PATCH] feat: implement on-demand cleanup triggered after analysis completion - Replace time-based cleanup with on-demand cleanup in development mode - Cleanup is triggered immediately after AI analysis completes - Added runPostAnalysisCleanup() method to aggressive-cleanup service - Cleanup triggers added to both single and batch analysis endpoints - More efficient: cleanup happens only when needed, not on timer - Prevents zombie processes without interfering with active analysis - Production mode still uses periodic cleanup as backup (10 min intervals) - Gentle cleanup in development: SIGTERM first, then SIGKILL if needed --- app/api/batch-analysis/route.js | 11 +++++ app/api/enhanced-screenshot/route.js | 11 +++++ lib/aggressive-cleanup.ts | 61 ++++++++++++++++++++++------ lib/ai-analysis.ts | 13 ++++++ 4 files changed, 84 insertions(+), 12 deletions(-) diff --git a/app/api/batch-analysis/route.js b/app/api/batch-analysis/route.js index efd17a5..9cb3fe6 100644 --- a/app/api/batch-analysis/route.js +++ b/app/api/batch-analysis/route.js @@ -206,6 +206,17 @@ export async function POST(request) { // Clean up session setTimeout(() => progressTracker.deleteSession(sessionId), 2000) + // Trigger post-analysis cleanup in development mode + if (process.env.NODE_ENV === 'development') { + try { + const { default: aggressiveCleanup } = await import('../../../lib/aggressive-cleanup') + // Run cleanup in background, don't block the response + aggressiveCleanup.runPostAnalysisCleanup().catch(console.error) + } catch (cleanupError) { + console.error('Error triggering post-batch-analysis cleanup:', cleanupError) + } + } + return NextResponse.json(result) } catch (error) { diff --git a/app/api/enhanced-screenshot/route.js b/app/api/enhanced-screenshot/route.js index 44a7e6a..54e5ac3 100644 --- a/app/api/enhanced-screenshot/route.js +++ b/app/api/enhanced-screenshot/route.js @@ -113,6 +113,17 @@ export async function POST(request) { message: `Successfully captured ${screenshots.length} screenshot(s)${analysis ? ' with AI analysis' : ''}` } + // Trigger post-analysis cleanup in development mode + if (process.env.NODE_ENV === 'development') { + try { + const { default: aggressiveCleanup } = await import('../../../lib/aggressive-cleanup') + // Run cleanup in background, don't block the response + aggressiveCleanup.runPostAnalysisCleanup().catch(console.error) + } catch (cleanupError) { + console.error('Error triggering post-analysis cleanup:', cleanupError) + } + } + return NextResponse.json(result) } catch (error) { console.error('Enhanced screenshot API error:', error) diff --git a/lib/aggressive-cleanup.ts b/lib/aggressive-cleanup.ts index b3bfbda..369c8e3 100644 --- a/lib/aggressive-cleanup.ts +++ b/lib/aggressive-cleanup.ts @@ -30,34 +30,38 @@ class AggressiveCleanup { this.isInitialized = true console.log('๐Ÿš€ Starting aggressive cleanup system') - // In development, disable aggressive cleanup to avoid interfering with analysis + // In development, use on-demand cleanup instead of periodic if (process.env.NODE_ENV === 'development') { - console.log('๐Ÿ”’ Aggressive cleanup disabled in development mode') + console.log('๐Ÿ”ง Development mode: Using on-demand cleanup (triggered after analysis)') + console.log('โœ… On-demand cleanup system ready') return } - // Clean up every 5 minutes + // Production: Clean up every 10 minutes (longer intervals) this.cleanupInterval = setInterval(async () => { try { await this.cleanupOrphanedProcesses() } catch (error) { console.error('Error in periodic cleanup:', error) } - }, 5 * 60 * 1000) // 5 minutes + }, 10 * 60 * 1000) // 10 minutes - // Also run initial cleanup after 30 seconds + // Also run initial cleanup after 60 seconds setTimeout(() => { this.cleanupOrphanedProcesses().catch(console.error) - }, 30000) + }, 60000) - console.log('โœ… Aggressive cleanup system started (5 min intervals)') + console.log('โœ… Periodic cleanup system started (10 min intervals)') } async cleanupOrphanedProcesses(): Promise { if (this.isRunning) return this.isRunning = true - console.log('๐Ÿงน Running aggressive cleanup for orphaned processes...') + const isDevelopment = process.env.NODE_ENV === 'development' + const cleanupType = isDevelopment ? 'gentle' : 'aggressive' + + console.log(`๐Ÿงน Running ${cleanupType} cleanup for orphaned processes...`) try { // Check for active analysis sessions @@ -85,12 +89,35 @@ class AggressiveCleanup { for (const pid of chromiumProcesses) { try { - await execAsync(`kill -9 ${pid}`) - console.log(`โœ… Killed process ${pid}`) + if (isDevelopment) { + // In development, use gentler SIGTERM first + console.log(`๐Ÿ”ง Dev mode: Gentle shutdown of process ${pid}`) + await execAsync(`kill -TERM ${pid}`) + // Give process 3 seconds to shut down gracefully + await new Promise(resolve => setTimeout(resolve, 3000)) + + // Check if process is still running + try { + await execAsync(`kill -0 ${pid}`) + // Process still running, force kill + console.log(`โš ๏ธ Process ${pid} didn't shut down gracefully, force killing`) + await execAsync(`kill -9 ${pid}`) + } catch { + // Process already dead, that's good + console.log(`โœ… Process ${pid} shut down gracefully`) + } + } else { + // Production: immediate force kill + await execAsync(`kill -9 ${pid}`) + console.log(`โœ… Killed process ${pid}`) + } } catch (error) { // Process might already be dead + console.log(`โ„น๏ธ Process ${pid} may already be terminated`) } } + } else { + console.log('โœ… No orphaned chromium processes found') } // Clean up temp directories @@ -110,7 +137,7 @@ class AggressiveCleanup { } } catch (error) { - console.error('Error in aggressive cleanup:', error) + console.error(`Error in ${cleanupType} cleanup:`, error) } finally { this.isRunning = false } @@ -119,7 +146,7 @@ class AggressiveCleanup { 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 !== '') + return stdout.trim().split('\n').filter((pid: string) => pid && pid !== '') } catch (error) { return [] } @@ -146,6 +173,16 @@ class AggressiveCleanup { } } + // New method for on-demand cleanup after analysis + async runPostAnalysisCleanup(): Promise { + console.log('๐Ÿงน Post-analysis cleanup triggered...') + + // Small delay to ensure analysis processes are fully closed + await new Promise(resolve => setTimeout(resolve, 2000)) + + await this.cleanupOrphanedProcesses() + } + stop(): void { if (this.cleanupInterval) { clearInterval(this.cleanupInterval) diff --git a/lib/ai-analysis.ts b/lib/ai-analysis.ts index 6424583..ecb4776 100644 --- a/lib/ai-analysis.ts +++ b/lib/ai-analysis.ts @@ -718,6 +718,19 @@ Analyze all provided screenshots comprehensively and return only the JSON respon setTimeout(() => progressTracker.deleteSession(sessionId), 1000) } + // Trigger post-analysis cleanup in development mode + if (process.env.NODE_ENV === 'development') { + try { + // Dynamic import to avoid circular dependencies + const aggressiveCleanupModule = await import('./aggressive-cleanup') + const aggressiveCleanup = aggressiveCleanupModule.default + // Run cleanup in background, don't block the response + aggressiveCleanup.runPostAnalysisCleanup().catch(console.error) + } catch (cleanupError) { + console.error('Error triggering post-analysis cleanup:', cleanupError) + } + } + return { screenshots, analysis