forked from lei.chen/myxce
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:
parent
318098c727
commit
cbd91fca43
7 changed files with 361 additions and 2 deletions
|
|
@ -918,6 +918,95 @@ int ha_apply_wal_catchup_entry(ha_state_t *ha, persist_t *p,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* ha_handle_snapshot_chunk — standby snapshot receive handler (Task 3.5c).
|
||||
*
|
||||
* Each call appends payload_bytes to snap_dir/snap.part.
|
||||
* When chunk->is_last is non-zero:
|
||||
* 1. Verify total bytes written matches chunk->total_size.
|
||||
* 2. Atomically rename snap.part → snap-{snapshot_lsn:016x}.bin.
|
||||
* 3. Load the snapshot via persist_load_snapshot().
|
||||
* 4. Set ha->applied_lsn = p->checkpoint_lsn (in-memory).
|
||||
*
|
||||
* On mid-transfer reconnect callers discard snap.part externally (Req 4.4).
|
||||
* Returns 0 on success, negative errno on error.
|
||||
* Requirements: 4.2, 4.3, 4.4 (ha-repl-hardening spec)
|
||||
*/
|
||||
int ha_handle_snapshot_chunk(ha_state_t *ha, persist_t *p,
|
||||
const char *snap_dir,
|
||||
snapshot_transfer_t *chunk,
|
||||
const uint8_t *payload_bytes,
|
||||
size_t payload_len)
|
||||
{
|
||||
if (!ha || !p || !snap_dir || !chunk)
|
||||
return -EINVAL;
|
||||
|
||||
char part_path[4096 + 16];
|
||||
snprintf(part_path, sizeof(part_path), "%s/snap.part", snap_dir);
|
||||
|
||||
/* Append chunk payload to snap.part */
|
||||
if (payload_bytes && payload_len > 0) {
|
||||
FILE *f = fopen(part_path, "ab");
|
||||
if (!f) {
|
||||
int err = errno;
|
||||
ERROR("ha_handle_snapshot_chunk: cannot open snap.part: %s",
|
||||
strerror(err));
|
||||
return -err;
|
||||
}
|
||||
size_t written = fwrite(payload_bytes, 1, payload_len, f);
|
||||
fclose(f);
|
||||
if (written != payload_len) {
|
||||
ERROR("ha_handle_snapshot_chunk: short write to snap.part "
|
||||
"(%zu of %zu bytes)", written, payload_len);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
if (!chunk->is_last)
|
||||
return 0;
|
||||
|
||||
/* isLast=true: verify size, rename, load, set applied_lsn */
|
||||
|
||||
/* Verify total file size equals chunk->total_size */
|
||||
struct stat st;
|
||||
if (stat(part_path, &st) != 0) {
|
||||
int err = errno;
|
||||
ERROR("ha_handle_snapshot_chunk: stat(snap.part) failed: %s",
|
||||
strerror(err));
|
||||
return -err;
|
||||
}
|
||||
if ((uint64_t)st.st_size != chunk->total_size) {
|
||||
ERROR("ha_handle_snapshot_chunk: size mismatch — "
|
||||
"written=%" PRIu64 " expected=%" PRIu64,
|
||||
(uint64_t)st.st_size, chunk->total_size);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Atomic rename snap.part → snap-{lsn:016x}.bin */
|
||||
char final_path[4096 + 32];
|
||||
snprintf(final_path, sizeof(final_path),
|
||||
"%s/snap-%016" PRIx64 ".bin", snap_dir, chunk->snapshot_lsn);
|
||||
if (rename(part_path, final_path) != 0) {
|
||||
int err = errno;
|
||||
ERROR("ha_handle_snapshot_chunk: rename snap.part → %s failed: %s",
|
||||
final_path, strerror(err));
|
||||
return -err;
|
||||
}
|
||||
|
||||
/* Load the snapshot — sets p->checkpoint_lsn and p->current_lsn */
|
||||
int rc = persist_load_snapshot(p, final_path, chunk->snapshot_lsn);
|
||||
if (rc != 0) {
|
||||
ERROR("ha_handle_snapshot_chunk: persist_load_snapshot failed rc=%d", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Advance applied_lsn to checkpoint_lsn (in-memory only — Req 4.2) */
|
||||
atomic_store_explicit(&ha->applied_lsn, p->checkpoint_lsn,
|
||||
memory_order_release);
|
||||
INFO("HA: snapshot loaded lsn=%" PRIu64 " applied_lsn set",
|
||||
p->checkpoint_lsn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ha_nop_reply_state_report — populate *sr from ha state and wire into reply.
|
||||
* Reads ha->applied_lsn (atomic) and myconf->config_version_tag, stores them
|
||||
* in *sr, then sets reply_out->data_which = stateReportMsg and
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue