feat(ha-entity-snapshots): track in-flight snapshot transfer LSN per peer (task 4.1)

Adds snapshot_inflight_lsn[MAX_HA_CANDIDATES] to ha_state_t, alongside the
existing snapshot_inflight[] flag it mirrors exactly: set to the
checkpoint's LSN at ha_snapshot_send_dispatch before a transfer's first
byte goes out, cleared to 0 at every completion/failure path
(snapshot_lane_fn's normal completion, and all 3 early-return failure
sites: malloc failure, slowlane dedup-refuse, slowlane submit failure).

This is the foundation task 4.3 will consult from rotate_snapshots to
avoid deleting a checkpoint file a peer is still mid-transfer reading
from -- the one genuinely new retention guard this feature needs (the
existing write_snapshot-before-trim_wal_segments sequencing already
satisfies the WAL-trim side of Requirement 3).

Proven via a new gated unit test (test_ha_liveness.c) that observes the
tracked LSN mid-flight while the send body is parked at a test gate, not
just before/after -- confirms the lifecycle ordering, not just the
endpoints.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JDYFbrgyDHbvNYcxApKhrX
This commit is contained in:
Rongsong Shen 2026-07-01 07:26:44 +00:00
parent eac7bb8ad8
commit 8d1ee8b761
4 changed files with 108 additions and 3 deletions

View file

@ -300,6 +300,25 @@ typedef struct {
* set, the heartbeat decision loop /
* promotion enqueues no duplicate config
* push. 0 at init (ha_init memset). */
/* ── New field added by ha-entity-snapshots spec (Task 4.1) ──────── */
_Atomic uint64_t snapshot_inflight_lsn[MAX_HA_CANDIDATES]; /* Per-peer
* checkpoint LSN referenced by an
* in-flight snapshot transfer (Req
* 3.1-3.3): set in
* ha_snapshot_send_dispatch at the
* SAME point snapshot_inflight[peer_idx]
* is set to 1 (before dispatch),
* cleared at the same points the flag
* is cleared to 0 (on completion or
* CAS-release failure) — identical
* lifecycle, one more field. Consulted
* by rotate_snapshots (task 4.3) to
* skip deleting a checkpoint file a
* peer transfer is still reading from.
* Explicitly zeroed on every clear so
* a stale value can never be mistaken
* for a live in-flight LSN. 0 at
* init (ha_init memset). */
} ha_state_t;
/* ── Public API ────────────────────────────────────────────── */