/**
* MySQL Resource - Health
*
* Comprehensive database health status including connection pool,
* thread activity, and InnoDB status.
*/
import type { MySQLAdapter } from '../MySQLAdapter.js';
import type { ResourceDefinition, RequestContext } from '../../../types/index.js';
export function createHealthResource(adapter: MySQLAdapter): ResourceDefinition {
return {
uri: 'mysql://health',
name: 'Database Health',
title: 'MySQL Database Health',
description: 'Comprehensive database health status',
mimeType: 'application/json',
annotations: {
audience: ['user', 'assistant'],
priority: 1.0
},
handler: async (_uri: string, _context: RequestContext) => {
// Execute health queries in parallel for better performance
const [statusResult, maxConnResult] = await Promise.all([
// Get uptime and connection info
adapter.executeQuery(`
SHOW GLOBAL STATUS WHERE Variable_name IN (
'Uptime', 'Threads_connected', 'Threads_running',
'Max_used_connections', 'Connections', 'Aborted_connects',
'Slow_queries', 'Questions', 'Com_select', 'Com_insert',
'Com_update', 'Com_delete', 'Innodb_buffer_pool_read_requests',
'Innodb_buffer_pool_reads', 'Table_locks_waited', 'Table_locks_immediate'
)
`),
// Get max_connections for percentage calculation
adapter.executeQuery(
"SHOW GLOBAL VARIABLES LIKE 'max_connections'"
)
]);
const status: Record<string, string> = {};
for (const row of statusResult.rows ?? []) {
status[row['Variable_name'] as string] = row['Value'] as string;
}
const maxConnections = parseInt(
(maxConnResult.rows?.[0]?.['Value'] as string) ?? '151',
10
);
// Calculate health metrics
const threadsConnected = parseInt(status['Threads_connected'] ?? '0', 10);
const connectionUsagePercent = Math.round((threadsConnected / maxConnections) * 100);
const bufferPoolRequests = parseInt(status['Innodb_buffer_pool_read_requests'] ?? '0', 10);
const bufferPoolReads = parseInt(status['Innodb_buffer_pool_reads'] ?? '0', 10);
const bufferPoolHitRatio = bufferPoolRequests > 0
? Math.round(((bufferPoolRequests - bufferPoolReads) / bufferPoolRequests) * 100 * 100) / 100
: 100;
const tableLockWaited = parseInt(status['Table_locks_waited'] ?? '0', 10);
const tableLockImmediate = parseInt(status['Table_locks_immediate'] ?? '0', 10);
const tableLockContention = (tableLockWaited + tableLockImmediate) > 0
? Math.round((tableLockWaited / (tableLockWaited + tableLockImmediate)) * 100 * 100) / 100
: 0;
// Get connection pool stats if available
const pool = adapter.getPool();
const poolStats = pool?.getStats();
return {
status: 'healthy',
uptime_seconds: parseInt(status['Uptime'] ?? '0', 10),
connections: {
current: threadsConnected,
running: parseInt(status['Threads_running'] ?? '0', 10),
max_used: parseInt(status['Max_used_connections'] ?? '0', 10),
max_allowed: maxConnections,
usage_percent: connectionUsagePercent
},
performance: {
questions: parseInt(status['Questions'] ?? '0', 10),
slow_queries: parseInt(status['Slow_queries'] ?? '0', 10),
buffer_pool_hit_ratio: bufferPoolHitRatio,
table_lock_contention_percent: tableLockContention
},
queries: {
select: parseInt(status['Com_select'] ?? '0', 10),
insert: parseInt(status['Com_insert'] ?? '0', 10),
update: parseInt(status['Com_update'] ?? '0', 10),
delete: parseInt(status['Com_delete'] ?? '0', 10)
},
pool: poolStats ?? null
};
}
};
}