From 32f9d9834019d00a41074d9971f4a333f292fe70 Mon Sep 17 00:00:00 2001 From: mindesbunister Date: Sat, 19 Jul 2025 00:08:39 +0200 Subject: [PATCH] Fix cleanup process timing and coordination with analysis sessions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CRITICAL BUG FIX: Cleanup process was interfering with active analysis sessions - Aggressive cleanup was running during active analysis, causing navigation failures - Progress tracking was not properly coordinated with cleanup system - No session state checking before process termination 1. AUTOMATION SERVICE COORDINATION: - Added proper progress tracking to automation cycles - Created unique session IDs for each analysis run - Integrated with progressTracker for session state management - Added post-analysis cleanup triggers with proper timing 2. ENHANCED CLEANUP INTELLIGENCE: - Improved session checking with detailed logging of active sessions - Added process age filtering in development mode (only kill >5min old processes) - Better error handling when progress tracker import fails - More granular cleanup control with session state awareness 3. TIMING IMPROVEMENTS: - Post-analysis cleanup now waits for session completion - Added proper delays between analysis phases - Implemented graceful cleanup deferral when sessions are active - Added delayed cleanup fallback for stuck sessions 4. DEVELOPMENT MODE SAFETY: - Gentler SIGTERM โ†’ SIGKILL progression for development - Only clean processes older than 5 minutes during dev - Better logging of process age and cleanup decisions - Safer fallback behavior when session tracking fails This resolves the 'Failed to navigate to layout' errors by ensuring cleanup doesn't interfere with active browser sessions during analysis. --- lib/aggressive-cleanup.ts | 111 ++++++++++++++++++++++++++++--- lib/automation-service-simple.ts | 68 +++++++++++++++++-- prisma/prisma/dev.db | Bin 172032 -> 172032 bytes 3 files changed, 164 insertions(+), 15 deletions(-) diff --git a/lib/aggressive-cleanup.ts b/lib/aggressive-cleanup.ts index 369c8e3..df25354 100644 --- a/lib/aggressive-cleanup.ts +++ b/lib/aggressive-cleanup.ts @@ -55,7 +55,10 @@ class AggressiveCleanup { } async cleanupOrphanedProcesses(): Promise { - if (this.isRunning) return + if (this.isRunning) { + console.log('๐Ÿ”’ Cleanup already in progress, skipping...') + return + } this.isRunning = true const isDevelopment = process.env.NODE_ENV === 'development' @@ -70,24 +73,56 @@ class AggressiveCleanup { const activeSessions = progressTracker.getActiveSessions() if (activeSessions.length > 0) { - console.log(`โš ๏ธ Skipping cleanup - ${activeSessions.length} active analysis sessions: ${activeSessions.join(', ')}`) + console.log(`โš ๏ธ Skipping cleanup - ${activeSessions.length} active analysis sessions detected:`) + activeSessions.forEach(session => { + const progress = progressTracker.getProgress(session) + if (progress) { + const activeStep = progress.steps.find(step => step.status === 'active') + const currentStep = activeStep ? activeStep.title : 'Unknown' + console.log(` - ${session}: ${currentStep} (Step ${progress.currentStep}/${progress.totalSteps})`) + } else { + console.log(` - ${session}: Session info not available`) + } + }) + console.log('โ„น๏ธ Will retry cleanup after analysis completes') return } - console.log('โœ… No active analysis sessions, proceeding with cleanup') + console.log('โœ… No active analysis sessions detected, proceeding with cleanup') } catch (importError) { - console.error('โŒ Error importing progress tracker:', importError) - console.log('โš ๏ธ Skipping cleanup due to import error') - return + console.warn('โš ๏ธ Could not check active sessions, proceeding cautiously with cleanup') + console.warn('Import error:', importError) + + // In case of import errors, be extra cautious - only clean very old processes + if (isDevelopment) { + console.log('๐Ÿ”ง Development mode with import issues - skipping cleanup for safety') + return + } } // Find and kill orphaned chromium processes const chromiumProcesses = await this.findChromiumProcesses() if (chromiumProcesses.length > 0) { - console.log(`Found ${chromiumProcesses.length} chromium processes, cleaning up...`) + console.log(`๐Ÿ” Found ${chromiumProcesses.length} chromium processes, evaluating for cleanup...`) - for (const pid of chromiumProcesses) { + // In development, be more selective about which processes to kill + let processesToKill = chromiumProcesses + + if (isDevelopment) { + // Only kill processes that are likely orphaned (older than 5 minutes) + const oldProcesses = await this.filterOldProcesses(chromiumProcesses, 5 * 60 * 1000) // 5 minutes + processesToKill = oldProcesses + + if (processesToKill.length === 0) { + console.log('โœ… All chromium processes appear to be recent and potentially active - skipping cleanup') + return + } + + console.log(`๐Ÿ”ง Development mode: Cleaning only ${processesToKill.length} old processes (older than 5 minutes)`) + } + + for (const pid of processesToKill) { try { if (isDevelopment) { // In development, use gentler SIGTERM first @@ -140,6 +175,7 @@ class AggressiveCleanup { console.error(`Error in ${cleanupType} cleanup:`, error) } finally { this.isRunning = false + console.log(`๐Ÿ ${cleanupType} cleanup completed`) } } @@ -152,6 +188,43 @@ class AggressiveCleanup { } } + private async filterOldProcesses(pids: string[], maxAgeMs: number): Promise { + const oldProcesses: string[] = [] + + for (const pid of pids) { + try { + // Get process start time + const { stdout } = await execAsync(`ps -o pid,lstart -p ${pid} | tail -1`) + const processInfo = stdout.trim() + + if (processInfo) { + // Parse the process start time + const parts = processInfo.split(/\s+/) + if (parts.length >= 6) { + // Format: PID Mon DD HH:MM:SS YYYY + const startTimeStr = parts.slice(1).join(' ') + const startTime = new Date(startTimeStr) + const now = new Date() + const processAge = now.getTime() - startTime.getTime() + + if (processAge > maxAgeMs) { + console.log(`๐Ÿ• Process ${pid} is ${Math.round(processAge / 60000)} minutes old - marked for cleanup`) + oldProcesses.push(pid) + } else { + console.log(`๐Ÿ• Process ${pid} is ${Math.round(processAge / 60000)} minutes old - keeping alive`) + } + } + } + } catch (error) { + // If we can't get process info, assume it's old and safe to clean + console.log(`โ“ Could not get age info for process ${pid} - assuming it's old`) + oldProcesses.push(pid) + } + } + + return oldProcesses + } + async forceCleanup(): Promise { console.log('๐Ÿšจ Force cleanup initiated...') @@ -178,7 +251,27 @@ class AggressiveCleanup { console.log('๐Ÿงน Post-analysis cleanup triggered...') // Small delay to ensure analysis processes are fully closed - await new Promise(resolve => setTimeout(resolve, 2000)) + console.log('โณ Waiting 3 seconds for analysis processes to close...') + await new Promise(resolve => setTimeout(resolve, 3000)) + + // Check if there are still active sessions before cleaning + try { + const { progressTracker } = await import('./progress-tracker') + const activeSessions = progressTracker.getActiveSessions() + + if (activeSessions.length > 0) { + console.log(`โš ๏ธ Post-analysis cleanup: Still ${activeSessions.length} active sessions - deferring cleanup`) + + // Schedule a delayed cleanup in case sessions don't clear properly + setTimeout(() => { + console.log('๐Ÿ• Running delayed post-analysis cleanup...') + this.cleanupOrphanedProcesses() + }, 10000) // 10 seconds later + return + } + } catch (error) { + console.warn('โš ๏ธ Could not check active sessions for post-analysis cleanup:', error) + } await this.cleanupOrphanedProcesses() } diff --git a/lib/automation-service-simple.ts b/lib/automation-service-simple.ts index f9a386b..f6be9d1 100644 --- a/lib/automation-service-simple.ts +++ b/lib/automation-service-simple.ts @@ -3,6 +3,8 @@ import { aiAnalysisService, AnalysisResult } from './ai-analysis' import { jupiterDEXService } from './jupiter-dex-service' import { enhancedScreenshotService } from './enhanced-screenshot-simple' import { TradingViewCredentials } from './tradingview-automation' +import { progressTracker } from './progress-tracker' +import aggressiveCleanup from './aggressive-cleanup' const prisma = new PrismaClient() @@ -197,8 +199,22 @@ export class AutomationService { screenshots: string[] analysis: AnalysisResult | null } | null> { + // Generate unique session ID for this analysis + const sessionId = `automation-${Date.now()}-${Math.random().toString(36).substr(2, 9)}` + try { - console.log('๐Ÿ“ธ Starting multi-timeframe analysis with dual layouts...') + console.log(`๐Ÿ“ธ Starting multi-timeframe analysis with dual layouts... (Session: ${sessionId})`) + + // Create progress tracking session + const progressSteps = [ + { id: 'init', title: 'Initialize', description: 'Starting multi-timeframe analysis', status: 'pending' as const }, + { id: 'capture', title: 'Capture', description: 'Capturing screenshots for all timeframes', status: 'pending' as const }, + { id: 'analysis', title: 'Analysis', description: 'Running AI analysis on screenshots', status: 'pending' as const }, + { id: 'complete', title: 'Complete', description: 'Analysis complete', status: 'pending' as const } + ] + + progressTracker.createSession(sessionId, progressSteps) + progressTracker.updateStep(sessionId, 'init', 'active', 'Starting multi-timeframe analysis...') // Multi-timeframe analysis: 15m, 1h, 2h, 4h const timeframes = ['15', '1h', '2h', '4h'] @@ -206,48 +222,88 @@ export class AutomationService { console.log(`๐Ÿ” Analyzing ${symbol} across timeframes: ${timeframes.join(', ')} with AI + DIY layouts`) + progressTracker.updateStep(sessionId, 'init', 'completed', `Starting analysis for ${timeframes.length} timeframes`) + progressTracker.updateStep(sessionId, 'capture', 'active', 'Capturing screenshots...') + // Analyze each timeframe with both AI and DIY layouts - const multiTimeframeResults = await this.analyzeMultiTimeframeWithDualLayouts(symbol, timeframes) + const multiTimeframeResults = await this.analyzeMultiTimeframeWithDualLayouts(symbol, timeframes, sessionId) if (multiTimeframeResults.length === 0) { console.log('โŒ No multi-timeframe analysis results') + progressTracker.updateStep(sessionId, 'capture', 'error', 'No analysis results captured') + progressTracker.deleteSession(sessionId) + + // Run post-analysis cleanup + await aggressiveCleanup.runPostAnalysisCleanup() return null } + progressTracker.updateStep(sessionId, 'capture', 'completed', `Captured ${multiTimeframeResults.length} timeframe analyses`) + progressTracker.updateStep(sessionId, 'analysis', 'active', 'Processing multi-timeframe results...') + // Process and combine multi-timeframe results const combinedResult = this.combineMultiTimeframeAnalysis(multiTimeframeResults) if (!combinedResult.analysis) { console.log('โŒ Failed to combine multi-timeframe analysis') + progressTracker.updateStep(sessionId, 'analysis', 'error', 'Failed to combine analysis results') + progressTracker.deleteSession(sessionId) + + // Run post-analysis cleanup + await aggressiveCleanup.runPostAnalysisCleanup() return null } console.log(`โœ… Multi-timeframe analysis completed: ${combinedResult.analysis.recommendation} with ${combinedResult.analysis.confidence}% confidence`) console.log(`๐Ÿ“Š Timeframe alignment: ${this.analyzeTimeframeAlignment(multiTimeframeResults)}`) + progressTracker.updateStep(sessionId, 'analysis', 'completed', `Analysis complete: ${combinedResult.analysis.recommendation}`) + progressTracker.updateStep(sessionId, 'complete', 'completed', 'Multi-timeframe analysis finished') + + // Clean up session after successful completion + setTimeout(() => { + progressTracker.deleteSession(sessionId) + }, 2000) + + // Run post-analysis cleanup + await aggressiveCleanup.runPostAnalysisCleanup() + return combinedResult } catch (error) { console.error('Error performing multi-timeframe analysis:', error) + progressTracker.updateStep(sessionId, 'analysis', 'error', error instanceof Error ? error.message : 'Unknown error') + setTimeout(() => { + progressTracker.deleteSession(sessionId) + }, 5000) + + // Run post-analysis cleanup even on error + await aggressiveCleanup.runPostAnalysisCleanup() return null } } private async analyzeMultiTimeframeWithDualLayouts( symbol: string, - timeframes: string[] + timeframes: string[], + sessionId: string ): Promise> { const results: Array<{ symbol: string; timeframe: string; analysis: AnalysisResult | null }> = [] - for (const timeframe of timeframes) { + for (let i = 0; i < timeframes.length; i++) { + const timeframe = timeframes[i] try { - console.log(`๐Ÿ“Š Analyzing ${symbol} ${timeframe} with AI + DIY layouts...`) + console.log(`๐Ÿ“Š Analyzing ${symbol} ${timeframe} with AI + DIY layouts... (${i + 1}/${timeframes.length})`) + + // Update progress for timeframe + progressTracker.updateTimeframeProgress(sessionId, i + 1, timeframes.length, timeframe) // Use the dual-layout configuration for each timeframe const screenshotConfig = { symbol: symbol, timeframe: timeframe, - layouts: ['ai', 'diy'] + layouts: ['ai', 'diy'], + sessionId: sessionId } const result = await aiAnalysisService.captureAndAnalyzeWithConfig(screenshotConfig) diff --git a/prisma/prisma/dev.db b/prisma/prisma/dev.db index be905c41ac5ba9bfb8837838ca697395c11af070..155b6a8139990c1ac3efffe0022ec51ef7b54d2f 100644 GIT binary patch delta 152 zcmZoTz}0YoYl1Xm<3t%}#>U2ktqF|F;@R#q@a^NhzgbWqj(2)iE@P>AV+^yjy{e?5 zq#y?rFfuk)GB7Z3FtD;RBy8+-3jI(vO9hIzC6ZH0wRXl hh3N(c1_jxL234h{g{dWp28QiN>KV5msb{iL002OWEU*9o