fix bug-1060 bug-1067
This commit is contained in:
parent
6067ba6c94
commit
61e1391cd0
28 changed files with 7021 additions and 2926 deletions
|
|
@ -1001,6 +1001,60 @@ static void test_safe_commit_reflects_granting_voter_lsn(void)
|
|||
printf("test_safe_commit_reflects_granting_voter_lsn: PASS\n");
|
||||
}
|
||||
|
||||
/* ── bug-1060: a new leader must NEVER truncate its own committed entries ─────
|
||||
*
|
||||
* Leader Completeness (Raft §5.4): a server that wins an election must already
|
||||
* hold every entry committed by a prior leader, and it must never truncate its
|
||||
* own log to "catch down" to a slower majority.
|
||||
*
|
||||
* Production root cause (bug-1060): raft_cb_become_leader truncated its WAL down
|
||||
* to ha_compute_safe_committed_lsn()'s majority-threshold element. When the new
|
||||
* leader durably holds entries ABOVE that element (it acked them as a standby
|
||||
* under the prior primary — so they were committed), truncating to the majority
|
||||
* element physically destroyed those committed entries and rewound commitIndex.
|
||||
* e.g. granted voters {self=47, peer=44} → median safe_lsn=44, but self holds
|
||||
* committed entries 45..47 → truncate(44) deletes 45..47. The leader then
|
||||
* wrote its leadership NOP at LSN 45 (term+1) while the diverged peers still
|
||||
* held the old job at LSN 45 → permanent prevote deadlock + committed-job loss.
|
||||
*
|
||||
* Invariant under test: the promotion truncation floor is clamped to the new
|
||||
* leader's own durable tip, so persist_truncate_wal_above is a no-op on the
|
||||
* leader's durable log. safe_lsn only governs removal of entries STRICTLY ABOVE
|
||||
* the leader's durable tip (non-durable, divergent tail).
|
||||
*
|
||||
* RED before the fix: ha_promotion_truncate_floor does not exist (undefined
|
||||
* reference).
|
||||
*/
|
||||
static void test_promotion_never_truncates_own_committed_entries(void)
|
||||
{
|
||||
/* The bug-1060 case: majority element (44) is BELOW the leader's own durable
|
||||
* tip (47). The truncation floor must clamp UP to the durable tip so the
|
||||
* leader's committed entries 45..47 are never cut. */
|
||||
TEST_ASSERT_EQUAL_UINT64_MESSAGE(
|
||||
47ULL, ha_promotion_truncate_floor(/*safe_lsn=*/44ULL, /*durable_tip=*/47ULL),
|
||||
"truncation floor must never fall below the leader's own durable tip "
|
||||
"(Leader Completeness: a new leader never truncates its own committed log)");
|
||||
|
||||
/* Equal: floor stays at the durable tip (no truncation). */
|
||||
TEST_ASSERT_EQUAL_UINT64_MESSAGE(
|
||||
47ULL, ha_promotion_truncate_floor(47ULL, 47ULL),
|
||||
"equal safe_lsn and durable tip → floor at the tip (no-op truncation)");
|
||||
|
||||
/* safe_lsn ABOVE the durable tip: only a divergent tail strictly above the
|
||||
* leader's durable log can be removed — safe_lsn governs there. */
|
||||
TEST_ASSERT_EQUAL_UINT64_MESSAGE(
|
||||
50ULL, ha_promotion_truncate_floor(50ULL, 47ULL),
|
||||
"when safe_lsn exceeds the durable tip, the floor follows safe_lsn "
|
||||
"(removes only the non-durable divergent tail above the leader's log)");
|
||||
|
||||
/* Single-node / fresh boot: durable tip 0 → floor follows safe_lsn. */
|
||||
TEST_ASSERT_EQUAL_UINT64_MESSAGE(
|
||||
0ULL, ha_promotion_truncate_floor(0ULL, 0ULL),
|
||||
"empty log: floor 0 (nothing to truncate)");
|
||||
|
||||
printf("test_promotion_never_truncates_own_committed_entries: PASS\n");
|
||||
}
|
||||
|
||||
/* ── Test suite entry point ───────────────────────────────────────────────── */
|
||||
|
||||
void test_ha_election(void)
|
||||
|
|
@ -1059,4 +1113,7 @@ void test_ha_election(void)
|
|||
|
||||
/* bug-828 — safe-commit-from-votes must reflect the granting voter LSN */
|
||||
RUN_TEST(test_safe_commit_reflects_granting_voter_lsn);
|
||||
|
||||
/* bug-1060 — a new leader must never truncate its own committed entries */
|
||||
RUN_TEST(test_promotion_never_truncates_own_committed_entries);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue