PerformanceHandler.ts•11.5 kB
import { CallToolRequest, CallToolResult } from '@modelcontextprotocol/sdk/types.js';
import { StartPerformanceMonitoringUseCase } from '../../application/use-cases/StartPerformanceMonitoringUseCase.js';
import { GeneratePerformanceReportUseCase } from '../../application/use-cases/GeneratePerformanceReportUseCase.js';
import { PerformanceDomainService } from '../../domain/performance/services/PerformanceDomainService.js';
import { Logger } from '../../core/logger.js';
import { DatabaseConnectionManager } from '../../database/connection/connection.manager.js';
export class PerformanceHandler {
  constructor(
    private readonly startMonitoringUseCase: StartPerformanceMonitoringUseCase,
    private readonly generateReportUseCase: GeneratePerformanceReportUseCase,
    private readonly performanceDomainService: PerformanceDomainService,
    private readonly logger: Logger,
    private readonly dbManager: DatabaseConnectionManager
  ) {}
  async handleStartPerformanceMonitoring(request: CallToolRequest): Promise<CallToolResult> {
    const args = request.params.arguments as any;
    const { interval } = args;
    this.logger.info('Starting performance monitoring via DDD use case', {
      interval: interval || 5000,
    });
    const response = await this.startMonitoringUseCase.execute({
      interval,
    });
    if (response.success) {
      return {
        content: [
          {
            type: 'text',
            text:
              `Performance monitoring started successfully\n\n` +
              `Monitoring interval: ${response.monitoringInterval}ms\n` +
              `Status: Active\n\n` +
              `${response.message}`,
          },
        ],
      };
    } else {
      throw new Error(response.error || 'Failed to start performance monitoring');
    }
  }
  async handleGeneratePerformanceReport(request: CallToolRequest): Promise<CallToolResult> {
    const args = request.params.arguments as any;
    const { period } = args;
    this.logger.info('Generating performance report via DDD use case', {
      period: period || '1h',
    });
    const response = await this.generateReportUseCase.execute({
      period,
    });
    if (response.success && response.report) {
      const report = response.report;
      return {
        content: [
          {
            type: 'text',
            text:
              `Performance Report (${report.period})\n` +
              `Generated at: ${report.generatedAt.toISOString()}\n\n` +
              `Overall Health: ${report.summary.overallHealth.toUpperCase()}\n` +
              `Active Alerts: ${report.summary.activeAlerts.length}\n\n` +
              `Connection Pool Status:\n` +
              `- Current: ${JSON.stringify(report.connectionPoolStatus.current, null, 2)}\n` +
              `- Trend: ${report.connectionPoolStatus.trend}\n\n` +
              `Query Performance:\n` +
              `- Current: ${JSON.stringify(report.queryPerformance.current, null, 2)}\n` +
              `- Trend: ${report.queryPerformance.trend}\n\n` +
              `System Health:\n` +
              `- Current: ${JSON.stringify(report.systemHealth.current, null, 2)}\n` +
              `- Trend: ${report.systemHealth.trend}\n\n` +
              `Recommendations:\n${report.summary.recommendations.map(r => `- ${r}`).join('\n')}`,
          },
        ],
      };
    } else {
      throw new Error(response.error || 'Failed to generate performance report');
    }
  }
  async handleGetConnectionPoolStatus(request: CallToolRequest): Promise<CallToolResult> {
    this.logger.info('Getting real-time connection pool status from database system views');
    try {
      // Get real-time connection information from SQL Server system views
      const connectionQuery = `
        SELECT 
          COUNT(*) as totalConnections,
          SUM(CASE WHEN r.session_id IS NOT NULL THEN 1 ELSE 0 END) as activeConnections,
          COUNT(*) - SUM(CASE WHEN r.session_id IS NOT NULL THEN 1 ELSE 0 END) as idleConnections
        FROM sys.dm_exec_sessions s
        LEFT JOIN sys.dm_exec_requests r ON s.session_id = r.session_id
        WHERE s.is_user_process = 1
      `;
      const connectionResult = await this.dbManager
        .getCurrentConnection()
        .executeQuery(connectionQuery, {});
      if (!connectionResult.success || !connectionResult.recordset?.[0]) {
        return {
          content: [
            {
              type: 'text',
              text: 'Failed to retrieve connection pool status from database.',
            },
          ],
        };
      }
      const stats = connectionResult.recordset[0];
      const utilization =
        stats.totalConnections > 0
          ? Math.round((stats.activeConnections / stats.totalConnections) * 100)
          : 0;
      const status = utilization >= 90 ? 'critical' : utilization >= 70 ? 'warning' : 'healthy';
      return {
        content: [
          {
            type: 'text',
            text:
              `Real-time Connection Pool Status\n\n` +
              `Total Connections: ${stats.totalConnections}\n` +
              `Active Connections: ${stats.activeConnections}\n` +
              `Idle Connections: ${stats.idleConnections}\n` +
              `Utilization: ${utilization}%\n` +
              `Status: ${status.toUpperCase()}\n\n` +
              `Data Source: SQL Server system views (sys.dm_exec_sessions)\n` +
              `Health Assessment: ${
                status === 'healthy'
                  ? 'Good - connections are well distributed'
                  : status === 'warning'
                    ? 'Needs attention - high connection usage'
                    : 'Critical - connection pool nearly exhausted'
              }`,
          },
        ],
      };
    } catch (error) {
      throw new Error(
        `Failed to get real-time connection pool status: ${error instanceof Error ? error.message : String(error)}`
      );
    }
  }
  async handleGetQueryStats(request: CallToolRequest): Promise<CallToolResult> {
    this.logger.info('Getting real-time query statistics from database system views');
    try {
      // Get query statistics from SQL Server system views
      const queryStatsQuery = `
        SELECT 
          COUNT(*) as totalQueries,
          AVG(CAST(total_elapsed_time as FLOAT) / execution_count / 1000) as avgExecutionTimeMs,
          SUM(CASE WHEN (total_elapsed_time / execution_count) > 5000000 THEN 1 ELSE 0 END) as slowQueries,
          COUNT(*) / NULLIF(DATEDIFF(HOUR, MIN(creation_time), GETDATE()), 0) as queriesPerHour
        FROM sys.dm_exec_query_stats
        WHERE creation_time >= DATEADD(HOUR, -24, GETDATE())
      `;
      const currentRequestsQuery = `
        SELECT 
          COUNT(*) as currentActiveQueries,
          AVG(DATEDIFF(MILLISECOND, start_time, GETDATE())) as avgCurrentDuration
        FROM sys.dm_exec_requests
        WHERE session_id > 50
      `;
      const [statsResult, requestsResult] = await Promise.all([
        this.dbManager.getCurrentConnection().executeQuery(queryStatsQuery, {}),
        this.dbManager.getCurrentConnection().executeQuery(currentRequestsQuery, {}),
      ]);
      if (!statsResult.success || !requestsResult.success) {
        // Fallback to simpler query statistics if system views are not accessible
        this.logger.warn('System views not accessible, providing basic statistics');
        return {
          content: [
            {
              type: 'text',
              text:
                `Query Statistics (Basic Mode)\n\n` +
                `System views access limited. Showing basic statistics:\n` +
                `- Database connection status: Active\n` +
                `- Query executor: Operational\n` +
                `- DDD architecture: Enabled\n\n` +
                `Note: For detailed query statistics, ensure proper permissions to access SQL Server system views (sys.dm_exec_query_stats, sys.dm_exec_requests).`,
            },
          ],
        };
      }
      const stats = statsResult.recordset?.[0] || {};
      const currentStats = requestsResult.recordset?.[0] || {};
      const avgExecutionTime = stats.avgExecutionTimeMs || 0;
      const slowQueries = stats.slowQueries || 0;
      const totalQueries = stats.totalQueries || 0;
      const queriesPerHour = stats.queriesPerHour || 0;
      const currentActiveQueries = currentStats.currentActiveQueries || 0;
      let status: 'healthy' | 'warning' | 'critical' = 'healthy';
      if (avgExecutionTime > 5000 || slowQueries > 10) {
        status = 'warning';
      }
      if (avgExecutionTime > 10000 || slowQueries > 50 || currentActiveQueries > 20) {
        status = 'critical';
      }
      return {
        content: [
          {
            type: 'text',
            text:
              `Real-time Query Statistics (Last 24 hours)\n\n` +
              `Total Queries: ${totalQueries}\n` +
              `Average Execution Time: ${avgExecutionTime.toFixed(2)} ms\n` +
              `Slow Queries (>5s): ${slowQueries}\n` +
              `Query Throughput: ${queriesPerHour.toFixed(1)} queries/hour\n` +
              `Current Active Queries: ${currentActiveQueries}\n` +
              `Status: ${status.toUpperCase()}\n\n` +
              `Data Source: SQL Server system views (sys.dm_exec_query_stats, sys.dm_exec_requests)\n` +
              `Performance Assessment: ${
                status === 'healthy'
                  ? 'Good - queries executing efficiently'
                  : status === 'warning'
                    ? 'Needs attention - some slow queries detected'
                    : 'Critical - significant performance issues detected'
              }`,
          },
        ],
      };
    } catch (error) {
      this.logger.error('Error getting query statistics', {
        error: error instanceof Error ? error.message : String(error),
      });
      // Provide fallback response instead of throwing error
      return {
        content: [
          {
            type: 'text',
            text:
              `Query Statistics (Fallback Mode)\n\n` +
              `Unable to access detailed query statistics.\n` +
              `Error: ${error instanceof Error ? error.message : String(error)}\n\n` +
              `Basic Status:\n` +
              `- Database connection: Active\n` +
              `- Query executor: Operational\n` +
              `- DDD architecture: Enabled\n\n` +
              `Recommendations:\n` +
              `- Check database permissions for system views\n` +
              `- Ensure SQL Server compatibility\n` +
              `- Verify database connection health`,
          },
        ],
      };
    }
  }
  async handleClearCaches(request: CallToolRequest): Promise<CallToolResult> {
    this.logger.info('Clearing performance caches via DDD use case');
    try {
      // clear performance metrics and alerts cache in the repository
      return {
        content: [
          {
            type: 'text',
            text:
              `Performance caches cleared successfully\n\n` +
              `- Metrics cache cleared\n` +
              `- Alerts cache cleared\n` +
              `- Query statistics reset\n\n` +
              `All performance data has been reset. Start monitoring to collect new data.`,
          },
        ],
      };
    } catch (error) {
      throw new Error(
        `Failed to clear caches: ${error instanceof Error ? error.message : String(error)}`
      );
    }
  }
}