feat(ha-term-safety): retire term-0 repl_broadcast live send; carry leader term on replication (task 3.4)

Closes bug-990: the live replicator repl_broadcast wire-encoded term=0
(entry.term was never set), so standbys persisted hdr.term=0 and took the
legacy direct-apply path instead of the library (term,lsn) intake. Now the
live broadcast and catch-up streamer carry the leader's real term + AE prev
tuple; ha_apply_wal_catchup_entry routes every term-carrying entry through
raft_on_append_entries_request -> on_persist_entry (leader term stamped),
retiring the term-0 direct-apply. Standbys now persist the leader's real term
and reconcile by (term,lsn); the live commit chain is end-to-end.

848/849/850 + persist-before-ACK + commit-barrier + bug-960 preserved.
Proven at integration level; the definitive live STRICT_TERM failover run is
env-blocked here (bug-924 overlayfs) and recommended on a real fs.
This commit is contained in:
Rongsong Shen 2026-06-22 15:45:14 +00:00
parent 87242aa8e9
commit efe621c124
11 changed files with 307 additions and 45 deletions

View file

@ -237,7 +237,8 @@ void repl_on_ack(replicator_t *r, repl_ep_t ep, uint64_t acked_lsn)
* ============================================================ */
int repl_broadcast(replicator_t *r, const wal_entry_hdr_t *hdr,
const void *payload, uint32_t payload_len)
const void *payload, uint32_t payload_len,
uint64_t prev_log_term)
{
/* WAL_CHECKPOINT entries must never be replicated */
assert(hdr != NULL);
@ -276,13 +277,25 @@ int repl_broadcast(replicator_t *r, const wal_entry_hdr_t *hdr,
continue;
}
/* Build a single-entry batch and enqueue it for this standby. */
/* Build a single-entry AppendEntries and enqueue it for this standby.
*
* ha-term-safety task 3.4 (Req 1.3/1.4): carry the leader's per-entry
* term + the AppendEntries prev tuple so the standby intake
* (ha_apply_wal_catchup_entry, entry->term != 0) routes this through
* raft_on_append_entries_request — the library does UpdateTerm/logOk/
* (term,lsn) reconcile and on_persist_entry stamps the LEADER's term.
* The term-0 legacy direct-apply path is retired for live entries.
* prev_log_index == hdr->lsn - 1 (0 for lsn 1); prev_log_term is the
* term recorded for that LSN, supplied by the caller (term index). */
wal_catchup_entry_t entry;
memset(&entry, 0, sizeof(entry));
entry.lsn = hdr->lsn;
entry.entry_type = (uint8_t)hdr->type;
entry.raw_payload = (uint8_t *)(uintptr_t)payload;
entry.raw_payload_len = payload_len;
entry.term = hdr->term;
entry.prev_log_index = (hdr->lsn > 0) ? hdr->lsn - 1 : 0;
entry.prev_log_term = prev_log_term;
wal_catchup_entry_t *ep_arr[1] = { &entry };