feat(ha-term-safety): on_get_entry_term + on_truncate_from callback bodies (task 2.2)

on_get_entry_term → persist_entry_term (recorded term, 0=absent, legacy-0→1; caveat
documented per 1.3 review). on_truncate_from → persist_truncate_wal_above(p, index-1)
(delete LSN>=index), returns rc for library/leader retry; non-promotable fence intact;
replaces the committed-boundary heuristic on the library-driven path. New
truncate_term_index_locked keeps the in-memory per-LSN term index consistent on both
truncate paths (under lock, cutoff-aligned) → no stale-term read after truncation.
3 tests, reviewer APPROVED, per-group 0 failures.
This commit is contained in:
Rongsong Shen 2026-06-22 05:29:06 +00:00
parent 8f3d3ce8f4
commit ed2443533f
5 changed files with 210 additions and 5 deletions

View file

@ -440,6 +440,21 @@ static void record_entry_term(persist_t* p, uint64_t lsn, uint64_t term)
p->term_index_count = lsn;
}
/* Shrink the in-memory per-LSN term index so that every LSN > `keep_lsn` reads
* back as absent (ha-term-safety task 2.2). Called from the truncate path after
* the on-disk entries above keep_lsn are removed, keeping the term index
* consistent with the WAL: a later persist_entry_term on a truncated LSN must
* return 0. The backing allocation is left as-is (only the live count moves);
* the zeroed-on-grow contract means a later re-append at those LSNs overwrites
* cleanly. Caller must hold p->lock. */
static void truncate_term_index_locked(persist_t* p, uint64_t keep_lsn)
{
if (!p)
return;
if (keep_lsn < p->term_index_count)
p->term_index_count = keep_lsn;
}
void persist_set_term(persist_t* p, uint64_t term)
{
if (!p)
@ -2659,6 +2674,8 @@ int persist_truncate_wal_above(persist_t* p, uint64_t cutoff_lsn)
atomic_store_explicit(&p->committed_lsn, cutoff_lsn,
memory_order_release);
clamp_durable_lsn_locked(p, cutoff_lsn);
/* Keep the per-LSN term index consistent: drop terms for removed LSNs. */
truncate_term_index_locked(p, cutoff_lsn);
pthread_mutex_unlock(&p->lock);
return 0;
}
@ -2738,6 +2755,8 @@ int persist_truncate_wal_above(persist_t* p, uint64_t cutoff_lsn)
p->last_crc = new_last_crc;
atomic_store_explicit(&p->committed_lsn, cutoff_lsn, memory_order_release);
clamp_durable_lsn_locked(p, cutoff_lsn);
/* Keep the per-LSN term index consistent: drop terms for removed LSNs. */
truncate_term_index_locked(p, cutoff_lsn);
pthread_mutex_unlock(&p->lock);
return 0;