feat(election-iomgr-refactor): remove old blocking election code (am_i_primary_quorum, vote_request_thread, bootstrap_config_if_needed) — task 5.3

This commit is contained in:
Rongsong Shen 2026-05-30 13:04:19 +00:00
parent 31a54043d5
commit 27ea14cd9a
7 changed files with 83 additions and 512 deletions

View file

@ -40,7 +40,7 @@
/* xce_request: defined in request.c, not yet in any public header */
extern int xce_request(endpoint_t *ep, xce_request_t *req);
/* SSL context for xce_probe_config_tag — set by bootstrap_config_if_needed.
/* SSL context for xce_probe_config_tag.
* Stored as void* to avoid SSL_CTX type at this point in the translation unit. */
static void *s_probe_ssl_ctx = NULL;
@ -1365,7 +1365,7 @@ int handle_upgrade_config(cluster_t *c, endpoint_t *ep, xce_request_t *req,
*
* Single-RTT config tag probe to one peer controller over SSL.
*
* Wire format (same pattern as vote_request_thread in ha.c):
* Wire format:
* Send: uint64_t size_hdr + capnp(ProbeConfigTagRequest) [direct struct]
* Receive: uint64_t size_hdr + capnp(ProbeConfigTagReply) [direct struct]
*
@ -1391,8 +1391,7 @@ int xce_probe_config_tag(const char *host, uint16_t port, uint64_t *tag_out)
/* ── Step 1: SSL connect via connect_ssl_raw (same path as do_one_config_fetch) ── */
{
/* s_ssl_ctx is set in bootstrap_config_if_needed which passes it here.
* If NULL (unit test), fall back to plain TCP. */
/* s_ssl_ctx may be set externally; if NULL (unit test), fall back to plain TCP. */
SSL_CTX *ssl_ctx = s_probe_ssl_ctx;
if (ssl_ctx) {
@ -1539,107 +1538,6 @@ int (*xce_probe_config_tag_hook)(const char *host, uint16_t port,
uint64_t *tag_out) = NULL;
#endif
/* ── bootstrap_config_if_needed ────────────────────────────────────────────
*
* Pre-election config probe for unconfigured nodes (config_version_tag == 0).
*
* 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)
{
/* Make the SSL context available to xce_probe_config_tag (used below) */
s_probe_ssl_ctx = c->ha.ssl_ctx;
/* Revised bootstrap strategy: skip the pre-election config probe entirely.
* The election protocol already carries config_tag in OP_requestVote, so
* the node with the freshest config wins naturally (Req 2.4 freshness gate).
* Standbys with config_tag=0 will lose the election (or yield to a fresher
* node), then fetch config from the elected primary via fetch_config_from_primary().
* This avoids the SSL/plain-TCP mismatch in the probe path. */
if (c->myconf->config_version_tag == 0) {
c->ha.defer_config_fetch = true;
INFO("HA bootstrap: config_tag=0 — skipping pre-election probe, "
"will fetch config from primary after election");
return 0;
}
/* Req 3.1: fast path — already have config */
if (c->myconf->config_version_tag != 0)
return 0;
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;
}
/* ── sanitise_cn ────────────────────────────────── */
char *sanitise_cn(const char *src, size_t maxlen)