fix(controller): standby dispatch-gate rejections now redirect to a known primary (bug-1700, bug-1701)
xceadmin scheduler record status/start/stop against a standby failed with the bare "controller returned status -10014" (E_XCE_STANDBY) with no redirect, forcing the caller to guess the primary manually. bug-1700: myreq_handler's generic ha_dispatch_gate rejection path (cluster.c) now calls fill_redirect_reply() when rc == -XCE_ERR_STANDBY, mirroring the same helper OPCode_join's handler already uses -- -XCE_ERR_NOT_READY (CTRL_INIT/CTRL_ELECTING, no settled primary to redirect to) is deliberately excluded. Landing this alone had zero observable effect live: ha_state_t.primary_host/primary_port were never written by any production path on a standby (only test fixtures set them) -- fill_redirect_reply() always saw an empty/zero redirect target in a real deployment. bug-1701: closes that data gap with a pure local lookup, no new wire field. handle_ctrlhello() (config_repl.c) now resolves the ctrlhello sender's name against this node's own statically-known peers_cfg (parsed once from --peers at boot) and populates primary_host/port at the exact moment a standby first learns who primary is. Live-verified end to end on a fresh 3-node cluster: xceadmin scheduler record status against any standby now prints "redirected: <standby> -> <primary>" and returns the real status, confirmed again after a re-election changed which node was primary. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
This commit is contained in:
parent
ac1e312e2f
commit
f886c80930
4 changed files with 305 additions and 0 deletions
|
|
@ -366,6 +366,10 @@ void schedadmin_set_timeout_ms_for_test(long ms)
|
|||
#define SCHEDADMIN_TIMEOUT_MS 5000L
|
||||
#endif
|
||||
|
||||
/* fill_redirect_reply is defined in join.c (no header declaration) --
|
||||
* mirrors config_repl.c's identical local extern for the same helper. */
|
||||
extern void fill_redirect_reply(xce_reply_t *reply, const ha_state_t *ha);
|
||||
|
||||
static int myreq_handler(vtask_t* task)
|
||||
{
|
||||
vtask_data_t* data = (vtask_data_t*)task->data;
|
||||
|
|
@ -408,6 +412,25 @@ static int myreq_handler(vtask_t* task)
|
|||
if (rc != 0) {
|
||||
reply.status = rc;
|
||||
reply.data_which = RequestReply_data_nodata;
|
||||
/* bug-1700: -XCE_ERR_STANDBY (ha_dispatch_gate's CTRL_STANDBY
|
||||
* default-case rejection) is the one gate rejection where this node
|
||||
* may know who the primary is -- populate the redirect hint so
|
||||
* xceadmin's already-generic client-side follow_redirects() can
|
||||
* transparently retry there, exactly like OPCode_join's handler
|
||||
* already does (join.c:277). ha_dispatch_gate's OTHER nonzero
|
||||
* rejection, -XCE_ERR_NOT_READY (CTRL_INIT/CTRL_ELECTING, and a
|
||||
* fenced/quorum-less CTRL_PRIMARY), has no settled primary to
|
||||
* redirect to, so it is deliberately excluded. fill_redirect_reply()
|
||||
* is safe even when no primary is known yet: ha->primary_host is a
|
||||
* fixed buffer embedded in ha_state_t (never NULL), so an
|
||||
* as-yet-unknown primary just yields an empty string / port 0,
|
||||
* which both the capnp encode path (controller.capnp.c's
|
||||
* encode_RequestReply, gated on redirect_host != NULL) and the
|
||||
* client's discovery.py _redirect_target() already treat as
|
||||
* "no redirect" -- verified via code inspection, not assumed. */
|
||||
if (rc == -XCE_ERR_STANDBY) {
|
||||
fill_redirect_reply(&reply, &c->ha);
|
||||
}
|
||||
xce_reply(ep, c->myhost, &reply);
|
||||
xce_free_request(&request);
|
||||
return rc;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue