feat(ha-commit-durability): publish durable floor from recovered WAL tip (task 3.4)
This commit is contained in:
parent
e3fc42c11e
commit
c1c35ab1b3
4 changed files with 271 additions and 1 deletions
|
|
@ -804,6 +804,16 @@ uint64_t persist_durable_lsn(persist_t* p)
|
|||
return atomic_load_explicit(&p->durable_lsn, memory_order_acquire);
|
||||
}
|
||||
|
||||
uint64_t persist_durable_floor(persist_t* p)
|
||||
{
|
||||
/* The durable freshness floor (durable last-log index) IS the durable
|
||||
* watermark: persist_recover re-derives it from the recovered WAL tip
|
||||
* (ha-commit-durability task 3.4), and the flusher advances it on every
|
||||
* successful fdatasync — so a thin wrapper avoids a redundant field. NULL
|
||||
* guard keeps the accessor safe for the election consumers (task 4.2). */
|
||||
return p ? atomic_load_explicit(&p->durable_lsn, memory_order_acquire) : 0;
|
||||
}
|
||||
|
||||
void persist_set_durable_cb(persist_t* p, void (*fn)(void* ctx), void* ctx)
|
||||
{
|
||||
if (!p)
|
||||
|
|
@ -1626,6 +1636,31 @@ int persist_recover(persist_t* p)
|
|||
"corrupt (CRC break / mismatch / short read); member marked "
|
||||
"recovery-truncated (non-promotable until caught up)");
|
||||
|
||||
/* --- Step 3b: re-derive the durable floor from the recovered WAL tip
|
||||
* (ha-commit-durability task 3.4, Req 2.3) ---
|
||||
*
|
||||
* Publish the member's durable floor (the durable last-log index consumed
|
||||
* by the Raft freshness gate, task 4.2) as the highest INTACT replayed LSN.
|
||||
* replay_wal advanced current_lsn to one past the last intact entry — incl.
|
||||
* stopping at a corruption boundary on TRUNCATED — so current_lsn - 1 is
|
||||
* exactly that intact WAL tip, and a snapshot-sentinel load leaves it at the
|
||||
* sentinel/replayed tip. We store it into durable_lsn because the recovered
|
||||
* on-disk entries are GENUINELY durable (each was CRC-validated and read
|
||||
* from stable storage by replay_wal), so the freshness advertisement is now
|
||||
* backed by durable state rather than a volatile in-memory position.
|
||||
*
|
||||
* Crux (Req 2.3 — a truncated member cannot over-advertise): this is an
|
||||
* ASSIGN to the recovered tip, NOT a max() with whatever durable_lsn held.
|
||||
* A fresh recover starts from durable_lsn=0 (persist_init), so on a
|
||||
* truncating restart the floor lands at the LOWER intact tip — never a
|
||||
* stale higher in-memory value and never the commit position
|
||||
* (committed_lsn). The recovered tip is, by construction, <= current_lsn-1,
|
||||
* preserving the durable_lsn <= current_lsn-1 invariant. The post-election
|
||||
* safe-commit point continues to come from voter replies, not this floor
|
||||
* (CLAUDE.md). */
|
||||
uint64_t recovered_tip = (p->current_lsn > 0) ? p->current_lsn - 1 : 0;
|
||||
atomic_store_explicit(&p->durable_lsn, recovered_tip, memory_order_release);
|
||||
|
||||
/* --- Step 4: retention pass (TTL check) — no-op in P0 (no entities) --- */
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue