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:
Rongsong Shen 2026-07-12 04:25:42 +00:00
parent 58e047f533
commit 6529dcf08f
14 changed files with 879 additions and 22 deletions

View file

@ -101,6 +101,13 @@ typedef struct endpoint_t {
time_t ts_last_in;
time_t ts_last_out;
void (*on_connected)(struct endpoint_t *ep); /* fire-once; called in iomgr thread after ssl_handshake_done=1 */
/* ha-elect-ctx-lifecycle task 1.1: fire-once completion-side counterpart
* to on_connected — called by endpoint_fini() immediately before it frees
* ep->data, so a consumer can observe teardown completion (e.g. release a
* reference a struct pointed to by ep->data was holding) while ep->data is
* still valid. Does not change endpoint_fini's teardown ordering/timing —
* it only adds an observation point at the existing free. */
void (*on_fini)(struct endpoint_t *ep);
/* bug-1298: reference count governing the lifetime of a HEAP-allocated
* endpoint (endpoint_get). An ep is fini'd + freed exactly when refcnt
* drops to 0. Holders: the creator/owner (endpoint_get sets refcnt=1, the
@ -176,6 +183,7 @@ extern int endpoint_set_owner(endpoint_t*, void*);
extern int endpoint_set_msg_handler(endpoint_t*, msg_handler_t);
extern int endpoint_set_err_handler(endpoint_t*, err_handler_t);
extern int endpoint_set_on_connected(endpoint_t*, void (*cb)(endpoint_t *));
extern int endpoint_set_on_fini(endpoint_t*, void (*cb)(endpoint_t *));
extern void endpoint_fini(endpoint_t*);
/* bug-1298: endpoint refcount. endpoint_ref takes a reference; endpoint_unref
* drops one and, when the last reference is released, fini's the ep and (for