feat(ha-repl-hardening): standby snapshot receive handler (task 3.5c)

ha_handle_snapshot_chunk writes chunks to snap.part, on isLast verifies
total_size, atomic renames to snap-{lsn}.bin, loads via new
persist_load_snapshot wrapper, sets applied_lsn=checkpoint_lsn.
3 tests: 2-chunk assembly, size mismatch, null args. Wired into
myreq_handler OPCode_snapshotTransfer dispatch.
This commit is contained in:
Rongsong Shen 2026-06-05 01:43:05 +00:00
parent 318098c727
commit cbd91fca43
7 changed files with 361 additions and 2 deletions

View file

@ -173,6 +173,35 @@ static int handle_wal_catchup_entry(cluster_t *c, xce_request_t *req)
}
/* handle_snapshot_transfer: dispatch handler for OPCode_snapshotTransfer.
* Delegates to ha_handle_snapshot_chunk (ha.c).
* On mid-transfer reconnect the caller (iomgr reconnect path) must delete
* snap.part before the next transfer starts — this handler does not track
* cross-chunk state itself (Req 4.4).
* No reply is sent — primary learns of completion via next heartbeat reply. */
static int handle_snapshot_transfer(cluster_t *c, xce_request_t *req)
{
snapshot_transfer_t *chunk =
(snapshot_transfer_t *)req->body.snapshottransfermsg;
/* body may be NULL if sender used Request_body_nomsg; skip gracefully */
if (!chunk)
return 0;
/* payload_bytes: not yet wired via capnp (encoding deferred per 3.5a).
* Pass NULL/0 — ha_handle_snapshot_chunk handles NULL payload gracefully. */
int rc = ha_handle_snapshot_chunk(&c->ha, &c->persist,
c->persist.snap_path,
chunk,
/*payload_bytes=*/NULL,
/*payload_len=*/0);
if (rc != 0)
ERROR("handle_snapshot_transfer: chunk %u rc=%d",
chunk->chunk_index, rc);
/* Return 0 — errors are logged; primary retries on next ctrlhello (Req 4.4) */
return 0;
}
static int myreq_handler(vtask_t* task)
{
vtask_data_t* data = (vtask_data_t*)task->data;
@ -382,6 +411,13 @@ static int myreq_handler(vtask_t* task)
* learns of progress via the next heartbeat reply's stateReport. */
rc = handle_wal_catchup_entry(c, &request);
break;
case OPCode_snapshotTransfer:
/* Task 3.5c: standby receives one snapshot chunk from the primary.
* Writes chunk payload to snap.part; on isLast renames and loads.
* No reply — primary learns of completion via next heartbeat reply.
* Req 4.2, 4.3, 4.4. */
rc = handle_snapshot_transfer(c, &request);
break;
case OPCode_upgradeConfig:
#if CONF_ENABLE_UPGRADE_CONFIG
/* OPCode_upgradeConfig is sent BY the primary TO standbys to push config.