forked from lei.chen/myxce
feat(ha-single-leader-role): delete force-promotion watchdog + convergence/ack machinery (task 4.1)
This commit is contained in:
parent
b7b951b9e7
commit
a5bf86112a
10 changed files with 95 additions and 1869 deletions
|
|
@ -59,7 +59,10 @@ extern endpoint_t *vote_ep_ctx_build_for_test(endpoint_t *ep,
|
|||
extern void raft_cb_become_leader_for_test(ha_state_t *ha, uint64_t term);
|
||||
extern void raft_cb_become_follower_for_test(ha_state_t *ha, uint64_t term);
|
||||
|
||||
/* ha_repl_ack_received: public, declared in ha.h */
|
||||
/* The replication-ack promotion handler was DELETED in production
|
||||
* (ha-single-leader-role task 4.1: the ack-counting force-promotion path is gone —
|
||||
* promotion is driven by the heartbeat decision loop + per-entry majority ack).
|
||||
* The ack-counting cases that depended on it were deleted; no shim. */
|
||||
|
||||
/* ha_phase2_hook: suppress cluster_init_phase2 in callbacks */
|
||||
extern int (*ha_phase2_hook)(void *c);
|
||||
|
|
@ -173,39 +176,12 @@ static void test_dispatcher_electing_calls_vote_reply_hook(void)
|
|||
printf("test_dispatcher_electing_calls_vote_reply_hook: PASS\n");
|
||||
}
|
||||
|
||||
/* ── Test 2: dispatcher routes CTRL_PRIMARY_COMMITTING → repl_ack ────────── */
|
||||
|
||||
static void test_dispatcher_committing_handles_repl_ack(void)
|
||||
{
|
||||
ha_state_t ha;
|
||||
init_ha_with_standby_eps(&ha);
|
||||
ha.n_peers = 2;
|
||||
atomic_store_explicit(&ha.role, (int32_t)CTRL_PRIMARY_COMMITTING,
|
||||
memory_order_release);
|
||||
atomic_store_explicit(&ha.repl_ack_count, 0, memory_order_release);
|
||||
|
||||
ha_elect_ctx_t *ctx = alloc_elect_ctx(1);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(ctx, "alloc_elect_ctx must succeed");
|
||||
|
||||
endpoint_t ep;
|
||||
memset(&ep, 0, sizeof(ep));
|
||||
vote_ep_ctx_build_for_test(&ep, &ha, ctx, 0, 1, 0, 0, "test");
|
||||
|
||||
io_packet_t *pkt = repl_catchup_done_pkt_for_test();
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(pkt, "repl_catchup_done_pkt_for_test must succeed");
|
||||
|
||||
int rc = peer_ctrl_msg_handler_for_test(&ep, pkt);
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc, "handler must return 0");
|
||||
|
||||
int acks = atomic_load_explicit(&ha.repl_ack_count, memory_order_acquire);
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(1, acks,
|
||||
"CTRL_COMMITTING: replCatchUpDone must increment repl_ack_count (Req 1.2)");
|
||||
|
||||
free(ep.data);
|
||||
free_elect_ctx(ctx);
|
||||
fini_ha_with_standby_eps(&ha);
|
||||
printf("test_dispatcher_committing_handles_repl_ack: PASS\n");
|
||||
}
|
||||
/* ── Test 2 (dispatcher CTRL_PRIMARY_COMMITTING → repl_ack count) DELETED ─────
|
||||
* ha-single-leader-role task 4.1. Subject was the deleted ack-counting
|
||||
* promotion machinery (replCatchUpDone incrementing repl_ack_count to drive
|
||||
* COMMITTING → PRIMARY). That path is gone — nothing left to verify. No shim
|
||||
* (design §"Transitional-State Deletions").
|
||||
*/
|
||||
|
||||
/* ── Test 3: dispatcher drops packet for CTRL_STANDBY ───────────────────── */
|
||||
|
||||
|
|
@ -502,92 +478,14 @@ static void test_follower_passive_no_outbound_connect(void)
|
|||
printf("test_follower_passive_no_outbound_connect: PASS\n");
|
||||
}
|
||||
|
||||
/* ── Test 10 (Task 7.3): promoted connection carries WAL replication ACK ────
|
||||
*
|
||||
* Req 5.2: OPCode_replCatchUpDone decoded on a promoted endpoint while in
|
||||
* CTRL_PRIMARY_COMMITTING must increment repl_ack_count. When majority
|
||||
* threshold is reached, role transitions to CTRL_PRIMARY via ha_promote_to_primary.
|
||||
*
|
||||
* Uses a 5-node cluster (n_peers=4: self + 4 peers).
|
||||
* quorum = (4+2)/2 = 3; threshold = quorum-1 = 2.
|
||||
* So two peer ACKs are required before promotion, allowing us to verify
|
||||
* the "still COMMITTING after first ACK" invariant.
|
||||
*
|
||||
* ha_promote_to_primary requires a non-NULL cluster_t, so we embed the
|
||||
* ha_state_t inside a zero-init cluster_t and pass &c via
|
||||
* vote_ep_ctx_set_cluster_for_test. ha_promote_to_primary is safe on a
|
||||
* zero-init cluster_t: xce_add_hook returns -EINVAL harmlessly (zeroed
|
||||
* hooksys), and ha_start_primary_heartbeat skips when delaymgr is not running.
|
||||
*
|
||||
* Sequence:
|
||||
* - Set n_peers=4 (5-node cluster → quorum=3 → threshold=2 peer ACKs)
|
||||
* - Inject first replCatchUpDone → repl_ack_count=1, still COMMITTING
|
||||
* - Inject second replCatchUpDone → repl_ack_count=2 >= threshold → CTRL_PRIMARY
|
||||
/* ── Test 10 (Task 7.3: promoted ep repl_ack promotes to PRIMARY) DELETED ─────
|
||||
* ha-single-leader-role task 4.1. Subject was the deleted ack-counting
|
||||
* promotion machinery (replCatchUpDone incrementing repl_ack_count until the
|
||||
* majority threshold drives COMMITTING → CTRL_PRIMARY via ha_promote_to_primary).
|
||||
* The mechanism is gone — promotion is now the heartbeat decision loop +
|
||||
* per-entry majority ack. Nothing left to verify; no shim (design
|
||||
* §"Transitional-State Deletions").
|
||||
*/
|
||||
static void test_promoted_ep_repl_ack_promotes_to_primary(void)
|
||||
{
|
||||
/* Use cluster_t so ha_promote_to_primary has a valid c->ha to promote */
|
||||
cluster_t c;
|
||||
memset(&c, 0, sizeof(c));
|
||||
ha_init(&c.ha, &g_cfg, 0);
|
||||
c.ha.standby_endpoints = calloc(1, sizeof(zarray_t));
|
||||
zarray_init(c.ha.standby_endpoints, 4, NULL);
|
||||
c.ha.n_peers = 4; /* 5-node: quorum=3, threshold=2 → need 2 peer ACKs */
|
||||
atomic_store_explicit(&c.ha.role, (int32_t)CTRL_PRIMARY_COMMITTING,
|
||||
memory_order_release);
|
||||
atomic_store_explicit(&c.ha.repl_ack_count, 0, memory_order_release);
|
||||
|
||||
ha_elect_ctx_t *ctx = alloc_elect_ctx(1);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(ctx, "alloc_elect_ctx must succeed");
|
||||
|
||||
/* First standby ep — wire cluster pointer so promotion can fire */
|
||||
endpoint_t ep0;
|
||||
memset(&ep0, 0, sizeof(ep0));
|
||||
vote_ep_ctx_build_for_test(&ep0, &c.ha, ctx, 0, 1, 0, 0, "test");
|
||||
vote_ep_ctx_set_cluster_for_test(&ep0, &c);
|
||||
|
||||
io_packet_t *pkt0 = repl_catchup_done_pkt_for_test();
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(pkt0, "repl_catchup_done_pkt_for_test must succeed");
|
||||
|
||||
int rc = peer_ctrl_msg_handler_for_test(&ep0, pkt0);
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc, "first ACK: handler must return 0");
|
||||
|
||||
int acks = atomic_load_explicit(&c.ha.repl_ack_count, memory_order_acquire);
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(1, acks,
|
||||
"Task 7.3: first replCatchUpDone must increment repl_ack_count to 1 (Req 5.2)");
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE((int)CTRL_PRIMARY_COMMITTING,
|
||||
(int)atomic_load_explicit(&c.ha.role, memory_order_acquire),
|
||||
"Task 7.3: role must stay COMMITTING after first ACK (quorum not reached)");
|
||||
|
||||
/* Second standby ep — same ctx, wire cluster pointer */
|
||||
endpoint_t ep1;
|
||||
memset(&ep1, 0, sizeof(ep1));
|
||||
vote_ep_ctx_build_for_test(&ep1, &c.ha, ctx, 0, 1, 0, 0, "test2");
|
||||
vote_ep_ctx_set_cluster_for_test(&ep1, &c);
|
||||
|
||||
io_packet_t *pkt1 = repl_catchup_done_pkt_for_test();
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(pkt1, "second repl_catchup_done_pkt_for_test must succeed");
|
||||
|
||||
rc = peer_ctrl_msg_handler_for_test(&ep1, pkt1);
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc, "second ACK: handler must return 0");
|
||||
|
||||
acks = atomic_load_explicit(&c.ha.repl_ack_count, memory_order_acquire);
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(2, acks,
|
||||
"Task 7.3: second replCatchUpDone must increment repl_ack_count to 2 (Req 5.2)");
|
||||
|
||||
int role = (int)atomic_load_explicit(&c.ha.role, memory_order_acquire);
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE((int)CTRL_PRIMARY, role,
|
||||
"Task 7.3: reaching quorum (2/2) must promote role to CTRL_PRIMARY (Req 5.2)");
|
||||
|
||||
free(ep0.data);
|
||||
free(ep1.data);
|
||||
free_elect_ctx(ctx);
|
||||
zarray_fini(c.ha.standby_endpoints);
|
||||
free(c.ha.standby_endpoints);
|
||||
c.ha.standby_endpoints = NULL;
|
||||
ha_fini(&c.ha);
|
||||
printf("test_promoted_ep_repl_ack_promotes_to_primary: PASS\n");
|
||||
}
|
||||
|
||||
/* ── Test suite entry point ─────────────────────────────────────────────── */
|
||||
|
||||
|
|
@ -595,7 +493,8 @@ void test_ha_connection_reuse(void)
|
|||
{
|
||||
/* Req 1.2 — dispatcher routes by role */
|
||||
RUN_TEST(test_dispatcher_electing_calls_vote_reply_hook);
|
||||
RUN_TEST(test_dispatcher_committing_handles_repl_ack);
|
||||
/* test_dispatcher_committing_handles_repl_ack deleted (ha-single-leader-role
|
||||
* 4.1: ack-counting promotion machinery removed, no shim) */
|
||||
RUN_TEST(test_dispatcher_standby_drops_packet);
|
||||
|
||||
/* Req 1.3 — winner promotes vote endpoints */
|
||||
|
|
@ -614,6 +513,6 @@ void test_ha_connection_reuse(void)
|
|||
/* Task 7.2 — Req 2.1, 2.4: follower path is fully passive */
|
||||
RUN_TEST(test_follower_passive_no_outbound_connect);
|
||||
|
||||
/* Task 7.3 — Req 5.2: replCatchUpDone on promoted ep promotes to PRIMARY */
|
||||
RUN_TEST(test_promoted_ep_repl_ack_promotes_to_primary);
|
||||
/* Task 7.3 (test_promoted_ep_repl_ack_promotes_to_primary) deleted
|
||||
* (ha-single-leader-role 4.1: ack-counting promotion machinery removed, no shim) */
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue