forked from lei.chen/myxce
feat(raft-sim-conformance): Figure-8 commit-safety scenario + restore dropped 5.3 impl (task 5.4)
Adds raft_sim_scenario_figure8: a previous-term entry on a quorum under a
new-term leader is NOT committed until a current-term entry commits (library-
gated by raft_advance_commit; verified by a mutation test). Also restores the
raft_sim_scenario_drop_dup_reorder implementation that commit e445404 (task 5.3)
silently dropped from raft_sim.c (a stray git checkout reverted the uncommitted
impl), which had left the lib build broken with an undefined reference.
Build green: test_sim_5_3_drop_dup_reorder + test_sim_5_4_figure8 both PASS.
This commit is contained in:
parent
e445404439
commit
b1f289d2cb
4 changed files with 521 additions and 1 deletions
|
|
@ -8407,6 +8407,67 @@ static void test_sim_5_3_drop_dup_reorder(void)
|
|||
TEST_ASSERT_EQUAL_INT(RAFT_SIM_INV_NONE, raft_sim_check_all(&c));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* test_sim_5_4_figure8 (raft-sim-conformance task 5.4; Req 4.4)
|
||||
*
|
||||
* Canonical Raft Figure-8 commit-safety scenario driven through the conformance
|
||||
* harness. raft_sim_scenario_figure8 constructs the marquee case: a PREVIOUS-term
|
||||
* entry E1 replicated to a QUORUM of logs (uncommitted), then a LEADER CHANGE to a
|
||||
* higher term T2 (the new leader necessarily holds E1). Under the new leader,
|
||||
* raft_advance_commit must NOT commit E1 by replica count alone (the current-term
|
||||
* Figure-8 rule, raft.c G4); only after a CURRENT-term entry E2 commits does E1
|
||||
* become committed transitively. All four invariants are asserted THROUGHOUT.
|
||||
*
|
||||
* The scenario returns 0 ONLY when both commit-index assertions hold (E1 not covered
|
||||
* pre-current-term-commit; covered after) AND no invariant ever fires. 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_4_figure8(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, 8ULL }, { 3, 0x4A33ULL }, { 3, 6ULL },
|
||||
{ 5, 19ULL }, { 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_figure8(&c, cases[k].n, cases[k].seed);
|
||||
|
||||
/* Zero invariant violations AND the Figure-8 safety property held throughout
|
||||
* (E1 uncommitted under the new term until a current-term entry committed,
|
||||
* then committed transitively). */
|
||||
TEST_ASSERT_TRUE_MESSAGE(rc == 0,
|
||||
"figure-8 scenario surfaced an invariant/safety 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));
|
||||
|
||||
/* Observable, non-vacuous outcome: a leader exists, it committed a non-trivial
|
||||
* prefix (the current-term commit released E1 transitively), and that committed
|
||||
* prefix is internally consistent. A scenario that never committed anything
|
||||
* would make the safety property vacuous, so assert real commit progress. */
|
||||
int leader_idx = -1;
|
||||
for (int i = 0; i < c.n; i++) {
|
||||
raft_snapshot_t snap;
|
||||
raft_snapshot(&c.nodes[i].state, &snap);
|
||||
if (snap.role == RAFT_LEADER) {
|
||||
leader_idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
TEST_ASSERT_TRUE_MESSAGE(leader_idx >= 0,
|
||||
"figure-8 scenario ended without a leader");
|
||||
TEST_ASSERT_TRUE_MESSAGE(c.nodes[leader_idx].commit_index >= 2,
|
||||
"figure-8 leader did not commit the prior-term + current-term entries");
|
||||
}
|
||||
}
|
||||
#endif /* XCE_TESTING */
|
||||
|
||||
/* ---------- test_raft entry point ---------- */
|
||||
|
|
@ -8620,6 +8681,7 @@ void test_raft(void)
|
|||
RUN_TEST(test_sim_5_1_partition_heal);
|
||||
RUN_TEST(test_sim_5_2_restart_churn);
|
||||
RUN_TEST(test_sim_5_3_drop_dup_reorder);
|
||||
RUN_TEST(test_sim_5_4_figure8);
|
||||
|
||||
/* 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