/**
* MySQL Resource - Replication
*
* Replication status, lag monitoring, and GTID information.
*/
import type { MySQLAdapter } from "../MySQLAdapter.js";
import type {
ResourceDefinition,
RequestContext,
} from "../../../types/index.js";
export function createReplicationResource(
adapter: MySQLAdapter,
): ResourceDefinition {
return {
uri: "mysql://replication",
name: "Replication Status",
title: "MySQL Replication Status",
description: "Replication status, lag monitoring, and GTID information",
mimeType: "application/json",
annotations: {
audience: ["user", "assistant"],
priority: 0.6,
},
handler: async (_uri: string, _context: RequestContext) => {
// Check if this is a replica
let replicaStatus: Record<string, unknown> | null = null;
try {
// Try MySQL 8.0.22+ syntax first
const replicaResult = await adapter.executeQuery("SHOW REPLICA STATUS");
if (replicaResult.rows && replicaResult.rows.length > 0) {
replicaStatus = replicaResult.rows[0] ?? null;
}
} catch {
try {
// Fall back to older syntax
const slaveResult = await adapter.executeQuery("SHOW SLAVE STATUS");
if (slaveResult.rows && slaveResult.rows.length > 0) {
replicaStatus = slaveResult.rows[0] ?? null;
}
} catch {
// Not a replica or no permissions
}
}
// Check if this is a source/master
let sourceStatus: Record<string, unknown> | null = null;
try {
const binlogResult = await adapter.executeQuery(
"SHOW BINARY LOG STATUS",
);
if (binlogResult.rows && binlogResult.rows.length > 0) {
sourceStatus = binlogResult.rows[0] ?? null;
}
} catch {
try {
// Fall back to older syntax
const masterResult = await adapter.executeQuery("SHOW MASTER STATUS");
if (masterResult.rows && masterResult.rows.length > 0) {
sourceStatus = masterResult.rows[0] ?? null;
}
} catch {
// Binary logging may be disabled
}
}
// Get GTID info if available
const gtidInfo: Record<string, string> = {};
try {
const gtidResult = await adapter.executeQuery(`
SHOW GLOBAL VARIABLES WHERE Variable_name IN (
'gtid_mode', 'gtid_executed', 'gtid_purged', 'enforce_gtid_consistency'
)
`);
for (const row of gtidResult.rows ?? []) {
gtidInfo[row["Variable_name"] as string] = row["Value"] as string;
}
} catch {
// GTID may not be available
}
// Get connected replicas if this is a source
let replicas: unknown[] = [];
try {
const replicasResult = await adapter.executeQuery("SHOW REPLICAS");
replicas = replicasResult.rows ?? [];
} catch {
try {
const replicasResult = await adapter.executeQuery("SHOW SLAVE HOSTS");
replicas = replicasResult.rows ?? [];
} catch {
// May not have permission or no replicas connected
}
}
// Determine role
let role = "standalone";
if (replicaStatus) {
role = sourceStatus ? "replica-source" : "replica";
} else if (sourceStatus) {
role = "source";
}
return {
role,
source: sourceStatus
? {
file: sourceStatus["File"],
position: sourceStatus["Position"],
binlog_do_db: sourceStatus["Binlog_Do_DB"],
binlog_ignore_db: sourceStatus["Binlog_Ignore_DB"],
}
: null,
replica: replicaStatus
? {
source_host:
replicaStatus["Source_Host"] ?? replicaStatus["Master_Host"],
source_port:
replicaStatus["Source_Port"] ?? replicaStatus["Master_Port"],
io_running:
replicaStatus["Replica_IO_Running"] ??
replicaStatus["Slave_IO_Running"],
sql_running:
replicaStatus["Replica_SQL_Running"] ??
replicaStatus["Slave_SQL_Running"],
seconds_behind:
replicaStatus["Seconds_Behind_Source"] ??
replicaStatus["Seconds_Behind_Master"],
last_error: replicaStatus["Last_Error"],
relay_log_file: replicaStatus["Relay_Log_File"],
relay_log_pos: replicaStatus["Relay_Log_Pos"],
}
: null,
gtid: gtidInfo,
connected_replicas: replicas,
};
},
};
}