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
This commit is contained in:
@@ -206,6 +206,17 @@ export async function POST(request) {
|
|||||||
// Clean up session
|
// Clean up session
|
||||||
setTimeout(() => progressTracker.deleteSession(sessionId), 2000)
|
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)
|
return NextResponse.json(result)
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -113,6 +113,17 @@ export async function POST(request) {
|
|||||||
message: `Successfully captured ${screenshots.length} screenshot(s)${analysis ? ' with AI analysis' : ''}`
|
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)
|
return NextResponse.json(result)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Enhanced screenshot API error:', error)
|
console.error('Enhanced screenshot API error:', error)
|
||||||
|
|||||||
@@ -30,34 +30,38 @@ class AggressiveCleanup {
|
|||||||
this.isInitialized = true
|
this.isInitialized = true
|
||||||
console.log('🚀 Starting aggressive cleanup system')
|
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') {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up every 5 minutes
|
// Production: Clean up every 10 minutes (longer intervals)
|
||||||
this.cleanupInterval = setInterval(async () => {
|
this.cleanupInterval = setInterval(async () => {
|
||||||
try {
|
try {
|
||||||
await this.cleanupOrphanedProcesses()
|
await this.cleanupOrphanedProcesses()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error in periodic cleanup:', 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(() => {
|
setTimeout(() => {
|
||||||
this.cleanupOrphanedProcesses().catch(console.error)
|
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<void> {
|
async cleanupOrphanedProcesses(): Promise<void> {
|
||||||
if (this.isRunning) return
|
if (this.isRunning) return
|
||||||
|
|
||||||
this.isRunning = true
|
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 {
|
try {
|
||||||
// Check for active analysis sessions
|
// Check for active analysis sessions
|
||||||
@@ -85,12 +89,35 @@ class AggressiveCleanup {
|
|||||||
|
|
||||||
for (const pid of chromiumProcesses) {
|
for (const pid of chromiumProcesses) {
|
||||||
try {
|
try {
|
||||||
await execAsync(`kill -9 ${pid}`)
|
if (isDevelopment) {
|
||||||
console.log(`✅ Killed process ${pid}`)
|
// 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) {
|
} catch (error) {
|
||||||
// Process might already be dead
|
// 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
|
// Clean up temp directories
|
||||||
@@ -110,7 +137,7 @@ class AggressiveCleanup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error in aggressive cleanup:', error)
|
console.error(`Error in ${cleanupType} cleanup:`, error)
|
||||||
} finally {
|
} finally {
|
||||||
this.isRunning = false
|
this.isRunning = false
|
||||||
}
|
}
|
||||||
@@ -119,7 +146,7 @@ class AggressiveCleanup {
|
|||||||
private async findChromiumProcesses(): Promise<string[]> {
|
private async findChromiumProcesses(): Promise<string[]> {
|
||||||
try {
|
try {
|
||||||
const { stdout } = await execAsync('ps aux | grep -E "(chromium|chrome)" | grep -v grep | awk \'{print $2}\'')
|
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) {
|
} catch (error) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
@@ -146,6 +173,16 @@ class AggressiveCleanup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New method for on-demand cleanup after analysis
|
||||||
|
async runPostAnalysisCleanup(): Promise<void> {
|
||||||
|
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 {
|
stop(): void {
|
||||||
if (this.cleanupInterval) {
|
if (this.cleanupInterval) {
|
||||||
clearInterval(this.cleanupInterval)
|
clearInterval(this.cleanupInterval)
|
||||||
|
|||||||
@@ -718,6 +718,19 @@ Analyze all provided screenshots comprehensively and return only the JSON respon
|
|||||||
setTimeout(() => progressTracker.deleteSession(sessionId), 1000)
|
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 {
|
return {
|
||||||
screenshots,
|
screenshots,
|
||||||
analysis
|
analysis
|
||||||
|
|||||||
Reference in New Issue
Block a user