collect_replica_status() in src/extension.cc never emits any
mysql_replica_* metric, so replication is missing from /metrics entirely.
The collector walks the result row pairing adjacent cells as name -> value:
for (size_t i = 0; i < row.size(); ++i) {
std::string col_name(row[i]); // treated as a column NAME
if (i + 1 < row.size() && row[i + 1].data()) {
std::string value(row[i + 1]); // next cell treated as its value
if (col_name == "Replica_IO_Running") io_running = value; // never matches
else if (col_name == "Seconds_Behind_Source") seconds_behind = value;
// ...
}
}
That assumes a flattened name, value, name, value, ... sequence. But
SHOW REPLICA STATUS returns a single wide row of values (no names), so
col_name is always something like "Yes" or "3306" and none of the name
comparisons fire. collect_global_status() reads SHOW GLOBAL STATUS correctly
(row[0] name, row[1] value) because that result really is two columns per
row — the replica path looks like it reused that idea for a result that isn't
shaped the same way.
It gets past CI because the replica_status test runs against a server with no
replica configured, so SHOW REPLICA STATUS returns zero rows and the loop body
never executes. Pointing that test at a real channel (or stubbing a row) would
catch it.
The fix is to read the row by position, or pull the field names from the result
metadata and build a name->value map before extracting the metrics.
A few smaller things I noticed in the same file, all lower priority:
collect_global_status writes two # TYPE lines for the base metric name
(gauge then untyped) and no per-metric TYPE; strict parsers reject that.
The computed type_str also looks unused.
g_requests_total / g_errors_total / g_scrape_duration_us are plain
long long written from the scrape path and read from client threads via
SHOW STATUS (the file already uses std::atomic for shutdown_requested) —
they probably want to be atomic too.
g_ctx isn't protected by a lock across start/stop. If the enable/disable
paths (on_sys_var_change, install) can ever overlap, two start_listener()
calls could each see g_ctx == nullptr and leak a socket and thread. Not sure
whether the server serializes those callbacks, so flagging rather than claiming.
/metrics is unauthenticated. Fine on the 127.0.0.1 default, but it exposes
SHOW GLOBAL STATUS/VARIABLES to the network if bind_address is 0.0.0.0.
Line references are against 74faf54. I can send a PR for the replica fix if
that's useful.
AI=CLAUDE
collect_replica_status()insrc/extension.ccnever emits anymysql_replica_*metric, so replication is missing from/metricsentirely.The collector walks the result row pairing adjacent cells as name -> value:
That assumes a flattened
name, value, name, value, ...sequence. ButSHOW REPLICA STATUSreturns a single wide row of values (no names), socol_nameis always something like"Yes"or"3306"and none of the namecomparisons fire.
collect_global_status()readsSHOW GLOBAL STATUScorrectly(
row[0]name,row[1]value) because that result really is two columns perrow — the replica path looks like it reused that idea for a result that isn't
shaped the same way.
It gets past CI because the
replica_statustest runs against a server with noreplica configured, so
SHOW REPLICA STATUSreturns zero rows and the loop bodynever executes. Pointing that test at a real channel (or stubbing a row) would
catch it.
The fix is to read the row by position, or pull the field names from the result
metadata and build a name->value map before extracting the metrics.
A few smaller things I noticed in the same file, all lower priority:
collect_global_statuswrites two# TYPElines for the base metric name(
gaugethenuntyped) and no per-metric TYPE; strict parsers reject that.The computed
type_stralso looks unused.g_requests_total/g_errors_total/g_scrape_duration_usare plainlong longwritten from the scrape path and read from client threads viaSHOW STATUS(the file already usesstd::atomicforshutdown_requested) —they probably want to be atomic too.
g_ctxisn't protected by a lock across start/stop. If the enable/disablepaths (
on_sys_var_change, install) can ever overlap, twostart_listener()calls could each see
g_ctx == nullptrand leak a socket and thread. Not surewhether the server serializes those callbacks, so flagging rather than claiming.
/metricsis unauthenticated. Fine on the127.0.0.1default, but it exposesSHOW GLOBAL STATUS/VARIABLESto the network ifbind_addressis0.0.0.0.Line references are against
74faf54. I can send a PR for the replica fix ifthat's useful.
AI=CLAUDE