forked from lei.chen/myxce
feat(ha-config-bootstrap): implement bootstrap_config_if_needed() (task 5.3)
- Probe all peers: any tag>0 -> defer_config_fetch=true; all-zero -> proceed; none reachable -> exponential backoff 500ms->10s cap with WARN per attempt - Wire into myapp.c: before ha_wait_for_heartbeat_or_timeout() - Post-election: defer_config_fetch && CTRL_STANDBY -> fetch_config_from_primary() - INFO log for all three bootstrap outcomes (Req 10.2) - XCE_TESTING hook for probe mock; test_ha_bootstrap_support.c installs mock - 9/9 bootstrap tests GREEN, 141/141 full suite GREEN
This commit is contained in:
parent
0c4a0760d1
commit
ab174c8b06
6 changed files with 163 additions and 8 deletions
|
|
@ -1525,21 +1525,94 @@ int xce_probe_config_tag(const char *host, uint16_t port, uint64_t *tag_out)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef XCE_TESTING
|
||||
/* Injection point for unit tests — NULL means use real xce_probe_config_tag */
|
||||
int (*xce_probe_config_tag_hook)(const char *host, uint16_t port,
|
||||
uint64_t *tag_out) = NULL;
|
||||
#endif
|
||||
|
||||
/* ── bootstrap_config_if_needed ────────────────────────────────────────────
|
||||
*
|
||||
* Stub: task 5.3 will implement the full pre-election config probe.
|
||||
* Declared in ha.h; needed here so the unit test binary links cleanly
|
||||
* for the probe tests (task 5.2 scope).
|
||||
* Pre-election config probe for unconfigured nodes (config_version_tag == 0).
|
||||
*
|
||||
* Returns -ENOSYS (not yet implemented) for any cluster with peers.
|
||||
* Returns 0 immediately when config_version_tag != 0 (Req 3.1 fast path).
|
||||
* Outcomes:
|
||||
* 1. Local config exists (config_version_tag != 0) → return 0 immediately (3.1)
|
||||
* 2. All reachable peers have tag=0 → first-boot cluster; return 0, no defer (3.3)
|
||||
* 3. Any peer has tag > 0 → set ha.defer_config_fetch = true; return 0 (3.2)
|
||||
* 4. No peer reachable → retry with exponential backoff 500ms→1s→...→10s (3.4)
|
||||
*
|
||||
* Requirements: 3.1, 3.2, 3.3, 3.4, 10.2
|
||||
* Boundary: Config Bootstrap (config_repl.c)
|
||||
*/
|
||||
int bootstrap_config_if_needed(cluster_t *c)
|
||||
{
|
||||
/* Req 3.1: fast path — already have config */
|
||||
if (c->myconf->config_version_tag != 0)
|
||||
return 0; /* already have config — nothing to do (Req 3.1) */
|
||||
return 0;
|
||||
|
||||
/* Full probe loop implemented in task 5.3 */
|
||||
const xce_cluster_config_t *pcfg = &c->myconf->peers_cfg;
|
||||
int n_peers = pcfg->candidates_count;
|
||||
|
||||
/* Single-node or no peers configured — nothing to probe */
|
||||
if (n_peers == 0) {
|
||||
INFO("HA bootstrap: no peers configured, proceeding as first-boot primary");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Exponential backoff state: 500ms → doubling → 10s cap (Req 3.4) */
|
||||
uint32_t backoff_ms = 500;
|
||||
const uint32_t backoff_max_ms = 10000;
|
||||
int attempt = 0;
|
||||
|
||||
for (;;) {
|
||||
int any_nonzero = 0; /* any peer replied with tag > 0 */
|
||||
int any_reachable = 0; /* any peer replied at all (tag >= 0) */
|
||||
|
||||
for (int i = 0; i < n_peers; i++) {
|
||||
const xce_candidate_t *peer = &pcfg->candidates[i];
|
||||
uint64_t tag_out = 0;
|
||||
|
||||
#ifdef XCE_TESTING
|
||||
int rc = xce_probe_config_tag_hook
|
||||
? xce_probe_config_tag_hook(peer->hostname, (uint16_t)peer->port, &tag_out)
|
||||
: xce_probe_config_tag(peer->hostname, (uint16_t)peer->port, &tag_out);
|
||||
#else
|
||||
int rc = xce_probe_config_tag(peer->hostname, (uint16_t)peer->port, &tag_out);
|
||||
#endif
|
||||
if (rc == 0) {
|
||||
any_reachable = 1;
|
||||
if (tag_out != 0) {
|
||||
any_nonzero = 1;
|
||||
/* Req 3.2: at least one peer has config — defer fetch */
|
||||
c->ha.defer_config_fetch = true;
|
||||
INFO("HA bootstrap: peer '%s:%u' has config tag=%016" PRIx64
|
||||
" — deferring config fetch to post-election",
|
||||
peer->hostname, (unsigned)peer->port, tag_out);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (any_reachable) {
|
||||
/* Req 3.3: all reachable peers have tag=0 — first-boot cluster */
|
||||
INFO("HA bootstrap: all reachable peers have tag=0 — first-boot cluster, "
|
||||
"proceeding to election without config fetch");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Req 3.4: no peer reachable — retry with exponential backoff */
|
||||
attempt++;
|
||||
WARN("HA bootstrap: no peer reachable after %d probe round(s) — "
|
||||
"retrying in %u ms (attempt %d)",
|
||||
attempt, backoff_ms, attempt);
|
||||
|
||||
usleep((useconds_t)backoff_ms * 1000);
|
||||
|
||||
backoff_ms = (backoff_ms * 2 < backoff_max_ms)
|
||||
? backoff_ms * 2 : backoff_max_ms;
|
||||
}
|
||||
|
||||
/* Unreachable — blocks until a peer responds or process is killed */
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue