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:
parent
b2a8fe4ce9
commit
977607615f
7 changed files with 955 additions and 3 deletions
|
|
@ -1023,6 +1023,50 @@ int restore_job_rusage_state(cluster_t* c, uint64_t jobid, uint32_t state,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* bug-1691: WAL_JOB_CKPT's own sibling of restore_job_state() above, for
|
||||
* apply_wal_entry()'s standby catch-up / replicated-apply seam -- takes
|
||||
* ownership of an optional decoded ckptinfo string (NULL for a state-only
|
||||
* WAL_JOB_CKPT entry, e.g. one written before this fix, or one fired while
|
||||
* job.ckptinfo was still NULL at write time) so a promoted standby recovers
|
||||
* the checkpoint info the same way apply_committed_entry's WAL_JOB_CKPT
|
||||
* branch already restores it on the primary's own boot replay. Absolute
|
||||
* state overwrite, matching every other restore_job_*_state() sibling on
|
||||
* this seam -- no transition-result guarding happens on standby catch-up.
|
||||
*
|
||||
* `ckptinfo` is a TRANSIENT, caller-owned decode buffer (persist.c's
|
||||
* decode_ckptinfo()), NOT itself an xstring -- j->job.ckptinfo is
|
||||
* xstring-backed (makeString(), xstring.c), so adoption always goes through
|
||||
* makeString() (which copies its input into its own refcounted allocation)
|
||||
* and the existing value is released via dereference() (the RTTI verb),
|
||||
* never free()/FREEUP, mirroring on_job_ckpt()'s own
|
||||
* dereference(j->job.ckptinfo) call. `ckptinfo` itself is always freed here
|
||||
* before returning, regardless of whether adoption happened -- the value
|
||||
* actually stored in j->job.ckptinfo is always a freshly-made makeString()
|
||||
* copy, never the input pointer itself. */
|
||||
int restore_job_ckpt_state(cluster_t* c, uint64_t jobid, uint32_t state,
|
||||
char* ckptinfo)
|
||||
{
|
||||
job_t* j = NULL;
|
||||
lock(c->jobmgr, RTTI_LOCK_READ);
|
||||
key_find(c->jobmgr, (void*)jobid, (void**)&j);
|
||||
unlock(c->jobmgr);
|
||||
if (!j) {
|
||||
FREEUP(ckptinfo);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
lock(j, RTTI_LOCK_WRITE);
|
||||
j->job.state = (int)state;
|
||||
if (ckptinfo != NULL) {
|
||||
if (j->job.ckptinfo != NULL)
|
||||
dereference(j->job.ckptinfo);
|
||||
j->job.ckptinfo = makeString(ckptinfo);
|
||||
}
|
||||
unlock(j);
|
||||
FREEUP(ckptinfo);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_nslots_in_alloc(xce_host_alloc_t* alloc)
|
||||
{
|
||||
int i;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue