fix(scheduler): a failed COMPOUND leaves no partial allocation (bug-1766)
When a later primary of a COMPOUND failed, sched_make_compound_alloc_
still moved the earlier primaries' merged allocation onto the schedobj,
so a job decided PEND carried a partial allocation that the next
successful round overwrote and leaked. The merged allocation is now
published only when every primary succeeds and freed otherwise. No
resource-state rollback is needed: the primaries' uncommitted debits live
in ctx.uncomitted, per-attempt scratch freed by sched_ctx_fini(), and
schedobj->alloc's only reader is the dispatch path. The ALTER loop's
per-alternative cleanup (added with bug-1769) is now redundant and removed.
Test: "1*{..} + 5*{..}" on 2x2 slots PENDs with schedobj->alloc NULL
(held the first primary's allocation before). Scheduler suite 87/87.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b63bb581d4
commit
601b245e5a
2 changed files with 39 additions and 7 deletions
|
|
@ -315,8 +315,17 @@ static int sched_make_compound_alloc_(xce_sched_core_t* core,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* bug-1766: a compound is allocated only if EVERY primary is -- when a
|
||||||
|
* later primary fails, drop the earlier primaries' allocation instead
|
||||||
|
* of leaving a partial one on obj. Their uncommitted debits live in
|
||||||
|
* ctx.uncomitted, per-attempt scratch freed by sched_ctx_fini(), so
|
||||||
|
* nothing else needs undoing. */
|
||||||
if (merged != NULL) {
|
if (merged != NULL) {
|
||||||
MOVE_PTR(obj->alloc, merged);
|
if (rc > 0) {
|
||||||
|
MOVE_PTR(obj->alloc, merged);
|
||||||
|
} else {
|
||||||
|
free_JobAlloc_ptr(&merged);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx.reason != NULL) {
|
if (ctx.reason != NULL) {
|
||||||
|
|
@ -356,12 +365,6 @@ static int sched_make_alter_allocation(xce_sched_core_t* core,
|
||||||
if (rc > 0) {
|
if (rc > 0) {
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* a failed alternative may leave a partial allocation (no
|
|
||||||
* rollback yet, bug-1766) -- drop it before trying the next one */
|
|
||||||
if (obj->alloc != NULL) {
|
|
||||||
free_JobAlloc_ptr(&(obj->alloc));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FIXME: merge pend reason from all children */
|
/* FIXME: merge pend reason from all children */
|
||||||
|
|
|
||||||
|
|
@ -847,6 +847,34 @@ static void test_compound_primaries_on_same_host_merge(void)
|
||||||
"entry");
|
"entry");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* bug-1766: when a later primary of a COMPOUND fails, the earlier
|
||||||
|
* primaries' (merged) allocation was still moved onto the schedobj, so a
|
||||||
|
* job reported PEND carried a partial allocation that the next successful
|
||||||
|
* round overwrote and leaked. A failed compound must leave no allocation
|
||||||
|
* (its uncommitted debits are per-attempt scratch, freed with the ctx).
|
||||||
|
*/
|
||||||
|
static void test_failed_compound_leaves_no_partial_allocation(void)
|
||||||
|
{
|
||||||
|
xce_scheduler_t* sched = start_sched();
|
||||||
|
xce_decision_t* decision;
|
||||||
|
xce_sched_job_t* sjob;
|
||||||
|
|
||||||
|
e2e_push_queue_and_workers(sched, "cmp4_queue");
|
||||||
|
decision = e2e_dispatch_resreq(sched, JOB_ID + 7, "cmp4_queue",
|
||||||
|
"1*{rusage[mem=1]} + 5*{rusage[mem=1]}");
|
||||||
|
TEST_ASSERT_NOT_NULL(decision);
|
||||||
|
TEST_ASSERT_EQUAL_INT_MESSAGE(XCE_DECISION_PEND, decision->op,
|
||||||
|
"the 5-task primary cannot fit on 4 slots");
|
||||||
|
|
||||||
|
sjob = sched_get_job(&(sched->core), JOB_ID + 7);
|
||||||
|
TEST_ASSERT_NOT_NULL(sjob);
|
||||||
|
TEST_ASSERT_NULL_MESSAGE(sjob->schedobj->alloc,
|
||||||
|
"a failed compound must not keep the first primary's partial "
|
||||||
|
"allocation");
|
||||||
|
TEST_ASSERT_NULL(sjob->allocs);
|
||||||
|
}
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
UNITY_BEGIN();
|
UNITY_BEGIN();
|
||||||
|
|
@ -858,5 +886,6 @@ int main(void)
|
||||||
RUN_TEST(test_alter_falls_back_to_alternative_that_fits);
|
RUN_TEST(test_alter_falls_back_to_alternative_that_fits);
|
||||||
RUN_TEST(test_compound_allocation_includes_every_primary);
|
RUN_TEST(test_compound_allocation_includes_every_primary);
|
||||||
RUN_TEST(test_compound_primaries_on_same_host_merge);
|
RUN_TEST(test_compound_primaries_on_same_host_merge);
|
||||||
|
RUN_TEST(test_failed_compound_leaves_no_partial_allocation);
|
||||||
return UNITY_END();
|
return UNITY_END();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue