fix(scheduler): a job's resource-request update takes effect for scheduling (bug-1773)

JOB_UPDATE (core_events.c) and the legacy sched_update_existing_job()
(schedobj.c) replaced job->resreq, but the allocator reads job->schedobj,
whose primary->resreq still pointed into the old parsed request -- so an
update never changed scheduling, for any field. The old primary was also
leaked (FREEUP only freed the container).

- core_bind_primary_resreq(): the resreq pointer + fingerprint computation
  factored out of core_build_primary_schedobj(), reused on update.
- core_rebind_schedobj_resreq(): primary->primary re-points in place and
  recomputes the fingerprint, keeping the schedobj's runtime state; a kind
  change rebuilds the kind-specific part (the old part is left alone, as
  nothing frees these today -- never a dangling pointer).
- JOB_UPDATE now applies the job's ingest-time task count to the new
  resreq, as core_build_job() does, so resreq-maxtasks-wiring Req 1.4 (an
  update never changes the dispatched task count) still holds -- it used to
  hold only because the update never reached the schedobj at all.
- schedobj.c's legacy path gets the same re-binding.

test_resource_request_update_never_changes_dispatched_task_count asserted
the old behaviour (schedobj keeps the ingest-time object); it now asserts
the schedobj uses the updated resreq and fingerprint while the task count
stays 4/7 (RED before). Scheduler suite 87/87.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rongsong Shen 2026-09-27 03:10:09 +00:00
parent 08e6c5e817
commit 41c3694d06
Signed by untrusted user: rshen
GPG key ID: 80533536B0EB651C
3 changed files with 97 additions and 32 deletions

View file

@ -954,34 +954,33 @@ test_resource_request_update_never_changes_dispatched_task_count(void)
TEST_ASSERT_NOT_NULL(sjob);
/* Sanity: the update DID genuinely replace job->resreq (not a silent
* no-op) -- job->resreq is now a fresh object showing parse_resreq_
* primary()'s own bare 1/1 default (JOB_UPDATE never calls apply_job_
* task_count(), design.md), confirming the assertions below are a
* meaningful immunity proof, not a vacuous one from an update that
* never took effect. */
* no-op). */
TEST_ASSERT_NOT_NULL(sjob->resreq);
TEST_ASSERT_EQUAL_INT(XCE_RESREQ_PRIMARY, sjob->resreq->tag);
TEST_ASSERT_FALSE_MESSAGE(
sjob->resreq->u.primary == ingest_time_primary,
"fixture: JOB_UPDATE must have replaced job->resreq with a "
"genuinely NEW object, not left the old one in place, for this "
"test's immunity assertion to be meaningful");
TEST_ASSERT_EQUAL_INT_MESSAGE(
1, sjob->resreq->u.primary->minTasks,
"fixture: job->resreq (freshly reparsed by JOB_UPDATE) must show "
"parse_resreq_primary()'s own bare default, confirming JOB_UPDATE "
"never calls apply_job_task_count()");
"genuinely NEW object");
/* ── The Req 1.4 proof: job->schedobj->u.primary->resreq (what the
* real allocator dispatch reads, scheduler.c:315) is the EXACT SAME
* object, with the EXACT SAME ingest-time task count, unaffected by
* the update -- because JOB_UPDATE never reassigns job->schedobj. ──── */
/* ── bug-1773: the allocator reads job->schedobj->u.primary->resreq
* (scheduler.c), so the update must re-point it at the NEW resreq --
* previously it kept the ingest-time object and the update never took
* effect for scheduling, for any field. ─────────────────────────────── */
TEST_ASSERT_NOT_NULL(sjob->schedobj);
TEST_ASSERT_EQUAL_INT(XCE_RESREQ_PRIMARY, sjob->schedobj->tag);
TEST_ASSERT_TRUE_MESSAGE(
sjob->schedobj->u.primary->resreq == ingest_time_primary,
"job->schedobj->u.primary->resreq must remain the EXACT SAME "
"object ingest originally built -- JOB_UPDATE must never rebuild "
"job->schedobj");
sjob->schedobj->u.primary->resreq == sjob->resreq->u.primary,
"bug-1773: job->schedobj must use the updated resreq, not the "
"ingest-time one");
TEST_ASSERT_EQUAL_UINT64_MESSAGE(
sjob->resreq->u.primary->rusage_hash,
sjob->schedobj->u.primary->resreq_fp.rusage_hash,
"bug-1773: the schedobj's resreq fingerprint must be recomputed "
"from the updated resreq");
/* ── Req 1.4 still holds: the dispatched task count stays the
* ingest-time value, not the update's own different mintasks/maxtasks
* nor parse_resreq_primary()'s bare 1/1 default. ─────────────────── */
TEST_ASSERT_EQUAL_INT_MESSAGE(
4, sjob->schedobj->u.primary->resreq->minTasks,
"Req 1.4: the job's dispatched minTasks must remain the "
@ -992,6 +991,8 @@ test_resource_request_update_never_changes_dispatched_task_count(void)
"Req 1.4: the job's dispatched maxTasks must remain the "
"ingest-time value (7), unaffected by the update's own different "
"maxtasks (1)");
TEST_ASSERT_EQUAL_INT(4, sjob->schedobj->u.primary->resreq_fp.min_tasks);
TEST_ASSERT_EQUAL_INT(7, sjob->schedobj->u.primary->resreq_fp.max_tasks);
}
/* ---------------------------------------------------------------------- */