Skip to content

Replica status metrics are never emitted #5

Description

@villagesql-adam

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions