forked from lei.chen/myxce
feat(raft-adapter-conformance): send explicit reply on every config-push outcome
handle_repl_config_push now sends a ReplConfigPushReply{accepted,term}
from every exit path instead of silently dropping on the four reject
conditions (malformed request, missing bytes/sha256, sha256 mismatch,
conf_save_config failure). Term is sourced live from c->ha.term.
-EAGAIN activation-defer still replies accepted=1, unchanged.
Adds an XCE_TESTING-only send-hook mirroring ha_wal_ack_send_hook so
tests can observe the reply payload without a live endpoint.
This commit is contained in:
parent
ee7ede03c2
commit
b7828eed4a
4 changed files with 148 additions and 13 deletions
|
|
@ -1247,6 +1247,50 @@ int handle_config_request(cluster_t *c, endpoint_t *ep, xce_request_t *req)
|
|||
return rc2;
|
||||
}
|
||||
|
||||
#ifdef XCE_TESTING
|
||||
void (*config_push_reply_send_hook)(uint64_t term, uint64_t version_tag,
|
||||
int accepted) = NULL;
|
||||
#endif
|
||||
|
||||
/* send_config_push_reply: construct and send ReplConfigPushReply{accepted,
|
||||
* version_tag, term} back to the primary over ep. Mirrors
|
||||
* ha_send_wal_ack_reply_ex (ha.c, bug-1479) exactly: when
|
||||
* config_push_reply_send_hook is set (XCE_TESTING), the hook is called with
|
||||
* the built {term, version_tag, accepted} INSTEAD of sending over ep -- lets
|
||||
* a unit test observe the reply payload without a live endpoint.
|
||||
* version_tag is always the standby's OWN current config_version_tag (per
|
||||
* repl_config_push_reply_s's documented semantics, "standby's current
|
||||
* version tag after decision") -- not the pushed request's tag, so a reject
|
||||
* correctly reports the unchanged local state. raft-adapter-conformance
|
||||
* task 2.1 (Req 2.2, 2.3, 2.5, 7.4): every handle_repl_config_push outcome
|
||||
* -- accept or reject -- sends exactly one explicit reply, never silence. */
|
||||
static void send_config_push_reply(cluster_t *c, endpoint_t *ep,
|
||||
xce_request_t *req, int accepted)
|
||||
{
|
||||
uint64_t term = (uint64_t)atomic_load_explicit(&c->ha.term,
|
||||
memory_order_acquire);
|
||||
uint64_t version_tag = c->myconf->config_version_tag;
|
||||
|
||||
#ifdef XCE_TESTING
|
||||
if (config_push_reply_send_hook) {
|
||||
config_push_reply_send_hook(term, version_tag, accepted);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
repl_config_push_reply_t reply_body = {
|
||||
.accepted = accepted,
|
||||
.version_tag = version_tag,
|
||||
.term = term,
|
||||
};
|
||||
xce_reply_t reply;
|
||||
init_reply_by_req(&reply, req);
|
||||
reply.status = 0;
|
||||
reply.data_which = RequestReply_data_replconfigpushreplymsg;
|
||||
reply.data.replconfigpushreplymsg = &reply_body;
|
||||
xce_reply(ep, c->myhost, &reply);
|
||||
}
|
||||
|
||||
/* ── handle_repl_config_push ────────────────────────────────────────────────
|
||||
*
|
||||
* Standby handler for OPCode_replConfigPush sent by the primary.
|
||||
|
|
@ -1256,13 +1300,17 @@ int handle_config_request(cluster_t *c, endpoint_t *ep, xce_request_t *req)
|
|||
* 2. Verify SHA-256 integrity
|
||||
* 3. Save to disk via conf_save_config
|
||||
* 4. Reload cluster_config in memory via load_primary_config
|
||||
* 5. Send ReplConfigPushReply with accepted=1
|
||||
* 5. Send ReplConfigPushReply -- accepted=1 on success (including
|
||||
* activation-deferred EAGAIN), accepted=0 on any reject (raft-adapter-
|
||||
* conformance Req 2.2, 2.3, 2.5); every path sends exactly one reply,
|
||||
* never silence.
|
||||
*/
|
||||
int handle_repl_config_push(cluster_t *c, endpoint_t *ep, xce_request_t *req)
|
||||
{
|
||||
if (req->body_which != Request_body_replconfigpushrequestmsg
|
||||
|| !req->body.replconfigpushrequestmsg) {
|
||||
WARN("handle_repl_config_push: malformed request");
|
||||
send_config_push_reply(c, ep, req, /*accepted*/0);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
@ -1282,6 +1330,7 @@ int handle_repl_config_push(cluster_t *c, endpoint_t *ep, xce_request_t *req)
|
|||
if (!rcpr->raw_bytes || rcpr->raw_bytes_len == 0 || !rcpr->raw_sha256) {
|
||||
WARN("handle_repl_config_push: missing bytes or sha256 (tag=%016" PRIx64 ")",
|
||||
rcpr->version_tag);
|
||||
send_config_push_reply(c, ep, req, /*accepted*/0);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
@ -1290,6 +1339,7 @@ int handle_repl_config_push(cluster_t *c, endpoint_t *ep, xce_request_t *req)
|
|||
SHA256(rcpr->raw_bytes, rcpr->raw_bytes_len, computed);
|
||||
if (memcmp(computed, rcpr->raw_sha256, 32) != 0) {
|
||||
WARN("handle_repl_config_push: SHA-256 mismatch — rejecting push");
|
||||
send_config_push_reply(c, ep, req, /*accepted*/0);
|
||||
return -EBADMSG;
|
||||
}
|
||||
|
||||
|
|
@ -1305,6 +1355,7 @@ int handle_repl_config_push(cluster_t *c, endpoint_t *ep, xce_request_t *req)
|
|||
rcpr->raw_sha256);
|
||||
if (rc != 0) {
|
||||
WARN("handle_repl_config_push: conf_save_config failed: %d", rc);
|
||||
send_config_push_reply(c, ep, req, /*accepted*/0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
@ -1322,17 +1373,10 @@ int handle_repl_config_push(cluster_t *c, endpoint_t *ep, xce_request_t *req)
|
|||
if (act_rc != 0 && act_rc != -EAGAIN)
|
||||
WARN("handle_repl_config_push: config_activate_committed rc=%d", act_rc);
|
||||
|
||||
/* ACK the primary */
|
||||
repl_config_push_reply_t reply_body = {
|
||||
.accepted = 1,
|
||||
.version_tag = c->myconf->config_version_tag,
|
||||
};
|
||||
xce_reply_t reply;
|
||||
init_reply_by_req(&reply, req);
|
||||
reply.status = 0;
|
||||
reply.data_which = RequestReply_data_replconfigpushreplymsg;
|
||||
reply.data.replconfigpushreplymsg = &reply_body;
|
||||
xce_reply(ep, c->myhost, &reply);
|
||||
/* ACK the primary. -EAGAIN (activation deferred) is NOT a rejection --
|
||||
* the bytes were saved out-of-band successfully, so this still replies
|
||||
* accepted=1 (raft-adapter-conformance Req 2.5, 7.5: unchanged). */
|
||||
send_config_push_reply(c, ep, req, /*accepted*/1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue