fix(libxce): bug-1425 -- populate jobsubmit's *jobid on idempotent-replay success
xcecli_jobsubmit() could return rc>=0 (success) while leaving its *jobid output parameter at 0, reproducible under concurrent client load immediately after a fresh single-node election (~1-3/9 clients per round in the original repro). Root cause: XCECLI_FAILOVER_CALL's idempotent-replay rule (client_failover.c's xce_failover_classify) normalizes a retried resend that hits the server's EEXIST/inflight-collision rejection into overall success -- but that rejection reply carries no payload (handle_jobsubmit's EEXIST branches reply with RequestReply_data_nodata, job.c), so xce_jobsubmit() never populates *jobid on the winning attempt, nor did it on the earlier failing attempt (raw status was already negative). Adds jobsubmit_finalize_jobid() (client_internal.h/client.c), called from xcecli_jobsubmit() right after XCECLI_FAILOVER_CALL returns. Sets *jobid = submit->jobid (the client-generated id already on the wire -- the controller commits/echoes this exact value on every genuine success reply, bug-786/job.c:1439) whenever the overall call succeeded, otherwise leaves it untouched. Placed at the jobsubmit-specific wrapper layer, not the generic classify/decide layer, which stays fully generic. 3 new socket-free unit tests in test_libxce_retry.c (following this codebase's direct-state-simulation style for I/O-adjacent logic). Live-validated: full ASAN rebuild clean, libxce test suite 1/1 OK with all 3 new tests passing, and a 5-round/45-client concurrent-submit repro against a live single-node cluster showed 0 "no clean ack" failures (vs. 1-3/9 per round pre-fix). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
b33f8b68a6
commit
8a6c339a55
3 changed files with 95 additions and 2 deletions
|
|
@ -301,6 +301,44 @@ bool xcecli_ff_needs_reconnect(const api_ctx_t* ac);
|
|||
_ff_rc; \
|
||||
})
|
||||
|
||||
/*
|
||||
* jobsubmit_finalize_jobid (bug-1425) — guarantee xcecli_jobsubmit()'s *jobid
|
||||
* output param is populated on every overall success, including one the
|
||||
* XCecliRetryLoop's idempotent-replay rule (5.2, xce_failover_classify)
|
||||
* produced by normalizing an EEXIST/E_XCE_DUP_JOB reply from a RETRIED
|
||||
* resend into rc == 0.
|
||||
*
|
||||
* The gap: that normalization happens in the generic, type-erased
|
||||
* xcecli_retry_decide() layer, one level above xce_jobsubmit() (xceapi.c) —
|
||||
* which only ever copies the reply's jobid into *jobid on ITS OWN raw
|
||||
* non-negative status. The dup-rejection reply itself carries NO payload
|
||||
* (handle_jobsubmit's EEXIST/inflight-collision branches reply with
|
||||
* RequestReply_data_nodata, job.c), so *jobid is never written on the
|
||||
* winning (dup) attempt, and was never written on the earlier failing
|
||||
* attempt either (whose raw status was already negative). Caller-visible
|
||||
* result: xcecli_jobsubmit() returns 0 (success) with *jobid left at
|
||||
* whatever the caller initialized it to — reproduced live via a concurrent
|
||||
* 9-client submit burst immediately after a fresh single-node election
|
||||
* (~10.0-10.1s: one full 10s cbuf_timedget deadline on the failing first
|
||||
* attempt, then a fast dup-rejection reply on the resend).
|
||||
*
|
||||
* The job a successful submission belongs to is unambiguously the one THIS
|
||||
* client generated and put on the wire in the first place (submit->jobid —
|
||||
* job submission ids are client-generated; the controller commits/echoes
|
||||
* this exact value verbatim on every genuine success reply, bug-786,
|
||||
* job.c:1439's `jobid = submit->jobid`). So on ANY overall success this is
|
||||
* always the correct id to report, regardless of which attempt in the retry
|
||||
* sequence actually produced the winning rc. No socket/reply-decode
|
||||
* dependency — pure, and exposed non-static so it is unit-testable without
|
||||
* a live connection (test_libxce_retry.c).
|
||||
*
|
||||
* Called by xcecli_jobsubmit() (client.c) immediately after
|
||||
* XCECLI_FAILOVER_CALL returns. A no-op (jobid left untouched) when
|
||||
* overall_rc < 0 (the call genuinely failed) or jobid is NULL.
|
||||
*/
|
||||
void jobsubmit_finalize_jobid(uint64_t* jobid, uint64_t submitted_jobid,
|
||||
int overall_rc);
|
||||
|
||||
/*
|
||||
* xcecli_invoke_rejoin (task 4.1) — invoke the consumer rejoin hook once after a
|
||||
* successful reconnect/recovery and BEFORE the interrupted op resumes. Socket-
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue