feat(ha): reference-counted ha_elect_ctx_t lifecycle (ha-elect-ctx-lifecycle)
Replace the deliberate election-carrier leak with an atomic refcount: a bias reference held by ha->elect_ctx plus one reference per captured vote endpoint, released via a new endpoint_t.on_fini teardown-completion hook (fired once, before ep->data is freed). Both real-election and PreVote capture sites (raft_cb_send_vote_request, raft_cb_send_pre_vote_request) increment on capture; both publish sites (ha_start_election_async, ha_start_prevote_async) now atomic_exchange the outgoing carrier and release it instead of leaking or freeing outright, so a still-outstanding vote endpoint keeps the superseded carrier alive until its own teardown completes. Verified: 758+ controller tests green, isolated LeakSanitizer run shows zero ha_elect_ctx_t leaks across many election/PreVote rounds, no premature-free/UAF under supersession, and a live 10-round HA soak (36 jobs, zero loss, all 3 nodes served as primary).
This commit is contained in:
parent
58e047f533
commit
6529dcf08f
14 changed files with 879 additions and 22 deletions
|
|
@ -112,6 +112,18 @@ typedef struct {
|
|||
* where votes[] is empty (n_peers=0). */
|
||||
uint64_t self_lsn; /* Candidate's own WAL LSN at election start */
|
||||
uint64_t self_tag; /* Candidate's own config_tag at election start */
|
||||
/* ha-elect-ctx-lifecycle task 2.1: reference count governing this
|
||||
* carrier's lifetime. Starts at 1 (bias reference for "currently
|
||||
* published in ha->elect_ctx"), incremented once per vote_ep_ctx_t that
|
||||
* captures this carrier (raft_cb_send_vote_request /
|
||||
* raft_cb_send_pre_vote_request), decremented once by that endpoint's
|
||||
* on_fini completion hook and once when a later round's atomic_exchange
|
||||
* supersedes this carrier in ha->elect_ctx. Freed via
|
||||
* ha_elect_ctx_release() when it reaches zero, from whichever of those
|
||||
* two release kinds fires last — see design.md's Architecture §Mechanism
|
||||
* for the full rationale (a pure per-endpoint count would allow a
|
||||
* spurious free before the round is superseded). */
|
||||
_Atomic int refcount;
|
||||
ha_vote_entry_t votes[]; /* C99 flexible array: n_peers-1 entries */
|
||||
} ha_elect_ctx_t;
|
||||
|
||||
|
|
@ -1709,6 +1721,14 @@ extern void (*ha_rdhup_election_hook)(ha_state_t *ha, cluster_t *c,
|
|||
uint64_t my_config_tag, uint64_t my_lsn,
|
||||
const char *my_name);
|
||||
|
||||
/*
|
||||
* ha_elect_ctx_release_for_test (ha-elect-ctx-lifecycle task 2.1): drive the
|
||||
* static ha_elect_ctx_release() release/free helper directly, so unit tests
|
||||
* can verify the reference-count-to-zero mechanism without staging a full
|
||||
* election round.
|
||||
*/
|
||||
void ha_elect_ctx_release_for_test(ha_elect_ctx_t *ctx);
|
||||
|
||||
/*
|
||||
* vote_ep_ctx_set_cluster_for_test: set the cluster_t pointer inside the
|
||||
* vote_ep_ctx_t stored at ep->data. Used by unit tests that need the
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue