feat(resreq-maxtasks-wiring): wire fresh job-ingest to real task count
core_build_job() now calls apply_job_task_count() right after its resreq parse succeeds and before core_build_schedobj() reads the resreq's task count for resreq_fp — so a freshly-submitted multi-task job's dispatched primary resreq reflects its real requested min/max task count instead of the previous always-one-task hardcode. Tracked follow-up (not blocking, see tasks.md Implementation Notes): test_repl_breakpoints_inspection.c:735 hardcodes an incidental "Tasks: min = 0, max = 0" byproduct of a fixture's parse_select() failure; now correctly reports (1,1) per Requirement 1.2. Fix folded into task 5.1's full-suite regression pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XYdtrEWWZfKRRGED7raEYg
This commit is contained in:
parent
799a71ecb1
commit
0aadb49493
3 changed files with 107 additions and 1 deletions
|
|
@ -10,7 +10,7 @@
|
|||
- _Boundary: TaskCountWiring_
|
||||
|
||||
- [ ] 2. Core: wire the derived task count into live scheduler ingest paths and close the adjacent worker launch-type gap
|
||||
- [ ] 2.1 (P) Wire fresh job-ingest to apply the job's real requested task count
|
||||
- [x] 2.1 (P) Wire fresh job-ingest to apply the job's real requested task count
|
||||
- Call the new helper immediately after fresh job ingest's existing resource-request parse, using the job's real submitted minimum/maximum task count already in scope there
|
||||
- Observable: a job's dispatched resource request (the one the allocator actually reads) reflects the real requested task count immediately after fresh ingest, not the previous always-one-task default
|
||||
- _Requirements: 1.1_
|
||||
|
|
@ -87,3 +87,6 @@
|
|||
- Re-run the existing sequential and chaotic HA soak unmodified and confirm no new job loss, divergence, or liveness stall attributable to this feature
|
||||
- Observable: a clean soak pass recorded as this feature's HA non-regression evidence
|
||||
- _Requirements: 5.2_
|
||||
|
||||
## Implementation Notes
|
||||
- Task 2.1 surfaced an out-of-boundary side effect: `src/scheduler/testsuite/test_repl_breakpoints_inspection.c:735`'s `test_info_job_reports_state_queue_priority` hardcodes an incidental `"Tasks: min = 0, max = 0"` byproduct of its fixture's `parse_select()` failing on an unregistered `"type"` resource (which previously left the primary's task-count fields at memset-zero, before `apply_job_task_count()` existed). Now correctly overwritten to `(1,1)` per Requirement 1.2. Before task 5.1 is considered complete, change that assertion's expected substring to `"Tasks: min = 1, max = 1"`.
|
||||
|
|
|
|||
|
|
@ -256,6 +256,23 @@ static xce_sched_job_t* core_build_job(xce_sched_core_t* core, xce_job_t* j)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/* resreq-maxtasks-wiring task 2.1 (bug-1770, Req 1.1/1.2/1.5): the
|
||||
* just-parsed resreq's standalone primary carries whatever
|
||||
* parse_resreq_primary() left it at -- normally its own minTasks==
|
||||
* maxTasks==1 hardcode, but NOT unconditionally (a standalone primary
|
||||
* whose select clause fails to resolve against this core's resmgr
|
||||
* returns early and leaves minTasks/maxTasks at 0, e.g. some fixtures'
|
||||
* "type == any" against an empty resmgr) -- never the job's real
|
||||
* requested count either way. Overwrite it here unconditionally, using
|
||||
* the job's real submitted task count (j->submit->mintasks/maxtasks) --
|
||||
* the SAME source job->mintasks/maxtasks below is populated from, just
|
||||
* applied before core_build_schedobj() so the fingerprint it computes
|
||||
* off job->resreq (core_build_primary_schedobj()'s resreq_fp) reflects
|
||||
* the real value too, not whatever the just-parsed default happened to
|
||||
* be. No-ops for a COMPOUND/ALTER resreq (apply_job_task_count()'s own
|
||||
* tag gate) -- out of this task's boundary per design.md. */
|
||||
apply_job_task_count(job->resreq, j->submit->mintasks, j->submit->maxtasks);
|
||||
|
||||
job->schedobj = (xce_sched_obj_t*)calloc(1, sizeof(*(job->schedobj)));
|
||||
if (job->schedobj == NULL) {
|
||||
FREEUP(job->resreq);
|
||||
|
|
|
|||
|
|
@ -1545,6 +1545,90 @@ test_core_build_primary_schedobj_populates_resreq_fp_from_parsed_resreq(void)
|
|||
FREEUP(errmsg);
|
||||
}
|
||||
|
||||
/*
|
||||
* resreq-maxtasks-wiring task 2.1 (bug-1770, Req 1.1): a job's real
|
||||
* requested task count (job_submit_data_t.mintasks/maxtasks, already
|
||||
* correctly threaded onto xce_job_t.submit by the time a JOBS_PUSH event
|
||||
* reaches core_build_job()) must reach the standalone primary resreq the
|
||||
* allocator actually dispatches on (job->resreq->u.primary->minTasks/
|
||||
* maxTasks) -- not parse_resreq_primary()'s always-1 hardcoded default for
|
||||
* every freshly-parsed resreq. Before this fix, core_build_job() never
|
||||
* applied the new apply_job_task_count() helper (src/lib/resreq.c, task
|
||||
* 1), so a job submitted with e.g. -n 4,6 silently ingested with
|
||||
* minTasks==maxTasks==1, permanently unreachable by scheduler-allocation's
|
||||
* parjob_allocator.
|
||||
*/
|
||||
static void
|
||||
test_core_build_job_wires_real_task_count_into_resreq_primary(void)
|
||||
{
|
||||
xce_sched_core_t core;
|
||||
xce_core_event_t event;
|
||||
xce_job_t* job1;
|
||||
xce_sched_job_t* tracked;
|
||||
int rc;
|
||||
|
||||
fixture_init(&core, &g_full_plugin);
|
||||
|
||||
job1 = make_job(4160, JOB_STAT_PEND, "normal", "alice", "proj1");
|
||||
job1->submit->mintasks = 4;
|
||||
job1->submit->maxtasks = 6;
|
||||
|
||||
memset(&event, 0, sizeof(event));
|
||||
event.type = XCE_CORE_EVENT_JOBS_PUSH;
|
||||
event.u.jobs_push.njobs = 1;
|
||||
event.u.jobs_push.jobs = &job1;
|
||||
|
||||
rc = xce_core_push_events(&core, &event, 1);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
|
||||
tracked = sched_get_job(&core, 4160);
|
||||
TEST_ASSERT_NOT_NULL(tracked);
|
||||
TEST_ASSERT_NOT_NULL(tracked->resreq);
|
||||
TEST_ASSERT_EQUAL_INT(XCE_RESREQ_PRIMARY, tracked->resreq->tag);
|
||||
TEST_ASSERT_NOT_NULL(tracked->resreq->u.primary);
|
||||
|
||||
/* The fix under test: the standalone primary's minTasks/maxTasks must
|
||||
* reflect the job's real submitted task count, not parse_resreq_
|
||||
* primary()'s always-1 default. */
|
||||
TEST_ASSERT_EQUAL_INT(4, tracked->resreq->u.primary->minTasks);
|
||||
TEST_ASSERT_EQUAL_INT(6, tracked->resreq->u.primary->maxTasks);
|
||||
}
|
||||
|
||||
/* resreq-maxtasks-wiring task 2.1 (Req 1.2): a job submitted without an
|
||||
* explicit task count (make_job()'s default mintasks==maxtasks==1) must
|
||||
* continue to ingest with minTasks==maxTasks==1 -- zero behavioral change
|
||||
* for the common single-task case. */
|
||||
static void
|
||||
test_core_build_job_default_task_count_still_one(void)
|
||||
{
|
||||
xce_sched_core_t core;
|
||||
xce_core_event_t event;
|
||||
xce_job_t* job1;
|
||||
xce_sched_job_t* tracked;
|
||||
int rc;
|
||||
|
||||
fixture_init(&core, &g_full_plugin);
|
||||
|
||||
job1 = make_job(4161, JOB_STAT_PEND, "normal", "alice", "proj1");
|
||||
|
||||
memset(&event, 0, sizeof(event));
|
||||
event.type = XCE_CORE_EVENT_JOBS_PUSH;
|
||||
event.u.jobs_push.njobs = 1;
|
||||
event.u.jobs_push.jobs = &job1;
|
||||
|
||||
rc = xce_core_push_events(&core, &event, 1);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
|
||||
tracked = sched_get_job(&core, 4161);
|
||||
TEST_ASSERT_NOT_NULL(tracked);
|
||||
TEST_ASSERT_NOT_NULL(tracked->resreq);
|
||||
TEST_ASSERT_EQUAL_INT(XCE_RESREQ_PRIMARY, tracked->resreq->tag);
|
||||
TEST_ASSERT_NOT_NULL(tracked->resreq->u.primary);
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(1, tracked->resreq->u.primary->minTasks);
|
||||
TEST_ASSERT_EQUAL_INT(1, tracked->resreq->u.primary->maxTasks);
|
||||
}
|
||||
|
||||
/* Session-active rejection (Req 5 AC 8 / design.md precondition):
|
||||
* core_push_events() is only valid between sessions. */
|
||||
static void
|
||||
|
|
@ -1631,6 +1715,8 @@ int main(void)
|
|||
RUN_TEST(test_job_rusage_reconciles_mem_reservation_against_usage);
|
||||
RUN_TEST(test_config_push_decomposes_queues_resources_and_resvals);
|
||||
RUN_TEST(test_core_build_primary_schedobj_populates_resreq_fp_from_parsed_resreq);
|
||||
RUN_TEST(test_core_build_job_wires_real_task_count_into_resreq_primary);
|
||||
RUN_TEST(test_core_build_job_default_task_count_still_one);
|
||||
RUN_TEST(test_push_events_rejects_with_ebusy_when_session_active_no_mutation);
|
||||
RUN_TEST(test_push_events_null_core_returns_einval);
|
||||
RUN_TEST(test_push_events_null_events_with_positive_nevents_returns_einval);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue