UNPKG

il2cpp-dump-analyzer-mcp

Version:

Agentic RAG system for analyzing IL2CPP dump.cs files from Unity games

486 lines 17.1 kB
"use strict"; /** * Comprehensive health monitoring service for IL2CPP dump analyzer MCP system * Provides health checks, metrics collection, and lifecycle management */ Object.defineProperty(exports, "__esModule", { value: true }); exports.HealthService = void 0; const events_1 = require("events"); /** * Health monitoring service with comprehensive component checking */ class HealthService extends events_1.EventEmitter { constructor(config) { super(); this.isRunning = false; this.shutdownHandlers = []; // Metrics tracking this.mcpMetrics = { activeConnections: 0, totalRequests: 0, responseTimeSum: 0, errorCount: 0 }; this.config = { interval: 30000, // 30 seconds timeout: 10000, // 10 seconds retries: 3, gracefulShutdownTimeout: 30000, // 30 seconds components: { database: true, vectorStore: true, embeddings: true, mcp: true }, ...config }; this.startTime = new Date(); this.setupSignalHandlers(); } /** * Initialize health service with component dependencies */ async initialize(components) { this.connectionManager = components.connectionManager; this.vectorStore = components.vectorStore; this.embeddings = components.embeddings; console.log('Health service initialized with components:', { database: !!this.connectionManager, vectorStore: !!this.vectorStore, embeddings: !!this.embeddings }); } /** * Start health monitoring */ start() { if (this.isRunning) { console.warn('Health service is already running'); return; } this.isRunning = true; console.log(`Starting health monitoring with ${this.config.interval}ms interval`); // Perform initial health check this.performHealthCheck(); // Schedule periodic health checks this.healthCheckInterval = setInterval(() => { this.performHealthCheck(); }, this.config.interval); this.emit('started'); } /** * Stop health monitoring */ stop() { if (!this.isRunning) { return; } this.isRunning = false; if (this.healthCheckInterval) { clearInterval(this.healthCheckInterval); this.healthCheckInterval = undefined; } console.log('Health monitoring stopped'); this.emit('stopped'); } /** * Get current health status */ async getHealthStatus() { if (this.lastHealthStatus && Date.now() - this.lastHealthStatus.timestamp.getTime() < 5000) { // Return cached status if less than 5 seconds old return this.lastHealthStatus; } return this.performHealthCheck(); } /** * Perform comprehensive health check */ async performHealthCheck() { const startTime = Date.now(); const components = []; try { // Check database health if (this.config.components.database && this.connectionManager) { components.push(await this.checkDatabaseHealth()); } // Check vector store health if (this.config.components.vectorStore && this.vectorStore) { components.push(await this.checkVectorStoreHealth()); } // Check embeddings health if (this.config.components.embeddings && this.embeddings) { components.push(await this.checkEmbeddingsHealth()); } // Check MCP server health if (this.config.components.mcp) { components.push(await this.checkMCPHealth()); } // Determine overall status const overallStatus = this.determineOverallStatus(components); // Collect metrics const metrics = await this.collectMetrics(); const healthStatus = { status: overallStatus, timestamp: new Date(), uptime: Date.now() - this.startTime.getTime(), version: process.env.npm_package_version || '1.0.0', components, metrics }; this.lastHealthStatus = healthStatus; // Emit health status event this.emit('healthCheck', healthStatus); // Log health status changes if (this.lastHealthStatus?.status !== overallStatus) { console.log(`Health status changed to: ${overallStatus}`); } return healthStatus; } catch (error) { console.error('Health check failed:', error); const errorStatus = { status: 'unhealthy', timestamp: new Date(), uptime: Date.now() - this.startTime.getTime(), version: process.env.npm_package_version || '1.0.0', components: [{ name: 'health-service', status: 'unhealthy', message: `Health check failed: ${error instanceof Error ? error.message : 'Unknown error'}`, lastCheck: new Date() }], metrics: await this.collectMetrics() }; this.emit('healthCheck', errorStatus); return errorStatus; } } /** * Check database connection health */ async checkDatabaseHealth() { const startTime = Date.now(); try { if (!this.connectionManager) { return { name: 'database', status: 'unhealthy', message: 'Database connection manager not initialized', lastCheck: new Date() }; } const healthStatus = this.connectionManager.getHealthStatus(); const responseTime = Date.now() - startTime; return { name: 'database', status: healthStatus.isHealthy ? 'healthy' : 'unhealthy', message: healthStatus.isHealthy ? 'Database connection healthy' : 'Database connection issues', lastCheck: new Date(), responseTime, details: { connectionStats: healthStatus.stats, lastHealthCheck: healthStatus.lastHealthCheck } }; } catch (error) { return { name: 'database', status: 'unhealthy', message: `Database health check failed: ${error instanceof Error ? error.message : 'Unknown error'}`, lastCheck: new Date(), responseTime: Date.now() - startTime }; } } /** * Check vector store health */ async checkVectorStoreHealth() { const startTime = Date.now(); try { if (!this.vectorStore) { return { name: 'vector-store', status: 'unhealthy', message: 'Vector store not initialized', lastCheck: new Date() }; } const healthStatus = await this.vectorStore.getHealthStatus(); const responseTime = Date.now() - startTime; return { name: 'vector-store', status: healthStatus.isHealthy ? 'healthy' : 'degraded', message: healthStatus.isHealthy ? 'Vector store healthy' : 'Vector store issues detected', lastCheck: new Date(), responseTime, details: { performanceStats: healthStatus.performanceStats, cacheStats: healthStatus.cacheStats, circuitBreakerStats: healthStatus.circuitBreakerStats } }; } catch (error) { return { name: 'vector-store', status: 'unhealthy', message: `Vector store health check failed: ${error instanceof Error ? error.message : 'Unknown error'}`, lastCheck: new Date(), responseTime: Date.now() - startTime }; } } /** * Check embeddings model health */ async checkEmbeddingsHealth() { const startTime = Date.now(); try { if (!this.embeddings) { return { name: 'embeddings', status: 'unhealthy', message: 'Embeddings service not initialized', lastCheck: new Date() }; } // Test embeddings with a simple query await this.embeddings.embedQuery('health check test'); const responseTime = Date.now() - startTime; return { name: 'embeddings', status: 'healthy', message: 'Embeddings model healthy', lastCheck: new Date(), responseTime, details: { modelName: 'Xenova/all-MiniLM-L6-v2', dimensions: 384 } }; } catch (error) { return { name: 'embeddings', status: 'unhealthy', message: `Embeddings health check failed: ${error instanceof Error ? error.message : 'Unknown error'}`, lastCheck: new Date(), responseTime: Date.now() - startTime }; } } /** * Check MCP server health */ async checkMCPHealth() { const startTime = Date.now(); try { // Basic MCP health check - verify process is running const responseTime = Date.now() - startTime; return { name: 'mcp-server', status: 'healthy', message: 'MCP server healthy', lastCheck: new Date(), responseTime, details: { transport: 'stdio', activeConnections: this.mcpMetrics.activeConnections, totalRequests: this.mcpMetrics.totalRequests } }; } catch (error) { return { name: 'mcp-server', status: 'unhealthy', message: `MCP server health check failed: ${error instanceof Error ? error.message : 'Unknown error'}`, lastCheck: new Date(), responseTime: Date.now() - startTime }; } } /** * Determine overall health status from component statuses */ determineOverallStatus(components) { if (components.length === 0) { return 'unhealthy'; } const unhealthyCount = components.filter(c => c.status === 'unhealthy').length; const degradedCount = components.filter(c => c.status === 'degraded').length; if (unhealthyCount > 0) { return 'unhealthy'; } else if (degradedCount > 0) { return 'degraded'; } else { return 'healthy'; } } /** * Collect system and application metrics */ async collectMetrics() { const memUsage = process.memoryUsage(); const cpuUsage = process.cpuUsage(); return { memory: { used: memUsage.rss, total: memUsage.rss + memUsage.heapTotal, percentage: (memUsage.rss / (memUsage.rss + memUsage.heapTotal)) * 100, heapUsed: memUsage.heapUsed, heapTotal: memUsage.heapTotal }, cpu: { usage: (cpuUsage.user + cpuUsage.system) / 1000000, // Convert to seconds loadAverage: process.platform !== 'win32' ? require('os').loadavg() : [0, 0, 0] }, database: await this.getDatabaseMetrics(), embeddings: await this.getEmbeddingsMetrics(), mcp: this.getMCPMetrics() }; } /** * Get database-specific metrics */ async getDatabaseMetrics() { if (!this.connectionManager) { return { connectionCount: 0, activeQueries: 0, avgResponseTime: 0, errorRate: 0 }; } const stats = this.connectionManager.getStats(); return { connectionCount: stats.totalConnections, activeQueries: stats.activeConnections, avgResponseTime: 0, // TODO: Implement from performance monitor errorRate: 0 // TODO: Implement from performance monitor }; } /** * Get embeddings-specific metrics */ async getEmbeddingsMetrics() { return { modelLoaded: !!this.embeddings, cacheSize: 0, // TODO: Implement cache size tracking avgProcessingTime: 0 // TODO: Implement from performance monitor }; } /** * Get MCP server metrics */ getMCPMetrics() { const avgResponseTime = this.mcpMetrics.totalRequests > 0 ? this.mcpMetrics.responseTimeSum / this.mcpMetrics.totalRequests : 0; const errorRate = this.mcpMetrics.totalRequests > 0 ? this.mcpMetrics.errorCount / this.mcpMetrics.totalRequests : 0; return { activeConnections: this.mcpMetrics.activeConnections, totalRequests: this.mcpMetrics.totalRequests, avgResponseTime, errorRate }; } /** * Update MCP metrics (called by MCP server) */ updateMCPMetrics(metrics) { if (metrics.activeConnections !== undefined) { this.mcpMetrics.activeConnections = metrics.activeConnections; } if (metrics.requestProcessed) { this.mcpMetrics.totalRequests++; if (metrics.responseTime !== undefined) { this.mcpMetrics.responseTimeSum += metrics.responseTime; } if (metrics.error) { this.mcpMetrics.errorCount++; } } } /** * Setup signal handlers for graceful shutdown */ setupSignalHandlers() { const signals = ['SIGTERM', 'SIGINT', 'SIGUSR2']; signals.forEach(signal => { process.on(signal, async () => { console.log(`Received ${signal}, initiating graceful shutdown...`); await this.gracefulShutdown(); process.exit(0); }); }); // Handle uncaught exceptions process.on('uncaughtException', async (error) => { console.error('Uncaught exception:', error); await this.gracefulShutdown(); process.exit(1); }); // Handle unhandled promise rejections process.on('unhandledRejection', async (reason, promise) => { console.error('Unhandled rejection at:', promise, 'reason:', reason); await this.gracefulShutdown(); process.exit(1); }); } /** * Perform graceful shutdown */ async gracefulShutdown() { console.log('Starting graceful shutdown...'); this.emit('shutdown'); // Stop health monitoring this.stop(); // Execute shutdown handlers const shutdownPromises = this.shutdownHandlers.map(async (handler, index) => { try { console.log(`Executing shutdown handler ${index + 1}/${this.shutdownHandlers.length}`); await Promise.race([ handler(), new Promise((_, reject) => setTimeout(() => reject(new Error('Shutdown handler timeout')), 5000)) ]); } catch (error) { console.error(`Shutdown handler ${index + 1} failed:`, error); } }); try { await Promise.all(shutdownPromises); console.log('Graceful shutdown completed'); } catch (error) { console.error('Some shutdown handlers failed:', error); } } /** * Register a shutdown handler */ onShutdown(handler) { this.shutdownHandlers.push(handler); } /** * Get health service statistics */ getStats() { return { isRunning: this.isRunning, uptime: Date.now() - this.startTime.getTime(), lastHealthCheck: this.lastHealthStatus?.timestamp, totalHealthChecks: this.listenerCount('healthCheck') }; } } exports.HealthService = HealthService; //# sourceMappingURL=health-service.js.map