fix(ha-term-safety): bug-828 partial — revive dead safe-commit-from-votes + advertise real durable tip

Clean failover was truncating committed jobs because the post-election
safe-commit-from-votes computation was dead: vote_reply_dispatch decoded each
granting voter's advertised peerLastLogLsn but discarded it, so ha_compute_safe
_committed_lsn saw only the self-vote (the 'from 1 granted voters' fingerprint)
and truncated to self_lsn.

Part 1: vote_reply_dispatch now records the granting voter's (lsn,tag,grant) into
ctx->votes[] via a record-only ha_record_vote_reply seam (no double-promotion;
raft_on_vote_reply stays the sole production promotion driver) — so safe-commit
reflects the true voted majority.
Part 2: the candidate/voter advertise the real DURABLE (fsync'd) tip; persist_wal
_write_replicated advances durable_lsn after its fail-loud inline fdatasync.

Reviewer-verified SAFE (advertises only durable positions; safe-commit bounded by
the durable majority, preserving more committed entries, never marking a
non-majority-durable LSN safe). 177 unit tests; new test_ha_vote_gate + election
tests green.

PARTIAL: does NOT yet make the live clean-failover pass. A deeper slice of
bug-828/795 remains — a catch-up'd standby still under-advertises its durable
position in the live run (the documented architectural catch-up/durability gap),
so a job-less node can still win and truncate. Tracked for a dedicated effort.
This commit is contained in:
Rongsong Shen 2026-06-23 03:13:02 +00:00
parent 14c41cb298
commit 91b9a445f0
6 changed files with 313 additions and 20 deletions

View file

@ -303,6 +303,14 @@ static void test_repl_write_persists_and_recovers(void)
/* current_lsn advanced to lsn+1 */
TEST_ASSERT_EQUAL_UINT64(2, p->current_lsn);
/* bug-828 secondary: the entry was fdatasync'd inline before returning, so
* durable_lsn must reflect the synced tip (lsn 1) IMMEDIATELY — not lag
* behind waiting for the cadence flusher. A standby advertises this as its
* durable floor (RequestVoteReply.peerLastLogLsn) during a vote; if it lags,
* a clean failover's safe-commit-from-votes under-counts. RED before fix. */
TEST_ASSERT_EQUAL_UINT64_MESSAGE(1ULL, persist_durable_lsn(p),
"durable_lsn must advance to the synced tip after persist_wal_write_replicated");
/* A .wal segment must now exist on disk. */
TEST_ASSERT_TRUE(count_replwr_wal_files(tmpdir) >= 1);
TEST_ASSERT_TRUE(total_replwr_wal_bytes(tmpdir) > 0);