fix(ha-repl-hardening): n_peers count and single-node safe_lsn (task 6.5)

- Fix ha_start_election_async: ha->n_peers = n_other (other peers),
  not candidates_count (total including self). The standby_converged[]
  array is indexed by peer index, so the bound must be n_other.
- Fix ha_compute_safe_committed_lsn: return self_lsn for n_peers==0
  (prevents WAL truncation on single-node re-election).
- Integration test: 3-node cluster reaches CTRL_PRIMARY via majority
  convergence within ~20s; no degraded-mode WARN in logs.
This commit is contained in:
Rongsong Shen 2026-06-05 07:03:37 +00:00
parent 345d51dd61
commit e10cf9a6fc
2 changed files with 13 additions and 7 deletions

View file

@ -3041,23 +3041,29 @@ void ha_start_election_async(ha_state_t *ha, cluster_t *c,
raft_init(&ha->raft, &rcfg);
}
/* Step 1: store peer count for ha_repl_ack_received quorum calculation */
ha->n_peers = n_peers;
/* Step 2: transition to CTRL_ELECTING before firing RPCs.
/* Step 1: transition to CTRL_ELECTING before firing RPCs.
* raft_start_election fires on_become_leader / on_send_vote_request below;
* the role must already be ELECTING so ha_dispatch_gate() admits vote RPCs. */
atomic_store_explicit(&ha->role, (int32_t)CTRL_ELECTING, memory_order_release);
INFO("HA FSM: → CTRL_ELECTING (async, n_peers=%d)", n_peers);
/* Step 3: pre-allocate ha_elect_ctx_t so raft_cb_send_vote_request can
/* Step 2: pre-allocate ha_elect_ctx_t so raft_cb_send_vote_request can
* read my_tag/my_lsn from it when building the capnp VoteRequest payload.
* Vote counting is now owned by the raft library; this ctx is only a
* metadata carrier for tag/lsn. For multi-node elections it is kept alive
* until all per-peer vote_ep_ctx_t endpoints finish (iomgr lifetime).
* For single-node, raft_start_election fires on_become_leader immediately
* so no ctx reads occur after return. */
* so no ctx reads occur after return.
*
* n_other = other-node count (self excluded). standby_converged[] is
* indexed by votes_idx (relative, 0 .. n_other-1), so ha->n_peers must
* store n_other, NOT the total candidates_count. Both
* ha_check_majority_convergence and ha_repl_ack_received read ha->n_peers
* as "number of OTHER nodes". */
int n_other = (n_peers > 1) ? n_peers - 1 : 0;
/* Store other-peer count for quorum calculations */
ha->n_peers = n_other;
ha_elect_ctx_t *ctx = calloc(1, sizeof(*ctx) +
(size_t)n_other * sizeof(ctx->votes[0]));
if (!ctx) {