feat(ha-single-leader-role): legacy election path symmetry — direct CTRL_PRIMARY via shared sequence (task 3.1)

This commit is contained in:
Rongsong Shen 2026-06-13 10:16:14 +00:00
parent c089fb69f3
commit b7b951b9e7
4 changed files with 167 additions and 46 deletions

View file

@ -4,9 +4,11 @@
*
* 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_COMMITTING; task 4.4 committing timeout
* will later promote to CTRL_PRIMARY for degraded/single-node mode).
* immediately after ha_start_election_async returns. The single-node
* shortcircuit delegates to raft_start_election, which fires on_become_leader
* synchronously → raft_cb_become_leader (ha-single-leader-role task 2.2),
* so the role lands DIRECTLY in CTRL_PRIMARY — there is no transitional
* CTRL_PRIMARY_COMMITTING and no committing-timeout watchdog (Req 1.1, 1.5).
* - Function returns in < 5ms (observable via clock_gettime).
* - n_peers=1 shortcircuit stores n_peers in ha->n_peers.
*
@ -101,7 +103,7 @@ static void test_single_node_stores_n_peers(void)
printf("test_single_node_stores_n_peers: PASS\n");
}
/* ── Test 3: single-node shortcircuit enters CTRL_PRIMARY_COMMITTING ──────── */
/* ── Test 3: single-node shortcircuit enters CTRL_PRIMARY directly ───────── */
static void test_single_node_shortcircuit_evaluates_immediately(void)
{
@ -111,22 +113,24 @@ 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.
* 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). */
/* n_peers=1: the shortcircuit fires raft_start_election → on_become_leader
* (raft_cb_become_leader) synchronously. ha-single-leader-role (Req 1.1):
* a winning node lands DIRECTLY in CTRL_PRIMARY — there is no transitional
* CTRL_PRIMARY_COMMITTING. New-leader safety comes from the own-term
* WAL_NOOP + writable_lsn gate, not from a fourth role value. */
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_EQUAL_INT_MESSAGE((int)CTRL_PRIMARY_COMMITTING, (int)role,
"single-node shortcircuit must enter CTRL_PRIMARY_COMMITTING (not CTRL_PRIMARY)");
TEST_ASSERT_EQUAL_INT_MESSAGE((int)CTRL_PRIMARY, (int)role,
"single-node shortcircuit must enter CTRL_PRIMARY directly (Req 1.1) — "
"no transitional CTRL_PRIMARY_COMMITTING");
ha_fini(&ha);
printf("test_single_node_shortcircuit_evaluates_immediately: PASS (role=%d == CTRL_PRIMARY_COMMITTING)\n",
printf("test_single_node_shortcircuit_evaluates_immediately: PASS (role=%d == CTRL_PRIMARY)\n",
(int)role);
}
/* ── Test 6: single-node shortcircuit does NOT go directly to CTRL_PRIMARY ── */
/* ── Test 6: single-node shortcircuit lands directly in CTRL_PRIMARY ──────── */
static void test_single_node_not_primary_directly(void)
{
@ -135,13 +139,17 @@ static void test_single_node_not_primary_directly(void)
ha_start_election_async(&ha, NULL, 1, NULL, 0ULL, 0ULL, "ctrl-a");
/* ha-single-leader-role (Req 1.1): the winning node lands in CTRL_PRIMARY
* immediately — the old "must go through COMMITTING first" contract is gone.
* Promotion arms NO committing-timeout watchdog (Req 1.5); the own-term gate
* (WAL_NOOP + writable_lsn) provides new-leader safety instead. */
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");
TEST_ASSERT_EQUAL_INT_MESSAGE((int)CTRL_PRIMARY, (int)role,
"single-node shortcircuit must land DIRECTLY in CTRL_PRIMARY (Req 1.1) — "
"no transitional CTRL_PRIMARY_COMMITTING, no committing watchdog (Req 1.5)");
ha_fini(&ha);
printf("test_single_node_not_primary_directly: PASS (role=%d)\n", (int)role);
printf("test_single_node_not_primary_directly: PASS (role=%d == CTRL_PRIMARY)\n", (int)role);
}
/* ── Test 4: ha->n_peers set for multi-peer count too ───────────────────── */