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
|
|
@ -1,5 +1,4 @@
|
|||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
|
|
|||
|
|
@ -173,6 +173,7 @@ libxce_test_src = ['testsuite/test.c',
|
|||
'testsuite/test_libxce_retry.c',
|
||||
'testsuite/test_netio_on_connected.c',
|
||||
'testsuite/test_raft.c',
|
||||
'testsuite/test_wal_wire.c',
|
||||
'raft/raft_sim.c',
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include <netdb.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
|
@ -949,6 +950,11 @@ SSL_CTX* create_ssl_context(char* cert_file, char* key_file)
|
|||
|
||||
int iomgr_init(iomgr_t* iomgr)
|
||||
{
|
||||
/* Ignore SIGPIPE — writing to a closed peer socket returns EPIPE
|
||||
* instead of killing the daemon. All daemons call iomgr_init before
|
||||
* any socket I/O, so this protects controller, worker, and scheduler. */
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
mylock_init(&(iomgr->lock));
|
||||
iomgr->epoll_fd = epoll_create1(0);
|
||||
if (iomgr->epoll_fd < 0) {
|
||||
|
|
|
|||
|
|
@ -56,6 +56,35 @@ int xce_encode(xce_request_t* req, io_packet_t* pkt)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* extract_data_bytes: copy a capnp Data (List(UInt8)) field into a fresh
|
||||
* malloc'd buffer. Returns NULL (*out_len = 0) for absent/empty fields or
|
||||
* allocation failure. Used by xce_decode() to pre-extract raw payload
|
||||
* bytes before capn_free() destroys the segment. */
|
||||
static uint8_t *extract_data_bytes(Data_ptr dp, size_t *out_len)
|
||||
{
|
||||
*out_len = 0;
|
||||
|
||||
capn_list8 l;
|
||||
l.p = dp.p;
|
||||
if (l.p.type == CAPN_NULL)
|
||||
return NULL;
|
||||
|
||||
int len = capn_len(l);
|
||||
if (len <= 0)
|
||||
return NULL;
|
||||
|
||||
uint8_t *buf = (uint8_t *)malloc((size_t)len);
|
||||
if (!buf)
|
||||
return NULL;
|
||||
if (capn_getv8(l, 0, buf, len) != len) {
|
||||
free(buf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*out_len = (size_t)len;
|
||||
return buf;
|
||||
}
|
||||
|
||||
int xce_decode(io_packet_t* pkt, xce_request_t* req)
|
||||
{
|
||||
struct capn c;
|
||||
|
|
@ -158,6 +187,47 @@ int xce_decode(io_packet_t* pkt, xce_request_t* req)
|
|||
}
|
||||
}
|
||||
|
||||
/* OPCode_walCatchupEntry: payload :Data per (sub-)entry — pre-extract
|
||||
* raw bytes into raw_payload/raw_payload_len (replConfigPush pattern).
|
||||
* The wire entries list order matches the decoded entries[] order. */
|
||||
if (rq.opcode == OPCode_walCatchupEntry
|
||||
&& req->body_which == Request_body_walcatchupentrymsg
|
||||
&& req->body.walcatchupentrymsg != NULL) {
|
||||
|
||||
wal_catchup_entry_t *batch = req->body.walcatchupentrymsg;
|
||||
WalCatchupEntry_ptr b_ptr = rq.body.walcatchupentrymsg;
|
||||
|
||||
/* Top-level payload (single-entry mode) */
|
||||
batch->raw_payload =
|
||||
extract_data_bytes(WalCatchupEntry_get_payload(b_ptr),
|
||||
&batch->raw_payload_len);
|
||||
|
||||
/* Batch sub-entries */
|
||||
WalCatchupEntry_list wlist = WalCatchupEntry_get_entries(b_ptr);
|
||||
int wn = (wlist.p.type != CAPN_NULL) ? capn_len(wlist) : 0;
|
||||
for (int i = 0; i < wn && i < batch->n_entries; i++) {
|
||||
if (!batch->entries || !batch->entries[i])
|
||||
continue;
|
||||
struct WalCatchupEntry we;
|
||||
get_WalCatchupEntry(&we, wlist, i);
|
||||
batch->entries[i]->raw_payload =
|
||||
extract_data_bytes(we.payload,
|
||||
&batch->entries[i]->raw_payload_len);
|
||||
}
|
||||
}
|
||||
|
||||
/* OPCode_snapshotTransfer: chunk payload :Data — pre-extract raw bytes. */
|
||||
if (rq.opcode == OPCode_snapshotTransfer
|
||||
&& req->body_which == Request_body_snapshottransfermsg
|
||||
&& req->body.snapshottransfermsg != NULL) {
|
||||
|
||||
snapshot_transfer_t *chunk = req->body.snapshottransfermsg;
|
||||
SnapshotTransfer_ptr s_ptr = rq.body.snapshottransfermsg;
|
||||
chunk->raw_payload =
|
||||
extract_data_bytes(SnapshotTransfer_get_payload(s_ptr),
|
||||
&chunk->raw_payload_len);
|
||||
}
|
||||
|
||||
TRACE("%s request op %d seq %ld from %s", __func__, req->opcode, req->seq,
|
||||
REQUEST_FROM(req));
|
||||
capn_free(&c);
|
||||
|
|
@ -210,6 +280,29 @@ void xce_free_request(xce_request_t *req)
|
|||
rcpr->raw_bytes_len = 0;
|
||||
}
|
||||
|
||||
if (req->body_which == Request_body_walcatchupentrymsg
|
||||
&& req->body.walcatchupentrymsg != NULL) {
|
||||
wal_catchup_entry_t *batch = req->body.walcatchupentrymsg;
|
||||
free(batch->raw_payload);
|
||||
batch->raw_payload = NULL;
|
||||
batch->raw_payload_len = 0;
|
||||
for (int i = 0; i < batch->n_entries; i++) {
|
||||
if (!batch->entries || !batch->entries[i])
|
||||
continue;
|
||||
free(batch->entries[i]->raw_payload);
|
||||
batch->entries[i]->raw_payload = NULL;
|
||||
batch->entries[i]->raw_payload_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (req->body_which == Request_body_snapshottransfermsg
|
||||
&& req->body.snapshottransfermsg != NULL) {
|
||||
snapshot_transfer_t *chunk = req->body.snapshottransfermsg;
|
||||
free(chunk->raw_payload);
|
||||
chunk->raw_payload = NULL;
|
||||
chunk->raw_payload_len = 0;
|
||||
}
|
||||
|
||||
free_Request(req);
|
||||
}
|
||||
|
||||
|
|
@ -356,6 +449,197 @@ int xce_replconfigpush_req_raw(endpoint_t *ep, xce_peerinfo_t *from,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* wal_entry_to_wire: fill a wire WalCatchupEntry from a C entry's header
|
||||
* fields and raw payload bytes. Sub-entry nesting is one level deep —
|
||||
* elements written here never carry their own entries list. */
|
||||
static void wal_entry_to_wire(struct capn_segment *cs,
|
||||
struct WalCatchupEntry *w,
|
||||
const wal_catchup_entry_t *e)
|
||||
{
|
||||
memset(w, 0, sizeof(*w));
|
||||
w->lsn = e->lsn;
|
||||
w->entryType = e->entry_type;
|
||||
if (e->raw_payload && e->raw_payload_len > 0) {
|
||||
capn_list8 pl = capn_new_list8(cs, (int)e->raw_payload_len);
|
||||
capn_setv8(pl, 0, e->raw_payload, (int)e->raw_payload_len);
|
||||
w->payload.p = pl.p;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* xce_wal_batch_encode_raw: encode an OPCode_walCatchupEntry batch into pkt.
|
||||
*
|
||||
* Bypasses encode_WalCatchupEntry (whose payload field routes through
|
||||
* xce_data_t — cannot carry raw bytes; same limitation as replConfigPush).
|
||||
* Builds the Request manually: a top-level WalCatchupEntry container
|
||||
* (lsn=0, entryType=0) whose entries list holds one element per batch
|
||||
* entry, each with lsn/entryType and payload written as capnp Data from
|
||||
* raw_payload/raw_payload_len.
|
||||
*
|
||||
* pkt is caller-allocated; on success it holds the serialized message
|
||||
* (caller owns; enqueue or free with free_packet). Returns 0 or -errno.
|
||||
*/
|
||||
int xce_wal_batch_encode_raw(wal_catchup_entry_t **entries, int n_entries,
|
||||
xce_peerinfo_t *from, uint64_t seq,
|
||||
io_packet_t *pkt)
|
||||
{
|
||||
struct capn c;
|
||||
struct capn_segment *cs;
|
||||
struct Request rq;
|
||||
struct WalCatchupEntry top;
|
||||
WalCatchupEntry_ptr tp;
|
||||
Request_ptr rp;
|
||||
int rc;
|
||||
|
||||
if (pkt == NULL || n_entries <= 0 || entries == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
capn_init_malloc(&c);
|
||||
cs = capn_root(&c).seg;
|
||||
|
||||
memset(&rq, 0, sizeof(rq));
|
||||
memset(&top, 0, sizeof(top));
|
||||
|
||||
/* Batch container: header fields zero, entries list carries the batch */
|
||||
WalCatchupEntry_list lst = new_WalCatchupEntry_list(cs, n_entries);
|
||||
for (int i = 0; i < n_entries; i++) {
|
||||
struct WalCatchupEntry w;
|
||||
wal_entry_to_wire(cs, &w, entries[i]);
|
||||
set_WalCatchupEntry(&w, lst, i);
|
||||
}
|
||||
top.entries = lst;
|
||||
|
||||
tp = new_WalCatchupEntry(cs);
|
||||
write_WalCatchupEntry(&top, tp);
|
||||
|
||||
rq.opcode = OPCode_walCatchupEntry;
|
||||
rq.seq = seq;
|
||||
rq.body_which = Request_body_walcatchupentrymsg;
|
||||
rq.body.walcatchupentrymsg = tp;
|
||||
encode_PeerInfo_ptr(cs, &rq.from, from);
|
||||
|
||||
rp = new_Request(cs);
|
||||
write_Request(&rq, rp);
|
||||
capn_setp(capn_root(&c), 0, rp.p);
|
||||
|
||||
pkt->sz = capn_size(&c);
|
||||
rc = alloc_packet(pkt);
|
||||
if (rc < 0) {
|
||||
capn_free(&c);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
capn_write_mem(&c, (uint8_t *)pkt->data, pkt->sz, 0);
|
||||
capn_free(&c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xce_wal_batch_req_raw(endpoint_t *ep, wal_catchup_entry_t **entries,
|
||||
int n_entries)
|
||||
{
|
||||
if (ep == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
io_packet_t *pkt = (io_packet_t *)calloc(1, sizeof(*pkt));
|
||||
if (!pkt)
|
||||
return -ENOMEM;
|
||||
|
||||
int rc = xce_wal_batch_encode_raw(entries, n_entries, ep->peer,
|
||||
endpoint_getseq(ep), pkt);
|
||||
if (rc < 0) {
|
||||
free(pkt);
|
||||
return rc;
|
||||
}
|
||||
|
||||
endpoint_enq(ep, &(pkt->link));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* xce_snapshot_chunk_encode_raw: encode an OPCode_snapshotTransfer chunk
|
||||
* into pkt. Envelope fields from *chunk; payload bytes written as capnp
|
||||
* Data from payload/payload_len (the legacy xce_data_t payload field is
|
||||
* ignored). pkt is caller-allocated. Returns 0 or -errno.
|
||||
*/
|
||||
int xce_snapshot_chunk_encode_raw(const snapshot_transfer_t *chunk,
|
||||
const uint8_t *payload, size_t payload_len,
|
||||
xce_peerinfo_t *from, uint64_t seq,
|
||||
io_packet_t *pkt)
|
||||
{
|
||||
struct capn c;
|
||||
struct capn_segment *cs;
|
||||
struct Request rq;
|
||||
struct SnapshotTransfer st;
|
||||
SnapshotTransfer_ptr sp;
|
||||
Request_ptr rp;
|
||||
int rc;
|
||||
|
||||
if (pkt == NULL || chunk == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
capn_init_malloc(&c);
|
||||
cs = capn_root(&c).seg;
|
||||
|
||||
memset(&rq, 0, sizeof(rq));
|
||||
memset(&st, 0, sizeof(st));
|
||||
|
||||
st.snapshotLsn = chunk->snapshot_lsn;
|
||||
st.chunkIndex = chunk->chunk_index;
|
||||
st.chunkOffset = chunk->chunk_offset;
|
||||
st.totalSize = chunk->total_size;
|
||||
st.isLast = chunk->is_last ? 1 : 0;
|
||||
if (payload && payload_len > 0) {
|
||||
capn_list8 pl = capn_new_list8(cs, (int)payload_len);
|
||||
capn_setv8(pl, 0, payload, (int)payload_len);
|
||||
st.payload.p = pl.p;
|
||||
}
|
||||
|
||||
sp = new_SnapshotTransfer(cs);
|
||||
write_SnapshotTransfer(&st, sp);
|
||||
|
||||
rq.opcode = OPCode_snapshotTransfer;
|
||||
rq.seq = seq;
|
||||
rq.body_which = Request_body_snapshottransfermsg;
|
||||
rq.body.snapshottransfermsg = sp;
|
||||
encode_PeerInfo_ptr(cs, &rq.from, from);
|
||||
|
||||
rp = new_Request(cs);
|
||||
write_Request(&rq, rp);
|
||||
capn_setp(capn_root(&c), 0, rp.p);
|
||||
|
||||
pkt->sz = capn_size(&c);
|
||||
rc = alloc_packet(pkt);
|
||||
if (rc < 0) {
|
||||
capn_free(&c);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
capn_write_mem(&c, (uint8_t *)pkt->data, pkt->sz, 0);
|
||||
capn_free(&c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xce_snapshot_chunk_req_raw(endpoint_t *ep, const snapshot_transfer_t *chunk,
|
||||
const uint8_t *payload, size_t payload_len)
|
||||
{
|
||||
if (ep == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
io_packet_t *pkt = (io_packet_t *)calloc(1, sizeof(*pkt));
|
||||
if (!pkt)
|
||||
return -ENOMEM;
|
||||
|
||||
int rc = xce_snapshot_chunk_encode_raw(chunk, payload, payload_len,
|
||||
ep->peer, endpoint_getseq(ep), pkt);
|
||||
if (rc < 0) {
|
||||
free(pkt);
|
||||
return rc;
|
||||
}
|
||||
|
||||
endpoint_enq(ep, &(pkt->link));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* xce_replconfig_reply_raw: primary sends OP_replConfig reply with real
|
||||
* config bytes via an endpoint (uses iomgr queueing).
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ extern void test_cluster_parse(void);
|
|||
extern void test_libxce_retry(void);
|
||||
extern void test_netio_on_connected(void);
|
||||
extern void test_raft(void);
|
||||
extern void test_wal_wire(void);
|
||||
|
||||
void setUp(void) { return; }
|
||||
|
||||
|
|
@ -143,6 +144,7 @@ int main(int argc, char* argv[])
|
|||
TEST(test_libxce_retry);
|
||||
TEST(test_netio_on_connected);
|
||||
TEST(test_raft);
|
||||
TEST(test_wal_wire);
|
||||
|
||||
return UNITY_END();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue