fix(election-iomgr-refactor): correct task 2.4 review findings

- CRITICAL: single-node shortcircuit enters CTRL_PRIMARY_COMMITTING
  (not CTRL_PRIMARY directly); phase2 transition deferred to task 4.x
- CRITICAL: add n_peers==0 guard in ha_elect_evaluate_and_signal to
  prevent votes[0] out-of-bounds access
- HIGH: wire client_ssl_ctx after endpoint_connect for outgoing votes
- HIGH: add timeout_ctx field to ha_elect_ctx_t for task 4.x cancel
- HIGH: clarify vote_ep_err_handler ownership (no premature free of vctx;
  deferred to ha_election_cleanup in task 5.1)
- Tests updated: single-node shortcircuit asserts COMMITTING not PRIMARY
This commit is contained in:
Rongsong Shen 2026-05-30 09:27:48 +00:00
parent e50afd02f6
commit 543981c505
3 changed files with 80 additions and 13 deletions

View file

@ -5,8 +5,8 @@
* Strategy:
* - n_peers=1 shortcircuit: no iomgr needed; election completes synchronously
* via ha_elect_evaluate_and_signal immediately after ha_start_election_async
* returns (role flips to CTRL_PRIMARY because phase2_done=0 path routes
* through election_timeout_fn logic).
* returns (role flips to CTRL_PRIMARY_COMMITTING; task 4.4 committing timeout
* will later promote to CTRL_PRIMARY for degraded/single-node mode).
* - Function returns in < 5ms (observable via clock_gettime).
* - n_peers=1 shortcircuit stores n_peers in ha->n_peers.
*
@ -101,7 +101,7 @@ static void test_single_node_stores_n_peers(void)
printf("test_single_node_stores_n_peers: PASS\n");
}
/* ── Test 3: single-node shortcircuit fires ha_elect_evaluate_and_signal ─── */
/* ── Test 3: single-node shortcircuit enters CTRL_PRIMARY_COMMITTING ──────── */
static void test_single_node_shortcircuit_evaluates_immediately(void)
{
@ -111,21 +111,39 @@ static void test_single_node_shortcircuit_evaluates_immediately(void)
/* Start in CTRL_ELECTING state so role transition is visible */
atomic_store_explicit(&ha.role, (int32_t)CTRL_ELECTING, memory_order_release);
/* n_peers=1: must call ha_elect_evaluate_and_signal immediately,
* which for a fresh single-node election grants PRIMARY.
* Since phase2_done=0, the COMMITTING state is entered
* (or PRIMARY directly depending on design, but role must NOT stay ELECTING). */
/* n_peers=1: must call ha_elect_evaluate_and_signal immediately.
* Fix 1 (CRITICAL): single-node shortcircuit must enter CTRL_PRIMARY_COMMITTING,
* not CTRL_PRIMARY. Task 4.4 committing timeout will promote to CTRL_PRIMARY
* once the committing phase completes (Req 8.4 / degraded-single-node path). */
ha_start_election_async(&ha, NULL, 1, NULL, 100ULL, 50ULL, "ctrl-a");
ctrl_role_t role = (ctrl_role_t)atomic_load_explicit(&ha.role, memory_order_acquire);
TEST_ASSERT_NOT_EQUAL_MESSAGE((int)CTRL_ELECTING, (int)role,
"single-node shortcircuit must transition role away from CTRL_ELECTING");
TEST_ASSERT_EQUAL_INT_MESSAGE((int)CTRL_PRIMARY_COMMITTING, (int)role,
"single-node shortcircuit must enter CTRL_PRIMARY_COMMITTING (not CTRL_PRIMARY)");
ha_fini(&ha);
printf("test_single_node_shortcircuit_evaluates_immediately: PASS (role=%d)\n",
printf("test_single_node_shortcircuit_evaluates_immediately: PASS (role=%d == CTRL_PRIMARY_COMMITTING)\n",
(int)role);
}
/* ── Test 6: single-node shortcircuit does NOT go directly to CTRL_PRIMARY ── */
static void test_single_node_not_primary_directly(void)
{
ha_state_t ha;
ha_init(&ha, &g_async_cfg, 0);
ha_start_election_async(&ha, NULL, 1, NULL, 0ULL, 0ULL, "ctrl-a");
ctrl_role_t role = (ctrl_role_t)atomic_load_explicit(&ha.role, memory_order_acquire);
TEST_ASSERT_NOT_EQUAL_MESSAGE((int)CTRL_PRIMARY, (int)role,
"single-node shortcircuit must NOT jump directly to CTRL_PRIMARY; "
"must go through CTRL_PRIMARY_COMMITTING first");
ha_fini(&ha);
printf("test_single_node_not_primary_directly: PASS (role=%d)\n", (int)role);
}
/* ── Test 4: ha->n_peers set for multi-peer count too ───────────────────── */
static void test_n_peers_stored_for_multi_peer(void)
@ -175,6 +193,7 @@ void test_ha_start_election_async(void)
RUN_TEST(test_single_node_returns_quickly);
RUN_TEST(test_single_node_stores_n_peers);
RUN_TEST(test_single_node_shortcircuit_evaluates_immediately);
RUN_TEST(test_single_node_not_primary_directly);
RUN_TEST(test_n_peers_stored_for_multi_peer);
RUN_TEST(test_single_node_increments_term);
}