fix(controller): WAL_JOB_CKPT loses job.ckptinfo on replay (bug-1691)

Same class as bug-1678/1687/1689/1690: on_job_ckpt() sets job.ckptinfo
(a plain char*, xstring-backed via makeString()) but is purely hook-driven
-- persist_job_hook_fun's default: arm only ever captured {jobid, state}
for WAL_JOB_CKPT. Neither apply seam restored ckptinfo after a restart.

Simplest of the five field-parity fixes: ckptinfo is a bare string, so no
capnp struct is needed -- mirrors WAL_QUEUE_CTRL's existing NUL-terminated-
string-after-header wire shape instead. Adoption is unconditional (not
gated on a state transition), matching bug-1690's precedent: a checkpoint
report never changes job.state. restore_job_ckpt_state() releases the
prior value via dereference() (the correct RTTI verb for an xstring), not
free().

Found by an /xce-audit-invariants controller pass (field-complete apply
parity dimension) -- the last of five sibling gaps found together
(bug-1678, bug-1687, bug-1689, bug-1690, this one). 7 new tests in
test_wal_job_ckpt_fact.c; RED/GREEN independently verified (5 genuine
pre-fix failures, 2 backward-compat tests correctly passed unchanged).
Full controller suite: 1167 tests, 2 failures -- both the pre-existing,
unrelated bug-1561 overlayfs sandbox flake. No ASAN findings.

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 12:29:10 +00:00
parent b2a8fe4ce9
commit 977607615f
No known key found for this signature in database
GPG key ID: 5BCDAAE0123E9FE2
7 changed files with 955 additions and 3 deletions

View file

@ -164,7 +164,29 @@ typedef enum {
* wrote -- freeing it via the capnp-generated
* free_JobRunusage_ptr() would corrupt the
* heap. */
WAL_JOB_CKPT = 9,
WAL_JOB_CKPT = 9, /* worker/scheduler-reported checkpoint info update.
* Payload is wal_jobstate_t{jobid, state, _pad=0}
* for any entry written before bug-1691, or one
* fired while job.ckptinfo was still NULL at write
* time; as of bug-1691 persist_job_hook_fun's
* HOOK_DETAIL_JOB_CKPT case appends a trailing
* NUL-terminated ckptinfo string whenever
* job.ckptinfo is non-NULL -- both apply seams
* treat a payload of exactly sizeof(wal_jobstate_t)
* as the legacy state-only shape for full backward
* compatibility. job.ckptinfo (xce/types.h) is a
* plain char* -- NOT the richer xce_ckptinfo_t/
* CheckpointInfo capnp struct used elsewhere for a
* different purpose -- and is xstring-backed
* (makeString(), xstring.c), so this mirrors
* WAL_QUEUE_CTRL's own [header][name
* NUL-terminated] wire shape rather than inventing
* a capnp wrapper for a bare string. Like
* WAL_JOB_RUSAGE, adoption on the
* apply_committed_entry() seam is NOT gated on a
* forward state transition -- a checkpoint report
* never changes job.state, so gating it the same
* way would make it dead code. */
WAL_JOB_PREASON = 10,
WAL_JOB_ARRAY_SUBMIT = 11, /* job-array-single-wal-record: one record for
* the whole array (base spec + index range),