feat(worker-rejoin-resync): task 6.2 - route job-state report to reconciliation
Add OPCode_jobStateReport dispatch in myreq_handler, reassembling a report across multiple incoming pages before handing it to task 3.2's resync_apply_report() exactly once - proven via a test-only call counter, not just end-state equivalence (a double-apply could pass an end-state check if idempotent). Standby-redirect investigation (Req 8.1/8.3): confirmed ha_dispatch_gate already redirects every rejected opcode uniformly, so no opcode-specific code was needed for that. OPCode_jobStateQuery has no controller-side receive path at all (only sent, never received) so needed no gate change either. That same investigation found a different, more consequential gap: fixes bug-1753, ha_dispatch_gate's quorum-loss fact-admission allowlist (OPCode_notify/update/setpreason/clrpreason) was missing OPCode_jobStateReport, so a report arriving during a transient quorum-loss window - this feature's own core failover scenario - would be rejected outright with no retry (worker sends fire-and-forget). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y4M5MJM2zK6nU43da267Ku
This commit is contained in:
parent
a3d12d37d9
commit
9f655c61c7
12 changed files with 1178 additions and 12 deletions
|
|
@ -982,6 +982,37 @@ static void test_gate_no_quorum_admits_clrpreason_fact(void)
|
|||
printf("test_gate_no_quorum_admits_clrpreason_fact: PASS\n");
|
||||
}
|
||||
|
||||
/* ── OPCode_jobStateReport admitted without write quorum ───────────────────
|
||||
* (worker-rejoin-resync task 6.2, found live while auditing this exact gate
|
||||
* for task 6.2's own standby-redirect investigation -- design.md only
|
||||
* anticipated the STANDBY case, per bug-1700's precedent, and did not
|
||||
* separately consider a PRIMARY that has temporarily lost write quorum.
|
||||
* OPCode_jobStateReport is a worker's already-happened job-state fact, sent
|
||||
* fire-and-forget (xce_job_state_report_req(), request.c) with no
|
||||
* redelivery on rejection -- exactly the same shape as OPCode_notify, which
|
||||
* this same allowlist already exists to protect from bug-396's silent-loss
|
||||
* failure mode. Rejecting a reconciliation report outright during a
|
||||
* transient quorum-loss window would reproduce that exact loss mode for
|
||||
* the specific facts this whole feature exists to recover (bug-1741). */
|
||||
static void test_gate_no_quorum_admits_jobstatereport_fact(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* Arrange: quorum absent */
|
||||
fact_gate_setup(3, 0);
|
||||
|
||||
/* Act */
|
||||
rc = ha_dispatch_gate(&g_fg_ha, OPCode_jobStateReport);
|
||||
|
||||
/* Assert */
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc,
|
||||
"OPCode_jobStateReport (worker reconciliation fact) must be "
|
||||
"admitted without write quorum, exactly like OPCode_notify");
|
||||
|
||||
fact_gate_teardown();
|
||||
printf("test_gate_no_quorum_admits_jobstatereport_fact: PASS\n");
|
||||
}
|
||||
|
||||
/* ── PIN: request-shaped opcodes stay rejected without quorum (Req 3.2) ───
|
||||
* The allowlist is FACTS ONLY: every request-shaped opcode (a requester
|
||||
* awaits confirmation, served by the task-3 handler barrier) must keep
|
||||
|
|
@ -1018,16 +1049,17 @@ static void test_gate_no_quorum_still_rejects_request_opcodes(void)
|
|||
static void test_gate_with_quorum_admits_all_opcodes(void)
|
||||
{
|
||||
static const struct { int opcode; const char *name; } ops[] = {
|
||||
{ OPCode_notify, "OPCode_notify" },
|
||||
{ OPCode_update, "OPCode_update" },
|
||||
{ OPCode_setpreason, "OPCode_setpreason" },
|
||||
{ OPCode_clrpreason, "OPCode_clrpreason" },
|
||||
{ OPCode_jobsub, "OPCode_jobsub" },
|
||||
{ OPCode_jobcontrol, "OPCode_jobcontrol" },
|
||||
{ OPCode_queuecontrol, "OPCode_queuecontrol" },
|
||||
{ OPCode_hostcontrol, "OPCode_hostcontrol" },
|
||||
{ OPCode_jobdispatch, "OPCode_jobdispatch" },
|
||||
{ OPCode_join, "OPCode_join" },
|
||||
{ OPCode_notify, "OPCode_notify" },
|
||||
{ OPCode_update, "OPCode_update" },
|
||||
{ OPCode_setpreason, "OPCode_setpreason" },
|
||||
{ OPCode_clrpreason, "OPCode_clrpreason" },
|
||||
{ OPCode_jobStateReport, "OPCode_jobStateReport" },
|
||||
{ OPCode_jobsub, "OPCode_jobsub" },
|
||||
{ OPCode_jobcontrol, "OPCode_jobcontrol" },
|
||||
{ OPCode_queuecontrol, "OPCode_queuecontrol" },
|
||||
{ OPCode_hostcontrol, "OPCode_hostcontrol" },
|
||||
{ OPCode_jobdispatch, "OPCode_jobdispatch" },
|
||||
{ OPCode_join, "OPCode_join" },
|
||||
};
|
||||
size_t i;
|
||||
|
||||
|
|
@ -2459,6 +2491,7 @@ void test_commit_barrier_rollout(void)
|
|||
RUN_TEST(test_gate_no_quorum_admits_update_fact);
|
||||
RUN_TEST(test_gate_no_quorum_admits_setpreason_fact);
|
||||
RUN_TEST(test_gate_no_quorum_admits_clrpreason_fact);
|
||||
RUN_TEST(test_gate_no_quorum_admits_jobstatereport_fact);
|
||||
RUN_TEST(test_gate_no_quorum_still_rejects_request_opcodes);
|
||||
RUN_TEST(test_gate_with_quorum_admits_all_opcodes);
|
||||
/* Task 5.1 — config barrier: configAck arithmetic + upgrade failure
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue