test(controller): cover live dispatch's apiserver notification (bug-1679)

New test_dc_success_notifies_job_update_hook_exactly_once
(test_dispatch_commit.c): a spy hook counting HOOK_AFTER_EVENT/
HOOK_DETAIL_JOB_UPDATE fires specifically -- the exact hook
apiserver_job_update_fun subscribes to in production. Asserts exactly
one fire per successful live dispatch.

Independently reproduced RED->GREEN: disabled the new notify call
with the test kept, rebuilt, confirmed FAIL (Expected 1 Was 0);
restored, rebuilt, confirmed PASS.

Also independently confirmed no double-WAL-append regression via two
existing tests whose fixtures genuinely wire persist.cluster with
persist_job_hook_fun registered: test_jobdispatch_receipt_ack_no_wal_append
(asserts exactly one WAL entry for a successful dispatch) and
test_handle_jobdispatch_matches_dispatch_a_job_notify_count -- both
pass unchanged. Full controller_unit_test: 1137/1137 (2 pre-existing
test_config_repl.c failures, confirmed identical on stock code via
git-stash -- the known overlayfs fdatasync errno=5 flake, bug-1561).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
This commit is contained in:
Rongsong Shen 2026-08-25 03:44:24 +00:00
parent da7813bfdb
commit 8d6f610ef9
No known key found for this signature in database
GPG key ID: 5BCDAAE0123E9FE2

View file

@ -274,6 +274,71 @@ static void test_dc_success_commits_alloc_state_and_resets_counter(void)
printf("test_dc_success_commits_alloc_state_and_resets_counter: PASS\n");
}
/* bug-1679: spy hook counting HOOK_AFTER_EVENT/HOOK_DETAIL_JOB_UPDATE fires
* specifically -- the exact hook apiserver_job_update_fun (apiserver.c)
* subscribes to in production to push a job's current state to every
* connected apiserver. Mirrors test_apply_committed_entry.c's tace_spy_hook
* pattern, filtered to this one detail rather than every AFTER_EVENT. */
static int g_dc_update_hook_fires;
static int dc_job_update_spy_hook(int type, int situation, int detail,
void* data, va_list ap)
{
(void)type; (void)data; (void)ap;
if (situation == HOOK_AFTER_EVENT && detail == HOOK_DETAIL_JOB_UPDATE)
g_dc_update_hook_fires++;
return 0;
}
#define DC_JOBID_NOTIFY 91004ULL
/* THE core proof for bug-1679 (Req: a live successful dispatch must notify
* the apiserver, restart or not): dispatch_a_job_()'s success path fires
* HOOK_DETAIL_JOB_UPDATE's AFTER_EVENT exactly once, synchronously, in the
* SAME operation that commits state+allocs -- unlike before this fix, where
* dispatch_a_job_() fired no hooks at all and relied entirely on
* apply_committed_entry()'s later (here, always-suppressed-by-duplicate-
* state) notify. RED before the fix: g_dc_update_hook_fires stays 0. */
static void test_dc_success_notifies_job_update_hook_exactly_once(void)
{
job_t* j;
xce_job_alloc_t* alloc;
int rc;
dc_setup();
g_dc_update_hook_fires = 0;
TEST_ASSERT_EQUAL_INT(0,
xce_add_hook(&g_dc.hooksys, XCE_CONTROLLER_HOOK_JOB,
dc_job_update_spy_hook, NULL));
j = dc_seed_ready(DC_JOBID_NOTIFY, 0, "dq", "wkr-dc-notify");
alloc = make_job_alloc(DC_JOBID_NOTIFY, 1,
make_host_allocs(1,
make_host_alloc((char*)"wkr-dc-notify", 1,
make_allocs(1,
make_alloc((char*)"slots", NULL, 0, 1.0f, 0)))));
TEST_ASSERT_NOT_NULL(alloc);
rc = dispatch_a_job_test(&g_dc, NULL, alloc);
TEST_ASSERT_TRUE(rc >= 0);
TEST_ASSERT_EQUAL_UINT32(JOB_STAT_RUN, j->job.state);
TEST_ASSERT_EQUAL_INT_MESSAGE(1, g_dc_update_hook_fires,
"a live successful dispatch must fire HOOK_DETAIL_JOB_UPDATE's "
"AFTER_EVENT exactly once (bug-1679) -- apiserver_job_update_fun's "
"subscription is the ONLY mechanism that pushes the job's new "
"RUN state and allocation to the apiserver; without this fire the "
"job is stuck at PEND from the apiserver's perspective forever");
free_JobAlloc_ptr(&alloc);
dc_drain_ep(&g_dc_worker_ep);
xce_disable_hook(&g_dc.hooksys, XCE_CONTROLLER_HOOK_JOB,
dc_job_update_spy_hook);
dc_teardown();
printf("test_dc_success_notifies_job_update_hook_exactly_once: PASS\n");
}
#define DC_JOBID_NOTFOUND 91002ULL
/* Regression safety net (untouched failure branch): a dispatch whose
@ -666,6 +731,7 @@ void test_dispatch_commit(void)
"5.1)\n\n");
RUN_TEST(test_dc_success_commits_alloc_state_and_resets_counter);
RUN_TEST(test_dc_success_notifies_job_update_hook_exactly_once);
RUN_TEST(test_dc_worker_not_found_leaves_job_pend_and_alloc_uncommitted);
RUN_TEST(test_dc_success_appends_wal_job_dispatch_durably);
}