chore: checkpoint all prior work for clean state

Captures all uncommitted work from ha-config-bootstrap,
ha-heartbeat-liveness, and associated spec/tooling updates:

- src: ha.c, ha.h, cluster.c, config_repl.c, myapp.c, raft.c —
  heartbeat liveness, RDHUP integration, committing phase fixes
- tests: test_ha_role.c, test_config_repl.c, test_ha_rdhup_integration.c,
  test_p1_integration.c, test_p3_hardening.c, scheduler test
- specs: election-iomgr-refactor, ha-heartbeat-liveness,
  ha-peer-connection-reuse, raft-library (new); ha-config-bootstrap
  and election-iomgr-refactor task/research updates
- tooling: .wolf/ state, ha/data cluster fixtures, raft/raft.tla,
  src/build symlink, xce-build skill update
This commit is contained in:
Rongsong Shen 2026-06-04 13:04:16 +00:00
parent 15bd291bdd
commit e31673dd88
66 changed files with 122422 additions and 860 deletions

View file

@ -68,6 +68,12 @@ int myapp_run(myconf_t *myconf)
return rc;
}
/* Wire raft_c before iomgr starts so raft_cb_persist can look up
* candidate names when processing incoming vote requests during the
* startup stagger nap. Without this, voted_for_name is cleared and the
* already_voted check fails → unnecessary duplicate elections. */
c.ha.raft_c = &c;
/* ── Step 2: SSL contexts ─────────────────────────────────────────────── */
/* Validate cert/key files early to fail fast before any network setup */
@ -227,17 +233,30 @@ int myapp_run(myconf_t *myconf)
nanosleep(&jitter, NULL);
}
/* If we already voted for someone during the startup delay (incoming vote
* request arrived from the higher-priority node that started first), skip
* starting our own election — we'll become STANDBY via the election timeout
* watchdog on the winner's side, or promote ourselves if no heartbeat arrives. */
/* If a leader is already known after the startup stagger (either because
* iomgr processed an incoming vote request and updated the raft term, or
* because ctrlhello arrived on a promoted vote ep), skip starting our own
* election. The voted_for_name check catches the synchronous vote-grant
* path (iomgr was already running); the raft term + primary_ctrl_fd checks
* catch the case where iomgr was NOT running during the nap (connections
* queued in the kernel backlog) but the winner's ctrlhello has already
* established the control channel. */
{
int leader_known = 0;
pthread_mutex_lock(&c.ha.lock);
int already_voted = (c.ha.voted_for_name[0] != '\0');
if (c.ha.voted_for_name[0] != '\0')
leader_known = 1;
pthread_mutex_unlock(&c.ha.lock);
if (already_voted) {
INFO("myapp_run: already voted during startup delay — "
"skipping own election, waiting for primary");
if (!leader_known && c.ha.raft.term > 0)
leader_known = 1;
if (!leader_known && c.ha.primary_ctrl_fd >= 0)
leader_known = 1;
if (leader_known) {
INFO("myapp_run: leader already known after startup delay "
"(voted_for=%s, raft_term=%" PRIu64 ", primary_fd=%d) — "
"skipping own election",
c.ha.voted_for_name[0] ? c.ha.voted_for_name : "(none)",
c.ha.raft.term, c.ha.primary_ctrl_fd);
goto skip_election;
}
}