feat(resreq-maxtasks-wiring): add task-count derivation helper

Adds apply_job_task_count() to overwrite a standalone primary resreq's
minTasks/maxTasks with a job's real requested task count, no-op for
COMPOUND/ALTER nested primaries, with a defensive min>max clamp.
Closes the gap that lets scheduler-allocation's multi-host allocator
ever be reached (bug-1770). Wiring at live ingest call sites follows
in subsequent tasks.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
This commit is contained in:
Rongsong Shen 2026-09-06 03:26:32 +00:00
parent ecfb0a3c64
commit 799a71ecb1
No known key found for this signature in database
GPG key ID: 5BCDAAE0123E9FE2
10 changed files with 918 additions and 0 deletions

View file

@ -482,6 +482,40 @@ int parse_resreq(xce_resreq_t* resreq, char* data, var resmgr, char** errmsg)
return rc;
}
/*
* TaskCountWiring (resreq-maxtasks-wiring spec): overwrite a standalone
* primary resreq's minTasks/maxTasks with the job's real requested task
* count. A COMPOUND/ALTER resreq's nested primaries are left exactly as
* parse_resreq_primary() set them -- only the job's own top-level
* standalone primary is in scope (design.md's TaskCountWiring component;
* requirements 1.1, 1.2, 1.5, 3.3).
*
* Narrowing conversion note: mintasks/maxtasks are uint32_t, matching
* job_submit_data_t.mintasks/maxtasks (the caller's own source of truth);
* xce_primary_resreq_t.minTasks/maxTasks are plain int. This is a
* deliberate, one-time, acknowledged narrowing assignment -- safe for
* realistic task counts (well under INT_MAX) -- not a mismatch to "fix"
* by changing either type.
*/
void apply_job_task_count(xce_resreq_t* resreq, uint32_t mintasks,
uint32_t maxtasks)
{
if (resreq == NULL || resreq->tag != XCE_RESREQ_PRIMARY) {
return;
}
/* Defensive clamp: mirror convert.go's own clamp direction -- raise
* the max, never lower the min. The apiserver already guarantees
* mintasks <= maxtasks twice before a job reaches this layer; this is
* a redundant safety net, not a new job-rejecting error path. */
if (mintasks > maxtasks) {
maxtasks = mintasks;
}
resreq->u.primary->minTasks = (int)mintasks;
resreq->u.primary->maxTasks = (int)maxtasks;
}
void kill_primary_resreq(xce_primary_resreq_t* resreq)
{
int i;