fix(scheduler): check slots/rusage for primaries nested in COMPOUND/ALTER (bug-1768)

seqjob_prepare_allocation() and parjob_prepare_allocation() returned 0
whenever ctx->schedobj was not PRIMARY, so a primary nested in a COMPOUND
(or ALTER) job never had its slot count or rusage checked: a 5-task
compound dispatched onto 4 slots, and an ALTER job was "placed" by its
first alternative with an empty (0-slot) allocation. Both hooks now
compute the want from ctx->primary -- the primary being allocated, set by
sched_ctx_set_primary() for nested primaries and identical to
obj->u.primary->resreq for PRIMARY jobs.

sched_ctx_set_primary() also clears ctx->worker, so the next primary of a
compound computes its job-level want rather than a per-host want left
over from the previous primary's host (PRIMARY jobs set it once, with
worker already NULL).

Tests (real ingest + live allocator): a 5-task COMPOUND on 2x2 slots now
PENDs; "{5*{..}} || {1*{..}}" falls back to the second alternative (1
slot). Both failed before. Scheduler suite 87/87.

Follow-up logged: bug-1863 (multi-primary COMPOUND keeps only the last
primary's allocation).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rongsong Shen 2026-09-25 13:07:56 +00:00
parent 3991d6d4f5
commit b4e9a09925
Signed by untrusted user: rshen
GPG key ID: 80533536B0EB651C
4 changed files with 113 additions and 12 deletions

View file

@ -98,12 +98,11 @@ static int parjob_prepare_allocation(void* arg, xce_sched_ctx_t* ctx)
return -EINVAL; return -EINVAL;
} }
if (obj->tag != XCE_RESREQ_PRIMARY) { /* bug-1768: the primary being allocated is ctx->primary -- for a
/* we just take care of parallel job */ * PRIMARY obj it is obj->u.primary->resreq, and for a COMPOUND/ALTER
return 0; * obj it is the nested primary sched_ctx_set_primary() just set. Gating
} * on obj->tag skipped every nested primary's slots/rusage check. */
primary = ctx->primary;
primary = obj->u.primary->resreq;
if (primary == NULL) { if (primary == NULL) {
return -EINVAL; return -EINVAL;
} }

View file

@ -61,12 +61,11 @@ static int seqjob_prepare_allocation(void* arg, xce_sched_ctx_t* ctx)
return -EINVAL; return -EINVAL;
} }
if (obj->tag != XCE_RESREQ_PRIMARY) { /* bug-1768: the primary being allocated is ctx->primary -- for a
/* we just take care of sequence job */ * PRIMARY obj it is obj->u.primary->resreq, and for a COMPOUND/ALTER
return 0; * obj it is the nested primary sched_ctx_set_primary() just set. Gating
} * on obj->tag skipped every nested primary's slots/rusage check. */
primary = ctx->primary;
primary = obj->u.primary->resreq;
if (primary == NULL) { if (primary == NULL) {
return -EINVAL; return -EINVAL;
} }

View file

@ -531,6 +531,11 @@ void sched_ctx_fini(xce_sched_ctx_t* ctx)
void sched_ctx_set_primary(xce_sched_ctx_t* ctx, xce_primary_resreq_t* primary) void sched_ctx_set_primary(xce_sched_ctx_t* ctx, xce_primary_resreq_t* primary)
{ {
ctx->primary = primary; ctx->primary = primary;
/* bug-1768: a COMPOUND sets one primary after another on the same ctx;
* a host left over from the previous primary would make the prepare
* below compute a per-host want instead of this primary's job-level
* (shared) want */
ctx->worker = NULL;
/* scheduler-sim-live-allocation spec, task 2.1 (Core-Safe Allocation /* scheduler-sim-live-allocation spec, task 2.1 (Core-Safe Allocation
* Chain): ctx->sched is now honestly typed xce_sched_core_t* (set by * Chain): ctx->sched is now honestly typed xce_sched_core_t* (set by

View file

@ -701,6 +701,102 @@ static void test_alter_resreq_job_dispatches_via_real_allocator(void)
TEST_ASSERT_EQUAL_UINT64(jobid, decision->u.alloc->jobid); TEST_ASSERT_EQUAL_UINT64(