feat(controller-status): source per-controller connection status in ctrlstatus_fill (task 8.2)

Populates CtrlPeer.status (added in task 8.1) inside ctrlstatus_fill's
peer-building loop: self always reports ok; a primary responder sources
every other peer from the live replication connection registry
(replicator_t.standbys[], host+port matched, reusing
repl_active_standby_count's ep!=NULL && !in_catch_up predicate) --
dead/catching-up/ok; a non-primary responder reports unknown for every
non-self peer. A self-identity mismatch (pre-existing misconfiguration)
logs one WARN and falls through to the normal non-self sourcing rather
than forcing any peer to ok. Read-only: a bounded lock-protected scan,
no state mutation.

TDD: 3 new tests (primary-sourced-from-registry, standby-unknown-except-
self, self-mismatch-falls-through) plus an extended no-mutation
assertion; RED->GREEN independently reproduced by the reviewer.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RLSNJhNoULvVcUhXEFafQC
This commit is contained in:
Rongsong Shen 2026-09-16 15:18:15 +00:00
parent 1b278c0e9a
commit ab5dd91537
No known key found for this signature in database
GPG key ID: 5BCDAAE0123E9FE2
3 changed files with 288 additions and 1 deletions

View file

@ -22,6 +22,7 @@
#include "cluster.h"
#include "ha.h"
#include "persist.h"
#include "utils.h" /* WARN */
#include "xce/cluster_def.h"
/*
@ -197,13 +198,89 @@ void ctrlstatus_fill(cluster_t* c, xce_ctrlstatus_t* st)
if (n > 0) {
st->peers.list = (xce_ctrlpeer_t**)calloc((size_t)n,
sizeof(xce_ctrlpeer_t*));
/* per-controller connection status (Req 13.1-13.5; design.md Req 13
* delta, Locked Decisions 3-6): the responder's own entry always
* reports ok; when the responder is currently primary, every OTHER
* peer is sourced by host+port match against the live replication
* connection registry (replicator_t.standbys[]) -- no live
* connection -> dead, live but not yet caught up -> catching-up,
* live and caught up -> ok; when the responder is not primary,
* every other peer is unknown, since only the primary has direct
* connection visibility into every other node. */
int is_primary = (st->responderRole == (uint8_t)CTRL_PRIMARY);
/* ha->repl is a back-pointer wired once commit-engine setup runs
* (cluster.c) and may be NULL before that (or in a minimal test
* fixture); &c->persist.repl is always a valid fallback (mirrors
* the ha->repl ? ha->repl : &p->repl idiom at ha.c:2673). */
replicator_t* repl = ha->repl ? ha->repl : &c->persist.repl;
/* Self-match fallback (design-review finding): if this responder's
* own identity matches none of its configured peers -- a pre-
* existing misconfiguration, not new to this delta -- WARN once per
* call and let every peer fall through to the normal non-self
* branches below. No entry is force-corrected to ok in this case;
* this is an explicit, logged degradation rather than a silent or
* fabricated result. */
int self_found = 0;
for (int i = 0; i < n; i++) {
if (strcmp(mc->peers_cfg.candidates[i].name,
st->responderName) == 0) {
self_found = 1;
break;
}
}
if (!self_found) {
WARN("ctrlstatus: responder identity '%s' matches none of the "
"%d configured peers -- per-peer connection status falls "
"through to the non-self sourcing for every configured "
"peer (check this node's --peers/name configuration)",
st->responderName, n);
}
if (is_primary)
mylock_lock(&repl->lock);
for (int i = 0; i < n; i++) {
xce_ctrlpeer_t* p = (xce_ctrlpeer_t*)calloc(1, sizeof(*p));
p->name = mc->peers_cfg.candidates[i].name;
p->host = mc->peers_cfg.candidates[i].hostname;
p->port = (uint32_t)mc->peers_cfg.candidates[i].port;
if (strcmp(mc->peers_cfg.candidates[i].name,
st->responderName) == 0) {
p->status = 1u; /* self: always ok (Req 13.2) */
} else if (is_primary) {
/* Reuses repl_active_standby_count's exact ep != NULL &&
* !in_catch_up predicate (repl.c) rather than inventing a
* new one (Locked Decision 3). */
uint8_t status = 3u; /* default: no matching live slot */
for (int j = 0; j < MAX_STANDBYS; j++) {
if (strcmp(repl->standbys[j].host,
mc->peers_cfg.candidates[i].hostname) == 0 &&
repl->standbys[j].port == p->port) {
if (repl->standbys[j].ep == NULL) {
status = 3u; /* matched slot, no live conn */
} else if (atomic_load_explicit(
&repl->standbys[j].in_catch_up,
memory_order_acquire)) {
status = 2u; /* catching-up */
} else {
status = 1u; /* ok */
}
break;
}
}
p->status = status;
} else {
p->status = 0u; /* unknown: only the primary can see peers */
}
st->peers.list[i] = p;
}
if (is_primary)
mylock_unlock(&repl->lock);
}
}