feat(worker-rejoin-resync): reconcile unqueried job assignment (task 2.2)
Add record_job_reconcile_assign_fact(cluster_t*, job_t*, worker_t*, xce_job_state_entry_t*) to job.c: eagerly sets job->job.state to match the worker's reported outcome (mirroring dispatch_a_job_'s inline mutation, minus allocation fields -- there are none to set, job->job.allocs stays NULL per Requirement 4.8's documented scope). Calls the existing persist_submit_job_dispatch_fact(..., alloc=NULL) directly, then leaves the post-unlock persist_notify_job_updated_already_persisted() call to the caller, same convention as task 2.1. Fixes bug-1746 along the way: capnp_encode_joballoc() (persist.c, part of bug-1678's original fix, predates this feature) unconditionally dereferenced a NULL alloc -- never triggered before since dispatch_a_job_ (the only prior caller) always passes non-NULL. Fixed with a minimal guard producing a true zero-length buffer, matching apply_committed_entry's alloc_len > 0 decode gate exactly. This directly contradicted design.md's "tested, backward-compatible on both seams" claim (true only for decode, never actually exercised for encode) -- corrected in 4 places. 2 tests: applies-running-no-allocs-and-notifies, and a real restart- replay round trip (write to real disk, fresh persist_recover(), confirm jobid/host/state/NULL-allocs survive) -- proving this task's own "durably survives a restart replay" completion criterion literally, not just the in-memory mutation. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y4M5MJM2zK6nU43da267Ku
This commit is contained in:
parent
b7ed6de2a1
commit
ecfb4c942f
7 changed files with 550 additions and 3 deletions
|
|
@ -230,6 +230,26 @@ static int capnp_encode_joballoc(const xce_job_alloc_t* a, uint8_t** out,
|
|||
struct JobAlloc cja;
|
||||
JobAlloc_ptr jap;
|
||||
|
||||
/* worker-rejoin-resync task 2.2 (bug-1746): a NULL `a` means "no
|
||||
* allocation to carry" (record_job_reconcile_assign_fact()'s Req 4.8
|
||||
* case -- the controller genuinely never learned one). Produce a
|
||||
* genuinely zero-length buffer, NOT a capnp-encoded null-pointer JobAlloc
|
||||
* (what encode_JobAlloc_ptr()'s existing NULL branch would produce) --
|
||||
* persist_submit_job_dispatch_fact()'s caller (both apply seams,
|
||||
* apply_committed_entry()/apply_wal_entry()) discriminates "is there an
|
||||
* allocation to decode" purely by `alloc_len > 0`, so any non-zero
|
||||
* trailing byte count here would be misread as a real allocation to
|
||||
* decode. encode_JobAlloc() below has no NULL guard of its own (it
|
||||
* unconditionally dereferences `a` on its first line) -- this early
|
||||
* return is the one and only guard needed, matching the sole documented
|
||||
* call convention this function must now support (dispatch_a_job_()'s
|
||||
* own call site continues to always pass non-NULL, unaffected). */
|
||||
if (a == NULL) {
|
||||
*out = NULL;
|
||||
*out_len = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
capn_init_malloc(&c);
|
||||
cs = capn_root(&c).seg;
|
||||
encode_JobAlloc(cs, &cja, (xce_job_alloc_t*)a);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue