fix(controller): job start_time/finish_time/run_time never set by apply-pump

apply_committed_entry()'s shared WAL_JOB_START/FINISH/STOP/RESUME/RUSAGE/
CKPT/PREASON dispatch case only ever called the generic, state-only
apply_job_update_transition(j, state) -- start_time/finish_time/run_time
were never touched, so `xcecli joblist` always showed StartTime=0 for
RUN/DONE/EXIT jobs and FinishTime=0 for DONE jobs (EXIT jobs got a
finish_time only via the separate, richer WAL_JOB_FAIL dispatch path).

Fix is additive: stamp time(NULL) keyed on wal_type right after
apply_job_update_transition() succeeds, still under the same
lock(j, RTTI_LOCK_WRITE) -- WAL_JOB_START sets start_time, WAL_JOB_FINISH
sets finish_time and derives run_time. No change to
apply_job_update_transition()'s signature or the dispatch structure.

Add test_ace_job_start_sets_start_time and
test_ace_job_finish_sets_finish_time_and_run_time to
test_apply_committed_entry.c, following the existing tace_seed_job
fixture pattern.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Rongsong Shen 2026-08-20 04:24:44 +00:00
parent 008553e982
commit 5147396d0e
No known key found for this signature in database
GPG key ID: 5BCDAAE0123E9FE2
4 changed files with 90 additions and 1 deletions

View file

@ -3714,7 +3714,9 @@ int apply_committed_entry(persist_t* p, uint32_t wal_type, uint64_t lsn,
* apply_committed_entry for the full routing table and why this
* does not dispatch to the richer type-specific apply_job_{start,
* finish}_transition() cores (their parameter structs are not
* present on this wire format). */
* present on this wire format) — start_time/finish_time/run_time
* are instead stamped directly below, keyed on wal_type, since
* apply_job_update_transition() only ever touches `state`. */
if (len < sizeof(wal_jobstate_t))
return 0; /* undersized payload — skip, mirrors apply_wal_entry */
@ -3729,6 +3731,21 @@ int apply_committed_entry(persist_t* p, uint32_t wal_type, uint64_t lsn,
lock(j, RTTI_LOCK_WRITE);
job_transition_result_t result =
apply_job_update_transition(j, (int)js->state);
if (result == JOB_TRANSITION_APPLIED) {
/* apply_job_update_transition() only carries {jobid, state} on
* this wire shape (see the doc comment above) and never touches
* start_time/finish_time/run_time itself — stamp them here,
* keyed on wal_type, mirroring what the richer
* apply_job_start_transition()/apply_job_finish_transition()
* cores do for their own (currently unreachable on this dispatch
* path) callers. */
if (wal_type == WAL_JOB_START) {
j->job.start_time = time(NULL);
} else if (wal_type == WAL_JOB_FINISH) {
j->job.finish_time = time(NULL);
j->job.run_time = (int)(j->job.finish_time - j->job.start_time);
}
}
unlock(j);
if (result != JOB_TRANSITION_APPLIED)