fix(election-iomgr-refactor): Raft election, config decode, and RDHUP chain
- ha.c: self-vote in ha_start_election_async (Raft invariant) - ha.c: heartbeat_timeout_ms for hb_timeout_thread (not election_timeout_ms) - ha.c: clear voted_for before RDHUP jitter (stale vote from dead primary) - ha.c: same-term voted_for check in RDHUP jitter - config_repl.c/h: use xce_decode for upgradeConfig (capnp extraction before capn_free) - cluster.c: fix upgradeConfig handler direction (standbys apply, primaries skip) - cluster.c: remove raw_pkt_copy hack; use req directly - join.c: push config to newly-joined standbys - typemap.h/request.h/request.c: raw_sha256/raw_bytes fields for decoded upgradeConfig - test_ha_failover.c: update heartbeat timeout test to use g_fast_cfg
This commit is contained in:
parent
1ded1022fb
commit
dbb750ebd3
8 changed files with 198 additions and 102 deletions
|
|
@ -69,6 +69,56 @@ int xce_decode(io_packet_t* pkt, xce_request_t* req)
|
|||
|
||||
decode_Request(req, &rq);
|
||||
|
||||
/* OPCode_upgradeConfig carries sha256 and bytes as List(UInt8) on the wire.
|
||||
* decode_UpgradeConfigRequest() intentionally leaves raw_sha256/raw_bytes as
|
||||
* NULL (the project's Data union cannot carry raw bytes via decode_Data_ptr).
|
||||
* Extract them here using UpgradeConfigRequest_get_* + capn_getv8 BEFORE
|
||||
* capn_free() destroys the capnp segment. */
|
||||
if (rq.opcode == OPCode_upgradeConfig
|
||||
&& req->body_which == Request_body_upgradeconfigrequestmsg
|
||||
&& req->body.upgradeconfigrequestmsg != NULL) {
|
||||
|
||||
upgrade_config_req_t *ucr = req->body.upgradeconfigrequestmsg;
|
||||
UpgradeConfigRequest_ptr u_ptr = rq.body.upgradeconfigrequestmsg;
|
||||
|
||||
/* sha256 */
|
||||
{
|
||||
Data_ptr dp = UpgradeConfigRequest_get_sha256(u_ptr);
|
||||
capn_list8 sha_list;
|
||||
sha_list.p = dp.p;
|
||||
if (sha_list.p.type != CAPN_NULL) {
|
||||
int slen = capn_len(sha_list);
|
||||
if (slen > 0) {
|
||||
uint8_t *sbuf = (uint8_t *)malloc((size_t)slen);
|
||||
if (sbuf && capn_getv8(sha_list, 0, sbuf, slen) == slen) {
|
||||
ucr->raw_sha256 = sbuf;
|
||||
} else {
|
||||
free(sbuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* bytes */
|
||||
{
|
||||
Data_ptr dp = UpgradeConfigRequest_get_bytes(u_ptr);
|
||||
capn_list8 bytes_list;
|
||||
bytes_list.p = dp.p;
|
||||
if (bytes_list.p.type != CAPN_NULL) {
|
||||
int blen = capn_len(bytes_list);
|
||||
if (blen > 0) {
|
||||
uint8_t *bbuf = (uint8_t *)malloc((size_t)blen);
|
||||
if (bbuf && capn_getv8(bytes_list, 0, bbuf, blen) == blen) {
|
||||
ucr->raw_bytes = bbuf;
|
||||
ucr->raw_bytes_len = (size_t)blen;
|
||||
} else {
|
||||
free(bbuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TRACE("%s request op %d seq %ld from %s", __func__, req->opcode, req->seq,
|
||||
REQUEST_FROM(req));
|
||||
capn_free(&c);
|
||||
|
|
@ -85,6 +135,35 @@ void init_reply_by_req(xce_reply_t* reply, xce_request_t* req)
|
|||
reply->opcode = req->opcode;
|
||||
}
|
||||
|
||||
/*
|
||||
* xce_free_request: free an xce_request_t and ALL owned sub-fields.
|
||||
*
|
||||
* The generated free_Request() handles the capnp-decoded fields but does
|
||||
* not know about raw_sha256/raw_bytes on upgrade_config_req_t (those are
|
||||
* extra fields populated by xce_decode() before capn_free()). This wrapper
|
||||
* frees those extra fields first, then calls free_Request().
|
||||
*
|
||||
* Use this everywhere instead of calling free_Request() directly.
|
||||
*/
|
||||
void xce_free_request(xce_request_t *req)
|
||||
{
|
||||
if (req == NULL)
|
||||
return;
|
||||
|
||||
/* Free raw_* fields that the generated free_UpgradeConfigRequest skips */
|
||||
if (req->body_which == Request_body_upgradeconfigrequestmsg
|
||||
&& req->body.upgradeconfigrequestmsg != NULL) {
|
||||
upgrade_config_req_t *ucr = req->body.upgradeconfigrequestmsg;
|
||||
free(ucr->raw_sha256);
|
||||
ucr->raw_sha256 = NULL;
|
||||
free(ucr->raw_bytes);
|
||||
ucr->raw_bytes = NULL;
|
||||
ucr->raw_bytes_len = 0;
|
||||
}
|
||||
|
||||
free_Request(req);
|
||||
}
|
||||
|
||||
int xce_request(endpoint_t* ep, xce_request_t* req)
|
||||
{
|
||||
int rc;
|
||||
|
|
@ -567,31 +646,17 @@ cleanup:
|
|||
}
|
||||
|
||||
/*
|
||||
* xce_upgrade_config_req_extract: attempt to extract sha256 + bytes
|
||||
* from a decoded OP_upgradeConfig request.
|
||||
* xce_upgrade_config_req_extract: extract sha256 + bytes from a decoded
|
||||
* OP_upgradeConfig request.
|
||||
*
|
||||
* ARCHITECTURAL FINDING (Task A research):
|
||||
* --------------------------------------------------------------------
|
||||
* The decoded form is unusable for byte extraction. When iomgr receives
|
||||
* an OP_upgradeConfig packet, xce_decode() calls decode_Request →
|
||||
* decode_UpgradeConfigRequest → decode_Data_ptr. decode_Data_ptr
|
||||
* allocates a fresh xce_data_t and walks decode_Data, which treats the
|
||||
* pointer as a discriminated Data union (reading a 16-bit discriminator
|
||||
* at offset 0). But the wire actually carries a List(UInt8) with
|
||||
* datasz=0, so capn_read16 returns 0 (Data_data_schedjobs) and a
|
||||
* zero-filled xce_jobs_data_t is allocated. No raw bytes survive.
|
||||
* xce_decode() now extracts the raw List(UInt8) bytes from the capnp
|
||||
* segment before capn_free(), storing malloc'd copies in
|
||||
* req->body.upgradeconfigrequestmsg->raw_sha256 and ->raw_bytes.
|
||||
* This function returns shallow copies (pointers into the struct); the
|
||||
* caller must NOT free them — they are owned by the xce_request_t and
|
||||
* released by free_Request().
|
||||
*
|
||||
* Worse: xce_decode() then calls capn_free() and free_packet(), so the
|
||||
* original capnp segment containing the List(UInt8) bytes is gone by
|
||||
* the time any handler runs. The xce_data_t* fields on the decoded
|
||||
* xce_request_t are non-NULL but contain only the bogus jobs_data_t.
|
||||
*
|
||||
* This function therefore always returns -ENOTSUP. Task D must use
|
||||
* xce_upgrade_config_req_extract_raw() below and arrange for the raw
|
||||
* io_packet_t bytes to reach handle_upgrade_config() before xce_decode
|
||||
* destroys them — e.g. by adding a pre-decode peek for OP_upgradeConfig
|
||||
* in the message-handler dispatch, or by stashing the raw buffer on
|
||||
* xce_request_t for select opcodes.
|
||||
* Returns 0 on success, -EINVAL on bad args or missing fields.
|
||||
*/
|
||||
int xce_upgrade_config_req_extract(const xce_request_t *req,
|
||||
uint8_t **out_bytes, size_t *out_len,
|
||||
|
|
@ -604,7 +669,22 @@ int xce_upgrade_config_req_extract(const xce_request_t *req,
|
|||
*out_bytes = NULL;
|
||||
*out_len = 0;
|
||||
*out_sha256 = NULL;
|
||||
return -ENOTSUP;
|
||||
|
||||
if (req->body_which != Request_body_upgradeconfigrequestmsg
|
||||
|| req->body.upgradeconfigrequestmsg == NULL) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
const upgrade_config_req_t *ucr = req->body.upgradeconfigrequestmsg;
|
||||
if (ucr->raw_sha256 == NULL || ucr->raw_bytes == NULL
|
||||
|| ucr->raw_bytes_len == 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*out_sha256 = ucr->raw_sha256;
|
||||
*out_bytes = ucr->raw_bytes;
|
||||
*out_len = ucr->raw_bytes_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue