fix(scheduler): reset the per-host want at each primary's job-level prepare pass (bug-1883)
A COMPOUND/ALTER job's primaries share one ctx. The job-level prepare pass reset only alloc_req.shared, and the allocators append their slots want into alloc_req.host, so a later primary's host pre-filter (and a global limit on slots) read the previous primary's last per-host want: a 1-task primary after a 4-task one was filtered on slots:4 and pended although a host with its one free slot existed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
61f948e601
commit
c10f32ccd8
2 changed files with 58 additions and 0 deletions
|
|
@ -615,8 +615,15 @@ int plugin_prepare_allocation(xce_sched_core_t* core, xce_sched_ctx_t* ctx)
|
|||
}
|
||||
|
||||
if (ctx->worker == NULL) {
|
||||
/* bug-1883: the job-level pass also resets .host. The allocators'
|
||||
* hooks store this primary's job-level slots want there (the host
|
||||
* pre-filter and a global limit on slots read it); a COMPOUND's
|
||||
* previous primary left its last per-host want in .host, which the
|
||||
* append-only store would otherwise extend, not replace. */
|
||||
xvec_idval_fini(&(ctx->alloc_req.shared));
|
||||
xvec_idval_init(&(ctx->alloc_req.shared), 8);
|
||||
xvec_idval_fini(&(ctx->alloc_req.host));
|
||||
xvec_idval_init(&(ctx->alloc_req.host), 8);
|
||||
} else {
|
||||
xvec_idval_fini(&(ctx->alloc_req.host));
|
||||
xvec_idval_init(&(ctx->alloc_req.host), 8);
|
||||
|
|
|
|||
|
|
@ -875,6 +875,56 @@ static void test_failed_compound_leaves_no_partial_allocation(void)
|
|||
TEST_ASSERT_NULL(sjob->allocs);
|
||||
}
|
||||
|
||||
/*
|
||||
* bug-1883: a COMPOUND's primaries share one ctx, and the job-level prepare
|
||||
* pass reset only alloc_req.shared, so a later 1-task (seqjob) primary's
|
||||
* host pre-filter ran against the previous primary's per-host want with
|
||||
* slots:1 appended. Host A (4 slots) takes the 4-task primary, leaving
|
||||
* slots:4 behind; host B (1 slot) fits the 1-task primary, but the stale
|
||||
* slots:4 rejected it and the job pended.
|
||||
*/
|
||||
static void test_compound_later_seqjob_primary_ignores_previous_want(void)
|
||||
{
|
||||
xce_scheduler_t* sched = start_sched();
|
||||
xce_core_event_t ev;
|
||||
xce_worker_t* worker_ptrs[2];
|
||||
xce_decision_t* decision;
|
||||
int i;
|
||||
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = XCE_CORE_EVENT_QUEUE_UPDATE;
|
||||
ev.u.queue_update.queue = make_wire_queue("cmp5_queue", 1);
|
||||
TEST_ASSERT_TRUE(xce_core_push_events(&(sched->core), &ev, 1) >= 0);
|
||||
|
||||
worker_ptrs[0] = make_wire_worker(WORKER_A_NAME, 4.0f);
|
||||
worker_ptrs[1] = make_wire_worker(WORKER_B_NAME, 1.0f);
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = XCE_CORE_EVENT_WORKERS_PUSH;
|
||||
ev.u.workers_push.nworkers = 2;
|
||||
ev.u.workers_push.workers = worker_ptrs;
|
||||
TEST_ASSERT_TRUE(xce_core_push_events(&(sched->core), &ev, 1) >= 0);
|
||||
|
||||
decision = e2e_dispatch_resreq(sched, JOB_ID + 8, "cmp5_queue",
|
||||
"4*{rusage[mem=1]} + 1*{rusage[mem=1]}");
|
||||
TEST_ASSERT_NOT_NULL(decision);
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(XCE_DECISION_DISPATCH, decision->op,
|
||||
"the 1-task primary fits host B's free slot -- it must not be "
|
||||
"filtered on the 4-task primary's per-host want");
|
||||
TEST_ASSERT_EQUAL_FLOAT(5.0f,
|
||||
(float)e2e_alloc_total_slots(decision->u.alloc));
|
||||
TEST_ASSERT_EQUAL_INT(2, decision->u.alloc->nhosts);
|
||||
for (i = 0; i < decision->u.alloc->nhosts; i++) {
|
||||
xce_job_alloc_t one = *(decision->u.alloc);
|
||||
|
||||
one.nhosts = 1;
|
||||
one.allocs = &(decision->u.alloc->allocs[i]);
|
||||
TEST_ASSERT_EQUAL_FLOAT(
|
||||
(strcmp(decision->u.alloc->allocs[i]->host, WORKER_A_NAME) == 0)
|
||||
? 4.0f : 1.0f,
|
||||
(float)e2e_alloc_total_slots(&one));
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
UNITY_BEGIN();
|
||||
|
|
@ -887,5 +937,6 @@ int main(void)
|
|||
RUN_TEST(test_compound_allocation_includes_every_primary);
|
||||
RUN_TEST(test_compound_primaries_on_same_host_merge);
|
||||
RUN_TEST(test_failed_compound_leaves_no_partial_allocation);
|
||||
RUN_TEST(test_compound_later_seqjob_primary_ignores_previous_want);
|
||||
return UNITY_END();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue