fix(bug-1731): fire HOOK_DETAIL_JOB_START/JOB_EXIT on WAL apply

apply_committed_entry() (persist.c), the single seam that applies every
committed WAL entry, only ever fired the generic HOOK_DETAIL_JOB_UPDATE
for job-state changes -- HOOK_DETAIL_JOB_START/JOB_EXIT were
permanently dead code, since job.c only ever fires them at
HOOK_BEFORE_EVENT (for WAL-append purposes). This left sched_job_start_
fun/sched_job_exit_fun (sched.c) and apiserver_job_start_fun/
apiserver_job_exit_fun (apiserver.c) unreachable, and the Go apiserver's
already-wired OnJobFinished handler permanently starved of data -- a
finished job's state could stay stuck (e.g. showing RUN) in the
scheduler's own view and the apiserver never learned a job finished.

The wire format was already enriched by bug-1697/bug-1689 with the
pidinfo/exit_info data these hooks need, so this closes the gap rather
than requiring new wire work. WAL_JOB_FINISH now fires
HOOK_DETAIL_JOB_EXIT; WAL_JOB_START now fires HOOK_DETAIL_JOB_START,
gated on a call-scoped decode flag rather than the transition result
(WAL_JOB_START applies as a no-op duplicate for every real job in
production per bug-1697, so gating on the transition result would make
it dead code again). WAL_JOB_FAIL -> HOOK_DETAIL_JOB_FAIL has the same
gap and is left as a follow-up.

Also includes this investigation's supporting changes: a DEBUG3 log-
retention bump (main.c, both controller and scheduler) so race-condition
evidence survives a live burst without rotating out, and DEBUG3-gated
diagnostic trace logging at the two points a job-update notification
can fire or silently drop (job.c, sched.c).

Live-verified end to end on a real deployed cluster: scheduler-side
sched_job_state_change now fires for RUN and EXIT states, and the
apiserver's OnJobFinished -- previously never invoked in production --
now correctly updates job status on completion.

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 11:29:10 +00:00
parent 42e1eeaa29
commit ebe691c3ed
No known key found for this signature in database
GPG key ID: 5BCDAAE0123E9FE2
9 changed files with 521 additions and 22 deletions

View file

@ -84,9 +84,19 @@ func (h *DefaultHandler) OnJobDeleted(jobid uint64) {
store.Jobs.Delete(jobid)
}
// bug-1731: previously permanently starved of data -- apply_committed_entry()
// (persist.c) never fired the controller-side HOOK_DETAIL_JOB_EXIT this
// method's C caller (apiserver_job_exit_fun -> NotifyData_data_jobFinishInfo)
// depends on, so OnJobFinished was live, wired, and correct but never once
// invoked in production. Logs at Info (mirroring this file's existing
// "job array push received and expanded" convention) so a finished job's
// terminal-state push to the apiserver is directly observable in
// apiserver.log, not just inferable from store state.
func (h *DefaultHandler) OnJobFinished(jobid uint64, exitCode int) {
existing, ok := store.Jobs.Get(jobid)
if !ok {
logger.Info("OnJobFinished: jobid not found in store, ignoring",
zap.Uint64("jobid", jobid), zap.Int("exit_code", exitCode))
return
}
if exitCode == 0 {
@ -95,6 +105,9 @@ func (h *DefaultHandler) OnJobFinished(jobid uint64, exitCode int) {
existing.JobStatus = models.JOBSTAT["EXIT"]
}
store.Jobs.Put(jobid, existing)
logger.Info("OnJobFinished: job status updated",
zap.Uint64("jobid", jobid), zap.Int("exit_code", exitCode),
zap.Int("job_status", existing.JobStatus))
}
func (h *DefaultHandler) OnQueueUpdated(queue *models.QueueInfo) {