feat(raft-sim-conformance): stale-rejoin scenario, bug-851 G5 term adoption via AE (task 5.5)
This commit is contained in:
parent
b1f289d2cb
commit
a2933c274d
4 changed files with 317 additions and 1 deletions
|
|
@ -8468,6 +8468,64 @@ static void test_sim_5_4_figure8(void)
|
|||
"figure-8 leader did not commit the prior-term + current-term entries");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* test_sim_5_5_stale_rejoin (raft-sim-conformance task 5.5; Req 4.5)
|
||||
*
|
||||
* The bug-851 G5 stale-rejoin: a term-lagging node is isolated while the connected
|
||||
* majority advances its TERM via a fresh election, so the surviving leader is at a
|
||||
* STRICTLY-HIGHER term than the isolated node. On HEAL, an AppendEntries/heartbeat from
|
||||
* the higher-term leader is driven to the stale node and DELIVERED through the bag — the
|
||||
* library's raft_on_append_entries_request UpdateTerm path adopts the leader's higher
|
||||
* term and steps the node down to FOLLOWER. The node does NOT re-elect at a reused term;
|
||||
* Election Safety holds throughout (no two leaders at one term).
|
||||
*
|
||||
* raft_sim_scenario_stale_rejoin returns 0 ONLY when (a) the stale node's term was
|
||||
* STRICTLY LOWER than the leader's BEFORE the rejoin AE (non-vacuous adoption),
|
||||
* (b) after delivering the leader's AE the stale node's term == leader's term and its
|
||||
* role is FOLLOWER (adopted via the LIBRARY AE path, not forged), and (c) no invariant
|
||||
* (notably Election Safety) ever fired. A return < 0 is a REAL safety violation the
|
||||
* scenario surfaced (the step already dumped the replayable trace to stderr) — NOT
|
||||
* something to mask. Several (n, seed) pairs are exercised so the property is not a
|
||||
* single-seed accident.
|
||||
*/
|
||||
static void test_sim_5_5_stale_rejoin(void)
|
||||
{
|
||||
/* (n, seed) pairs across 3- and 5-node clusters. The scenario inits the caller's
|
||||
* cluster internally from (n, seed). */
|
||||
struct { int n; uint64_t seed; } cases[] = {
|
||||
{ 3, 11ULL }, { 3, 0x4A33ULL }, { 3, 7ULL },
|
||||
{ 5, 23ULL }, { 5, 0xBEEFULL },
|
||||
};
|
||||
|
||||
for (size_t k = 0; k < sizeof(cases) / sizeof(cases[0]); k++) {
|
||||
raft_sim_cluster_t c;
|
||||
memset(&c, 0, sizeof(c));
|
||||
|
||||
int rc = raft_sim_scenario_stale_rejoin(&c, cases[k].n, cases[k].seed);
|
||||
|
||||
/* Zero invariant violations AND the stale-rejoin adoption property held
|
||||
* throughout (stale term strictly lower pre-rejoin; adopted == leader term and
|
||||
* FOLLOWER post-AE; Election Safety never fired). */
|
||||
TEST_ASSERT_TRUE_MESSAGE(rc == 0,
|
||||
"stale-rejoin scenario surfaced an invariant/adoption violation on the correct library");
|
||||
|
||||
/* Post-scenario: the cluster still satisfies all four invariants. */
|
||||
TEST_ASSERT_EQUAL_INT(RAFT_SIM_INV_NONE, raft_sim_check_all(&c));
|
||||
|
||||
/* Election Safety holds in the final converged state (no two leaders at one
|
||||
* term). Count leaders observably; exactly one must hold. */
|
||||
int leaders = 0;
|
||||
for (int i = 0; i < c.n; i++) {
|
||||
raft_snapshot_t snap;
|
||||
raft_snapshot(&c.nodes[i].state, &snap);
|
||||
if (snap.role == RAFT_LEADER)
|
||||
leaders++;
|
||||
}
|
||||
TEST_ASSERT_TRUE_MESSAGE(leaders <= 1,
|
||||
"post-stale-rejoin cluster reports more than one leader (Election Safety)");
|
||||
}
|
||||
}
|
||||
#endif /* XCE_TESTING */
|
||||
|
||||
/* ---------- test_raft entry point ---------- */
|
||||
|
|
@ -8682,6 +8740,7 @@ void test_raft(void)
|
|||
RUN_TEST(test_sim_5_2_restart_churn);
|
||||
RUN_TEST(test_sim_5_3_drop_dup_reorder);
|
||||
RUN_TEST(test_sim_5_4_figure8);
|
||||
RUN_TEST(test_sim_5_5_stale_rejoin);
|
||||
|
||||
/* Group A: basic election correctness (task 5.1) */
|
||||
RUN_TEST(test_proto_A1_single_node_self_elects);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue