feat(raft-ha-core-safety): establish snapshot (term,lsn) boundary on install (task 3.2)

Adds checkpointTerm (@6, append-only) to the SnapshotTransfer wire
message. The leader stamps it via persist_entry_term(p, checkpoint_lsn)
when sending a snapshot; the standby reconciles it into its own
per-LSN term index (persist_reconcile_checkpoint_term) right after
persist_load_snapshot succeeds, before applied_lsn advances. This
authoritatively overwrites any stale local term a divergent
pre-snapshot entry left at the checkpoint LSN, closing the gap task
3.1's RED test proved (Expected 7 Was 3 -> now passes). Transport
stays persist_t-free; the reconcile lives in persist.c per the
existing dependency direction. Decode is auto-generated via capnp
mapname; only the hand-rolled encode path (request.c/typemap.h, needed
for the raw snapshot payload) required a matching manual update.

test_ha_core_safety 25/0, test_config_repl 66/0, no ASAN reports.
This commit is contained in:
Rongsong Shen 2026-07-01 13:08:10 +00:00
parent 21858faba4
commit a53b9a7b6c
11 changed files with 132 additions and 23 deletions

View file

@ -2535,7 +2535,8 @@ int (*xce_snapshot_chunk_hook)(endpoint_t *ep, xce_request_t *req) = NULL;
#define SNAPSHOT_CHUNK_SIZE 65536
int ha_send_snapshot_to_standby(endpoint_t *ep, const char *snap_dir,
uint64_t checkpoint_lsn)
uint64_t checkpoint_lsn,
uint64_t checkpoint_term)
{
if (!snap_dir)
return -EINVAL;
@ -2591,6 +2592,12 @@ int ha_send_snapshot_to_standby(endpoint_t *ep, const char *snap_dir,
chunk.chunk_offset = offset;
chunk.total_size = total_size;
chunk.is_last = is_last;
/* raft-ha-core-safety task 3.2: stamp every chunk with the
* checkpoint's own term so the standby can reconcile its per-LSN
* term index at the boundary regardless of which chunk it last
* processes (is_last framing already exists per-chunk; cheap to
* repeat). */
chunk.checkpoint_term = checkpoint_term;
chunk.raw_payload = buf; /* borrowed for hook/encode only */
chunk.raw_payload_len = chunk_len;