fix(election-iomgr-refactor): startup election stability fixes

- delay.c: fix task_cmp 3-arg signature to match ordq_cmp_fn_t
- netio.c: skip SSL_new in endpoint_connect if ep->ssl pre-created
- ha.c: pre-create client SSL before endpoint_connect
- ha.c: fix double-free (let endpoint_fini own ep->data/vctx)
- ha.c: add endpoint_set_peer + AF_INET/SOCK_STREAM args to endpoint_init
- ha.c: fix voted_for concurrent-access with ha->lock in handle_request_vote
- ha.c: skip ECONNREFUSED immediate vote-deny in production (let timeout handle)
- myapp.c: priority-based startup stagger (priority_step_ms * priority)
- myapp.c: skip own election if already voted during startup delay
This commit is contained in:
Rongsong Shen 2026-05-30 15:36:10 +00:00
parent 81ea5e8eb8
commit 2f5a738b36

View file

@ -211,6 +211,37 @@ int myapp_run(myconf_t *myconf)
uint64_t my_lsn = (uint64_t)atomic_load_explicit(&c.ha.applied_lsn,
memory_order_acquire);
/* Startup stagger: delay election start by priority * election_priority_step_ms.
* Node 0 starts first; lower-priority nodes wait, see the winner's vote request
* arrive, and grant the vote instead of starting their own election.
* Uses the same step as the existing blocking-election probe path (Req 1.2). */
if (c.ha.my_priority > 0) {
uint32_t step_ms = c.ha.config.election_priority_step_ms
? c.ha.config.election_priority_step_ms
: HA_DEFAULT_ELECTION_PRIORITY_STEP_MS;
long delay_ns = (long)c.ha.my_priority * (long)step_ms * 1000000L;
struct timespec jitter = {
.tv_sec = delay_ns / 1000000000L,
.tv_nsec = delay_ns % 1000000000L,
};
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. */
{
pthread_mutex_lock(&c.ha.lock);
int already_voted = (c.ha.voted_for_name[0] != '\0');
pthread_mutex_unlock(&c.ha.lock);
if (already_voted) {
INFO("myapp_run: already voted during startup delay — "
"skipping own election, waiting for primary");
goto skip_election;
}
}
/* Non-blocking: arms election timeout watchdog, connects outgoing vote
* endpoints, returns immediately. Election resolves via iomgr callbacks;
* ha_elect_evaluate_and_signal calls cluster_init_phase2 from vtask context
@ -219,6 +250,8 @@ int myapp_run(myconf_t *myconf)
(n_other > 0) ? peer_ptrs : NULL,
my_tag, my_lsn, myconf->name);
skip_election:;
/* ── Step 9: cluster_run ─────────────────────────────────────────────── */
/* Req 9.4: myapp_run exits into cluster_run within 10ms of startup.