fix(controller): persist a failed job's finish_time through WAL_JOB_FAIL (bug-1747)
The FAIL half of bug-1747: apply_job_fail_transition() stamps finish_time = time(NULL) at APPLY time, and WAL_JOB_FAIL carried no time, so a failed job's finish_time changed on every restart replay / standby catch-up, and a worker-reported failure found on rejoin could never keep the worker's own time. - WAL_JOB_FAIL's trailer grows to int32_t code + uint64_t finish_time (seconds). Decoded by length: 16 B (pre-bug-1754), 20 B (code only, bug-1754) and 28 B (code + time) all apply as before for what they carry. - record_job_fail_fact_at(c, job, failinfo, finish_time_ms), the fail-side sibling of record_job_finish_fact_at(): 0 = receipt time. on_job_fail() uses receipt time; resync's finishedFail branch passes the worker- reported finish_time_ms (Req 4.2 now holds for failures too). - Both apply seams adopt the wire time after the transition. 4 new tests in test_wal_job_fail_fact.c (3 RED before the fix) and the resync round-trip now asserts the worker-reported failure time (RED before). Controller suite 1232/0. Factstream HA soak (2 rounds, 60 jobs) PASS. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
21e72726f0
commit
1d6c04c28a
9 changed files with 349 additions and 68 deletions
|
|
@ -1523,17 +1523,18 @@ int persist_submit_job_start_fact(persist_t* p, uint64_t jobid, int state,
|
|||
int persist_submit_job_finish_fact(persist_t* p, uint64_t jobid, int state,
|
||||
uint64_t finish_time, int exit_code);
|
||||
|
||||
/* bug-1754: WAL_JOB_FAIL's deferred-apply sibling of
|
||||
/* bug-1754/bug-1747: WAL_JOB_FAIL's deferred-apply sibling of
|
||||
* persist_submit_job_fact_ex() -- header {jobid, 0, WAL_JOB_FAIL_STAGE_FLAG
|
||||
* | stage} followed by a fixed int32_t code (job_failinfo_t.code). A worker
|
||||
* reports every nonzero job exit as a FAIL, so for a MAIN-stage failure
|
||||
* `code` is the job's real exit code; apply_job_fail_exit_code() (job.c)
|
||||
* adopts it into job.exit_info at both apply seams. A payload of exactly
|
||||
* sizeof(wal_jobstate_t) (any WAL_JOB_FAIL entry written before this fix)
|
||||
* carries no code and leaves exit_info untouched. Returns the local-append
|
||||
* rc, same contract as persist_submit_job_fact(). */
|
||||
* | stage} followed by a fixed int32_t code (job_failinfo_t.code) and a
|
||||
* fixed uint64_t finish_time (seconds). A worker reports every nonzero job
|
||||
* exit as a FAIL, so for a MAIN-stage failure `code` is the job's real exit
|
||||
* code; apply_job_fail_exit_code() (job.c) adopts it into job.exit_info and
|
||||
* both apply seams adopt finish_time instead of apply-time time(NULL).
|
||||
* Older payloads decode by length: exactly sizeof(wal_jobstate_t) (before
|
||||
* bug-1754) carries neither; header + code (bug-1754) carries no time.
|
||||
* Returns the local-append rc, same contract as persist_submit_job_fact(). */
|
||||
int persist_submit_job_fail_fact(persist_t* p, uint64_t jobid, uint32_t stage,
|
||||
int code);
|
||||
int code, uint64_t finish_time);
|
||||
|
||||
/* bug-1679: fire XCE_CONTROLLER_HOOK_JOB's HOOK_DETAIL_JOB_UPDATE AFTER_EVENT
|
||||
* for a job whose durable WAL fact the caller has ALREADY appended itself
|
||||
|
|
|
|||
|
|
@ -45,10 +45,11 @@ typedef struct {
|
|||
* markers below. */
|
||||
#define WAL_JOB_FAIL_STAGE_FLAG 0x80000000u
|
||||
/* WAL_JOB_FAIL: _pad = WAL_JOB_FAIL_STAGE_FLAG | job_failinfo_t.stage,
|
||||
* optionally followed by an int32_t job_failinfo_t.code (bug-1754).
|
||||
* Apply time reconstructs a minimal job_failinfo_t from {jobid, stage,
|
||||
* code} and calls the real apply_job_fail_transition() against current
|
||||
* state, then apply_job_fail_exit_code() when the code is present. */
|
||||
* optionally followed by an int32_t job_failinfo_t.code (bug-1754) and a
|
||||
* uint64_t finish_time in seconds (bug-1747). Apply time reconstructs a
|
||||
* minimal job_failinfo_t from {jobid, stage, code} and calls the real
|
||||
* apply_job_fail_transition() against current state, then
|
||||
* apply_job_fail_exit_code() and adopts finish_time when present. */
|
||||
#define WAL_JOB_UPDATE_POSTDONE_MARK 1u
|
||||
/* WAL_JOB_UPDATE: _pad = WAL_JOB_UPDATE_POSTDONE_MARK marks a postdone
|
||||
* merge-fact (`state` is unused/0) — apply time calls
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue