fix(bug-1731): fire HOOK_DETAIL_JOB_FAIL on WAL_JOB_FAIL apply

Follow-up to ebe691c3 (HOOK_DETAIL_JOB_START/JOB_EXIT), same gap for job
failures: apply_committed_entry()'s WAL_JOB_FAIL case only ever fired
the generic HOOK_DETAIL_JOB_UPDATE, leaving sched_job_fail_fun (sched.c)
and apiserver_job_fail_fun (apiserver.c) dead -- the apiserver's
OnJobFinished was starved of data for job failures specifically (the
prior fix only closed this for clean finishes).

Fires HOOK_DETAIL_JOB_FAIL only for the WAL_JOB_FAIL_STAGE_FLAG branch,
which already constructs a real job_failinfo_t -- the legacy _pad==0
absolute-overwrite branch has no fail-specific data and must not
fabricate one, mirroring how the WAL_JOB_START fix only fires when
pidinfo was genuinely decoded. Unlike WAL_JOB_START, no gate-widening
was needed: apply_job_fail_transition() always returns
JOB_TRANSITION_APPLIED for a genuine new fail transition (confirmed by
reading it), so the existing result-gated early return already covers
this correctly.

TDD: RED confirmed (fix reverted, test failed "Expected 2 Was 1"),
GREEN after restoring it. Full regression: 1220 tests, only the known
bug-1561 flake.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019QxaSzHRB7pmYFp4cVnwkA
This commit is contained in:
Rongsong Shen 2026-08-29 12:13:01 +00:00
parent 6193fa7ea1
commit 670098150d
No known key found for this signature in database
GPG key ID: 5BCDAAE0123E9FE2
3 changed files with 173 additions and 4 deletions

View file

@ -4888,13 +4888,16 @@ int apply_committed_entry(persist_t* p, uint32_t wal_type, uint64_t lsn,
lock(j, RTTI_LOCK_WRITE);
job_transition_result_t result;
job_failinfo_t failinfo;
bool have_failinfo = false;
if (js->_pad & WAL_JOB_FAIL_STAGE_FLAG) {
job_failinfo_t failinfo = {
failinfo = (job_failinfo_t){
.jobid = js->jobid,
.stage = js->_pad & ~WAL_JOB_FAIL_STAGE_FLAG,
.code = 0,
.detail = NULL,
};
have_failinfo = true;
result = apply_job_fail_transition(j, &failinfo);
} else {
result = apply_job_update_transition(j, (int)js->state);
@ -4904,8 +4907,32 @@ int apply_committed_entry(persist_t* p, uint32_t wal_type, uint64_t lsn,
if (result != JOB_TRANSITION_APPLIED)
return 0; /* rejected-backward / no-op-duplicate — no hook */
/* Hazard 1 guard — see the identical guard above. */
/* Hazard 1 guard — see the identical guard above. Single bracket
* wraps both possible fires (bug-1731 follow-up adds
* HOOK_DETAIL_JOB_FAIL alongside the pre-existing JOB_UPDATE). */
t_apply_seam_notifying = true;
/* bug-1731 follow-up: HOOK_DETAIL_JOB_FAIL -- only for the
* WAL_JOB_FAIL_STAGE_FLAG branch, which is the only one that
* constructs a real job_failinfo_t; the legacy _pad==0 absolute-
* overwrite branch has no fail-specific data to report and must not
* fabricate one (mirrors bug-1731's own WAL_JOB_START fix, which
* only fires HOOK_DETAIL_JOB_START when pidinfo was genuinely
* decoded). Args mirror job.c's own HOOK_BEFORE_EVENT fire sites for
* this detail — (c, failinfo, job). Revives sched_job_fail_fun
* (sched.c) and apiserver_job_fail_fun (apiserver.c), previously
* permanently dead for the exact same reason HOOK_DETAIL_JOB_START/
* JOB_EXIT were (see persist.h's apply_committed_entry doc comment
* and bug-1731) -- the apiserver's OnJobFinished (Go side,
* NotifyData_data_jobFailInfo case) was starved of data for job
* FAILURES specifically (bug-1731's own fix only closed this for
* clean finishes, via WAL_JOB_FINISH/HOOK_DETAIL_JOB_EXIT). */
if (have_failinfo) {
xce_run_hooks(&c->hooksys, XCE_CONTROLLER_HOOK_JOB,
HOOK_AFTER_EVENT, HOOK_DETAIL_JOB_FAIL, c,
&failinfo, j);
}
xce_run_hooks(&c->hooksys, XCE_CONTROLLER_HOOK_JOB, HOOK_AFTER_EVENT,
HOOK_DETAIL_JOB_UPDATE, c, j);
t_apply_seam_notifying = false;