fix(controller-status): ctrlstatus_fill must prefer a live registry slot over a stale disconnected one (task 8.6, bug-1819)
Found live during task 8.5's optional catching-up-observation attempt: a killed-then-reconnected standby can end up occupying a NEW replicator_t.standbys[] slot while its OLD slot (host/port still populated, ep == NULL) also matches the same configured peer. ctrlstatus_fill's primary-side peer-status loop stopped at the first host:port match, so it permanently reported that peer dead even minutes after it had genuinely reconnected and fully caught up -- violating Requirement 13.3's "true, currently-known connection status" guarantee. Fix: scan all MAX_STANDBYS slots for a given peer and prefer any slot with ep != NULL over a slot with ep == NULL; fall back to dead only when no matching slot is live. Read-only, lock scope unchanged from task 8.2. TDD RED->GREEN (1216 Tests: 2 Failures -> 0), independently reproduced by the reviewer (exact RED replay) plus two independent live kill/backlog/restart/poll cycles on separate fresh clusters, both confirming the previously-stuck standby now converges to ok. design.md's Locked Decision 3 corrected to document the fix. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RLSNJhNoULvVcUhXEFafQC
This commit is contained in:
parent
464489d972
commit
6c437e2bed
5 changed files with 790 additions and 440 deletions
|
|
@ -253,22 +253,43 @@ void ctrlstatus_fill(cluster_t* c, xce_ctrlstatus_t* st)
|
|||
} 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 */
|
||||
* new one (Locked Decision 3).
|
||||
*
|
||||
* Scans ALL MAX_STANDBYS slots (does NOT stop at the first
|
||||
* host/port match) and prefers any slot with ep != NULL
|
||||
* over a slot with ep == NULL when multiple slots match the
|
||||
* same configured peer's host:port (task 8.6 fix). A killed
|
||||
* standby's slot is freed (ep cleared) but keeps its
|
||||
* host/port populated; when it reconnects, repl_add_standby's
|
||||
* ep-identity idempotency check cannot reuse that now-ep-NULL
|
||||
* slot, so the reconnect lands in a NEW slot -- leaving two
|
||||
* slots that share the same peer's host:port, one stale
|
||||
* (ep == NULL) and one live (ep != NULL). A first-match-wins
|
||||
* scan reported the stale, lower-index slot's dead status
|
||||
* forever, even after the peer genuinely reconnected and
|
||||
* caught up. If more than one live slot somehow matches, any
|
||||
* one of them is an acceptable source -- they would all be
|
||||
* observing the same live peer. */
|
||||
uint8_t status = 3u; /* default: no matching slot at all */
|
||||
int live_match = 0; /* found a slot with ep != NULL */
|
||||
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;
|
||||
mc->peers_cfg.candidates[i].hostname) != 0 ||
|
||||
repl->standbys[j].port != p->port) {
|
||||
continue; /* not a match for this peer */
|
||||
}
|
||||
if (repl->standbys[j].ep == NULL) {
|
||||
if (!live_match)
|
||||
status = 3u; /* stale match; keep scanning for a live one */
|
||||
continue;
|
||||
}
|
||||
if (!live_match) {
|
||||
live_match = 1;
|
||||
status = atomic_load_explicit(
|
||||
&repl->standbys[j].in_catch_up,
|
||||
memory_order_acquire)
|
||||
? 2u /* live, catching-up */
|
||||
: 1u; /* live, caught up: ok */
|
||||
}
|
||||
}
|
||||
p->status = status;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue