chore: baseline snapshot — prior-session work + ha-consistency-invariants spec family
Pre-existing uncommitted work: ha-repl-hardening payload data plane (7.x), heartbeat liveness fix (Req 1.3), connection-reuse ASAN fixes, doc cleanup. New: ha-consistency-invariants umbrella (requirements/design/research) and its four implementation sub-specs (specs generated via batch, cross-spec review applied), steering roadmap + product.md Raft+CP decision.
This commit is contained in:
parent
084244941b
commit
06135954ff
88 changed files with 25611 additions and 5285 deletions
|
|
@ -694,6 +694,17 @@ int handle_repl_config_push(cluster_t *c, endpoint_t *ep, xce_request_t *req)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Req 1.3 (ha-heartbeat-liveness): any msg from primary resets standby
|
||||
* liveness and steps down from CTRL_ELECTING (live primary discovered). */
|
||||
{
|
||||
ctrl_role_t role = (ctrl_role_t)atomic_load_explicit(
|
||||
&c->ha.role, memory_order_acquire);
|
||||
if (role == CTRL_STANDBY)
|
||||
ha_on_heartbeat_received(&c->ha);
|
||||
else if (role == CTRL_ELECTING)
|
||||
ha_discover_primary(&c->ha, c);
|
||||
}
|
||||
|
||||
repl_config_push_req_t *rcpr = req->body.replconfigpushrequestmsg;
|
||||
|
||||
if (!rcpr->raw_bytes || rcpr->raw_bytes_len == 0 || !rcpr->raw_sha256) {
|
||||
|
|
@ -1404,6 +1415,9 @@ int (*xce_wal_stream_request_hook)(endpoint_t *ep, xce_request_t *req) = NULL;
|
|||
#endif
|
||||
|
||||
/* send_wal_batch: enqueue a single OPCode_walCatchupEntry batch message.
|
||||
* Entry payloads (raw_payload/raw_payload_len) travel as capnp Data via
|
||||
* xce_wal_batch_req_raw (task 7.3) — the generated encode path cannot
|
||||
* carry raw bytes (xce_data_t limitation, same as replConfigPush).
|
||||
* Does not free any memory — caller manages entry lifetime.
|
||||
* Returns 0 on success. */
|
||||
static int send_wal_batch(endpoint_t *ep,
|
||||
|
|
@ -1412,24 +1426,24 @@ static int send_wal_batch(endpoint_t *ep,
|
|||
if (n_entries <= 0)
|
||||
return 0;
|
||||
|
||||
/* Build the batch container entry */
|
||||
wal_catchup_entry_t batch;
|
||||
memset(&batch, 0, sizeof(batch));
|
||||
batch.n_entries = n_entries;
|
||||
batch.entries = entries;
|
||||
|
||||
xce_request_t req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.opcode = OPCode_walCatchupEntry;
|
||||
req.body_which = Request_body_nomsg;
|
||||
req.body.walcatchupentrymsg = &batch;
|
||||
|
||||
#ifdef XCE_TESTING
|
||||
if (xce_wal_stream_request_hook != NULL)
|
||||
if (xce_wal_stream_request_hook != NULL) {
|
||||
/* Build the batch container the hook expects */
|
||||
wal_catchup_entry_t batch;
|
||||
memset(&batch, 0, sizeof(batch));
|
||||
batch.n_entries = n_entries;
|
||||
batch.entries = entries;
|
||||
|
||||
xce_request_t req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.opcode = OPCode_walCatchupEntry;
|
||||
req.body_which = Request_body_walcatchupentrymsg;
|
||||
req.body.walcatchupentrymsg = &batch;
|
||||
return xce_wal_stream_request_hook(ep, &req);
|
||||
}
|
||||
#endif
|
||||
if (ep)
|
||||
xce_request(ep, &req);
|
||||
xce_wal_batch_req_raw(ep, entries, n_entries);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1505,7 +1519,7 @@ int ha_stream_wal_to_standby(endpoint_t *ep, const char *wal_path,
|
|||
if (batch_n > 0) {
|
||||
send_wal_batch(ep, batch_entries, batch_n);
|
||||
for (int bi = 0; bi < batch_n; bi++) {
|
||||
free(batch_entries[bi]->payload);
|
||||
free(batch_entries[bi]->raw_payload);
|
||||
free(batch_entries[bi]);
|
||||
}
|
||||
batch_n = 0;
|
||||
|
|
@ -1525,10 +1539,12 @@ int ha_stream_wal_to_standby(endpoint_t *ep, const char *wal_path,
|
|||
if (pbuf) {
|
||||
ssize_t pr = pread(fd, pbuf, hdr.payload_len,
|
||||
(off_t)(off + sizeof(hdr)));
|
||||
if (pr == (ssize_t)hdr.payload_len)
|
||||
entry->payload = (xce_data_t *)pbuf;
|
||||
else
|
||||
if (pr == (ssize_t)hdr.payload_len) {
|
||||
entry->raw_payload = pbuf;
|
||||
entry->raw_payload_len = hdr.payload_len;
|
||||
} else {
|
||||
free(pbuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1541,7 +1557,7 @@ int ha_stream_wal_to_standby(endpoint_t *ep, const char *wal_path,
|
|||
batch_bytes >= WAL_STREAM_BATCH_BYTES) {
|
||||
send_wal_batch(ep, batch_entries, batch_n);
|
||||
for (int bi = 0; bi < batch_n; bi++) {
|
||||
free(batch_entries[bi]->payload);
|
||||
free(batch_entries[bi]->raw_payload);
|
||||
free(batch_entries[bi]);
|
||||
}
|
||||
batch_n = 0;
|
||||
|
|
@ -1557,7 +1573,7 @@ int ha_stream_wal_to_standby(endpoint_t *ep, const char *wal_path,
|
|||
if (batch_n > 0) {
|
||||
send_wal_batch(ep, batch_entries, batch_n);
|
||||
for (int bi = 0; bi < batch_n; bi++) {
|
||||
free(batch_entries[bi]->payload);
|
||||
free(batch_entries[bi]->raw_payload);
|
||||
free(batch_entries[bi]);
|
||||
}
|
||||
}
|
||||
|
|
@ -1610,17 +1626,14 @@ int ha_send_snapshot_to_standby(endpoint_t *ep, const char *snap_dir,
|
|||
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. */
|
||||
/* Chunk the snapshot file and enqueue each chunk with its file bytes.
|
||||
* Payload travels as capnp Data via xce_snapshot_chunk_req_raw (task 7.2)
|
||||
* — the generated encode path cannot carry raw bytes (xce_data_t
|
||||
* limitation, same as replConfigPush). */
|
||||
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;
|
||||
|
|
@ -1630,26 +1643,26 @@ int ha_send_snapshot_to_standby(endpoint_t *ep, const char *snap_dir,
|
|||
|
||||
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;
|
||||
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.raw_payload = buf; /* borrowed for hook/encode only */
|
||||
chunk.raw_payload_len = chunk_len;
|
||||
|
||||
#ifdef XCE_TESTING
|
||||
if (xce_snapshot_chunk_hook)
|
||||
if (xce_snapshot_chunk_hook) {
|
||||
xce_request_t req;
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.opcode = OPCode_snapshotTransfer;
|
||||
req.body_which = Request_body_snapshottransfermsg;
|
||||
req.body.snapshottransfermsg = &chunk;
|
||||
xce_snapshot_chunk_hook(ep, &req);
|
||||
else
|
||||
} else
|
||||
#endif
|
||||
if (ep)
|
||||
xce_request(ep, &req);
|
||||
xce_snapshot_chunk_req_raw(ep, &chunk, buf, chunk_len);
|
||||
|
||||
offset += chunk_len;
|
||||
chunk_index++;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue