feat(ha-repl-hardening): primary snapshot chunk sender (task 3.5a)
ha_send_snapshot_to_standby reads snapshot file, splits into 64KB chunks, sends each via OPCode_snapshotTransfer with correct metadata (snapshotLsn, chunkIndex, chunkOffset, totalSize, isLast). Payload encoding deferred. Hook seam for unit testing. 2 tests: 128KB/2-chunks, small-file/1-chunk. Tasks 3.5b-3.5d remain.
This commit is contained in:
parent
6b69a47c50
commit
834ab4d5e7
5 changed files with 241 additions and 11 deletions
|
|
@ -1730,3 +1730,86 @@ int ha_stream_wal_to_standby(endpoint_t *ep, const char *wal_path,
|
|||
"] to standby", standby_lsn, committed_lsn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ── Snapshot chunk transfer ───────────────────────────────────────────── */
|
||||
|
||||
#ifdef XCE_TESTING
|
||||
int (*xce_snapshot_chunk_hook)(endpoint_t *ep, xce_request_t *req) = NULL;
|
||||
#endif
|
||||
|
||||
#define SNAPSHOT_CHUNK_SIZE 65536
|
||||
|
||||
int ha_send_snapshot_to_standby(endpoint_t *ep, const char *snap_dir,
|
||||
uint64_t checkpoint_lsn, uint64_t total_size)
|
||||
{
|
||||
if (!snap_dir)
|
||||
return -EINVAL;
|
||||
|
||||
/* Find the snapshot file by naming convention */
|
||||
char snap_path[4096];
|
||||
snprintf(snap_path, sizeof(snap_path),
|
||||
"%s/snap-%016" PRIx64 ".capnp", snap_dir, checkpoint_lsn);
|
||||
|
||||
FILE *f = fopen(snap_path, "rb");
|
||||
if (!f)
|
||||
return -errno;
|
||||
|
||||
uint8_t *buf = malloc(SNAPSHOT_CHUNK_SIZE);
|
||||
if (!buf) {
|
||||
fclose(f);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
int rc = 0;
|
||||
uint64_t offset = 0;
|
||||
uint32_t chunk_index = 0;
|
||||
|
||||
/* Chunk the snapshot file and enqueue each chunk via xce_request().
|
||||
* Payload encoding uses NULL for now — the user-defined Data type in capnp
|
||||
* (xce_data_t tagged union) requires a proper table dispatch for non-null
|
||||
* payloads. This is a known limitation shared with wal_catchup_entry_t
|
||||
* payload; both will be resolved when the capnp schema is updated to use
|
||||
* the built-in capnp Data (List(UInt8)) type. */
|
||||
while (offset < total_size) {
|
||||
size_t chunk_len = (total_size - offset > SNAPSHOT_CHUNK_SIZE)
|
||||
? SNAPSHOT_CHUNK_SIZE
|
||||
: (size_t)(total_size - offset);
|
||||
/* Consume the file bytes to advance offset — data not used yet */
|
||||
if (fread(buf, 1, chunk_len, f) != chunk_len) {
|
||||
rc = -EIO;
|
||||
break;
|
||||
}
|
||||
|
||||
int is_last = (offset + chunk_len >= total_size);
|
||||
|
||||
snapshot_transfer_t chunk;
|
||||
memset(&chunk, 0, sizeof(chunk));
|
||||
chunk.snapshot_lsn = checkpoint_lsn;
|
||||
chunk.chunk_index = chunk_index;
|
||||
chunk.chunk_offset = offset;
|
||||
chunk.total_size = total_size;
|
||||
chunk.is_last = is_last;
|
||||
chunk.payload = NULL; /* encoding deferred */
|
||||
|
||||
xce_request_t req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.opcode = OPCode_snapshotTransfer;
|
||||
req.body_which = Request_body_nomsg;
|
||||
req.body.snapshottransfermsg = &chunk;
|
||||
|
||||
#ifdef XCE_TESTING
|
||||
if (xce_snapshot_chunk_hook)
|
||||
xce_snapshot_chunk_hook(ep, &req);
|
||||
else
|
||||
#endif
|
||||
if (ep)
|
||||
xce_request(ep, &req);
|
||||
|
||||
offset += chunk_len;
|
||||
chunk_index++;
|
||||
}
|
||||
|
||||
free(buf);
|
||||
fclose(f);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue