/**
* MySQL Resource - Performance
*
* Query performance metrics and slow query information.
*/
import type { MySQLAdapter } from "../MySQLAdapter.js";
import type {
ResourceDefinition,
RequestContext,
} from "../../../types/index.js";
export function createPerformanceResource(
adapter: MySQLAdapter,
): ResourceDefinition {
return {
uri: "mysql://performance",
name: "Performance Metrics",
title: "MySQL Performance Metrics",
description: "Query performance statistics and slow query analysis",
mimeType: "application/json",
annotations: {
audience: ["user", "assistant"],
priority: 0.8,
},
handler: async (_uri: string, _context: RequestContext) => {
// Get performance-related status variables
const statusResult = await adapter.executeQuery(`
SHOW GLOBAL STATUS WHERE Variable_name IN (
'Queries', 'Questions', 'Slow_queries',
'Select_full_join', 'Select_range_check', 'Select_scan',
'Sort_merge_passes', 'Sort_range', 'Sort_rows', 'Sort_scan',
'Created_tmp_disk_tables', 'Created_tmp_tables',
'Handler_read_first', 'Handler_read_key', 'Handler_read_rnd',
'Handler_read_rnd_next', 'Handler_write'
)
`);
const status: Record<string, number> = {};
for (const row of statusResult.rows ?? []) {
status[row["Variable_name"] as string] = parseInt(
row["Value"] as string,
10,
);
}
// Get performance schema if available (MySQL 5.6+)
let topQueries: unknown[] = [];
try {
const perfResult = await adapter.executeQuery(`
SELECT
DIGEST_TEXT as query_pattern,
COUNT_STAR as execution_count,
ROUND(SUM_TIMER_WAIT / 1000000000, 2) as total_time_ms,
ROUND(AVG_TIMER_WAIT / 1000000000, 2) as avg_time_ms,
ROUND(MAX_TIMER_WAIT / 1000000000, 2) as max_time_ms,
SUM_ROWS_EXAMINED as rows_examined,
SUM_ROWS_SENT as rows_sent
FROM performance_schema.events_statements_summary_by_digest
WHERE DIGEST_TEXT IS NOT NULL
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 10
`);
topQueries = perfResult.rows ?? [];
} catch {
// Performance schema may not be available
}
// Calculate derived metrics
const tmpTablesCreated = status["Created_tmp_tables"] ?? 0;
const tmpDiskRatio =
tmpTablesCreated > 0
? Math.round(
((status["Created_tmp_disk_tables"] ?? 0) / tmpTablesCreated) *
100,
)
: 0;
const fullTableScans = status["Handler_read_rnd_next"] ?? 0;
const indexReads = status["Handler_read_key"] ?? 0;
const scanVsIndexRatio =
fullTableScans + indexReads > 0
? Math.round((fullTableScans / (fullTableScans + indexReads)) * 100)
: 0;
return {
summary: {
total_queries: status["Queries"] ?? 0,
slow_queries: status["Slow_queries"] ?? 0,
tmp_tables_to_disk_percent: tmpDiskRatio,
full_scan_percent: scanVsIndexRatio,
},
sorts: {
merge_passes: status["Sort_merge_passes"] ?? 0,
range: status["Sort_range"] ?? 0,
rows: status["Sort_rows"] ?? 0,
scan: status["Sort_scan"] ?? 0,
},
joins: {
full_join: status["Select_full_join"] ?? 0,
range_check: status["Select_range_check"] ?? 0,
scan: status["Select_scan"] ?? 0,
},
handler: {
read_key: status["Handler_read_key"] ?? 0,
read_rnd: status["Handler_read_rnd"] ?? 0,
read_rnd_next: status["Handler_read_rnd_next"] ?? 0,
write: status["Handler_write"] ?? 0,
},
top_queries: topQueries,
};
},
};
}