fix(scheduler): re-derive worker/shared resource used-totals on snapshot load
xce_sched_core_load_snapshot() left every restored worker's resInUse (and core->sharedInUse) at zero -- zarray_init() in the workers- restoration block clears them, and nothing re-summed against the restored job allocations. A caller inspecting worker/resource state between a snapshot load and the first scheduling session (e.g. the REPL's `info worker` before the first `step`) saw a misleadingly empty "used" total for jobs already known to be RUNNING. Added one sched_account_jobs(core) call after workers/jobs are restored -- a correct idempotent-from-zero re-derivation using the same sched_charge_account() chain the next real session would use anyway. Must not be called again without an intervening reset (accumulates, does not assign) -- verified no such double-call exists in this change. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019QxaSzHRB7pmYFp4cVnwkA
This commit is contained in:
parent
42fdeb6669
commit
14f95b06fe
5 changed files with 309 additions and 48 deletions
|
|
@ -127,9 +127,12 @@
|
|||
* Discriminating-power note (validated live, not merely asserted): this
|
||||
* test was run against a deliberately-reverted tree (task 3's
|
||||
* sim_reconcile_unaccounted_jobs() call site temporarily removed from
|
||||
* sim_loop.c) and confirmed to fail -- specifically, g_round_resinuse[0]
|
||||
* read back 0.0 instead of 10.0 (jobs A/C never reconciled, so
|
||||
* sched_account_jobs()'s JOB_IS_RUN()+accounts!=NULL guard skipped them),
|
||||
* sim_loop.c) and confirmed to fail -- specifically, the array slot for
|
||||
* round 1's own resInUse snapshot (g_round_resinuse[1] as of task 4.6's
|
||||
* added load-time slot at index 0; g_round_resinuse[0] at the time of this
|
||||
* historical run, before that slot existed) read back 0.0 instead of 10.0
|
||||
* (jobs A/C never reconciled, so sched_account_jobs()'s
|
||||
* JOB_IS_RUN()+accounts!=NULL guard skipped them),
|
||||
* and the direct sched_check_host_satisfy() capacity-awareness proof below
|
||||
* WRONGLY granted a hypothetical job wanting JOBB_WANT (5.0) against sim's
|
||||
* post-replay state -- then confirmed to pass again once the call site was
|
||||
|
|
@ -1211,12 +1214,23 @@ test_contention_parity_matches_real_normal_mode_recording_round_by_round(
|
|||
g_capturing = 0;
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
|
||||
/* 3, not 2: scheduler-snapshot-job-allocation task 4.6 added ONE extra
|
||||
* sched_account_jobs() call, inside xce_sched_core_load_snapshot()
|
||||
* itself (snapshot_load.c), which runs during sched_sim_driver_start()
|
||||
* -- strictly before either replayed round -- so it lands at
|
||||
* g_round_resinuse[0], pushing round 1/round 2's own snapshots to
|
||||
* indices 1/2 (see the shifted indices below). This fixture's initial
|
||||
* snapshot has no RUN-state jobs yet (A/B/C/D are all dispatched via
|
||||
* recorded round decisions, never present at the replay's own starting
|
||||
* snapshot), so this extra call charges nothing -- g_round_resinuse[0]
|
||||
* is expected to read 0.0, not a real round observation. */
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(
|
||||
2, g_round_idx,
|
||||
3, g_round_idx,
|
||||
"sched_account_jobs() must have been called exactly once per "
|
||||
"replayed round (2 rounds in this fixture) -- if this isn't 2, "
|
||||
"the round-boundary snapshot mechanism itself is miscounting, "
|
||||
"independent of the parity assertions below");
|
||||
"replayed round (2 rounds in this fixture) PLUS once at snapshot- "
|
||||
"load time (task 4.6) -- if this isn't 3, either the round-boundary "
|
||||
"snapshot mechanism itself is miscounting, or the load-time call "
|
||||
"is missing/duplicated, independent of the parity assertions below");
|
||||
|
||||
/* ==================================================================
|
||||
* Part C: the actual parity assertions
|
||||
|
|
@ -1224,15 +1238,16 @@ test_contention_parity_matches_real_normal_mode_recording_round_by_round(
|
|||
|
||||
/* Requirement 1.4: sim mode's round-by-round resInUse must match what
|
||||
* the real normal-mode recording observed at the SAME point -- not
|
||||
* just at the final round. */
|
||||
* just at the final round. Indices start at 1, not 0: index 0 is the
|
||||
* load-time call above, not a round. */
|
||||
TEST_ASSERT_EQUAL_FLOAT_MESSAGE(
|
||||
normal_resinuse_after_round1, g_round_resinuse[0],
|
||||
normal_resinuse_after_round1, g_round_resinuse[1],
|
||||
"round 1: simulation mode's reconciled resInUse must byte-for-byte "
|
||||
"match what the real normal-mode recording actually observed after "
|
||||
"its own round 1 (jobs A+C dispatched, consuming 10.0 of 10.0 "
|
||||
"total capacity)");
|
||||
TEST_ASSERT_EQUAL_FLOAT_MESSAGE(
|
||||
normal_resinuse_after_round2, g_round_resinuse[1],
|
||||
normal_resinuse_after_round2, g_round_resinuse[2],
|
||||
"round 2: simulation mode's resInUse must still match normal "
|
||||
"mode's -- the already-consumed capacity must PERSIST correctly "
|
||||
"into a SECOND round, not just the first");
|
||||
|
|
@ -1625,30 +1640,43 @@ test_contention_capacity_check_correct_across_multi_round_dispatch_confirm_gap(
|
|||
run_multiround_contention_fixture(&sim5, 5, "_mr5");
|
||||
g_capturing = 0;
|
||||
|
||||
/* 6, not 5: scheduler-snapshot-job-allocation task 4.6's extra
|
||||
* snapshot-load-time sched_account_jobs() call (see the sibling test
|
||||
* above for the full rationale) lands at g_round_resinuse[0], pushing
|
||||
* every round's own snapshot to index+1 below. This fixture's initial
|
||||
* snapshot also has "worker-a" registered but no RUN-state jobs yet
|
||||
* (same fixture_init_seed() as the sibling test), so index 0 reads
|
||||
* 0.0. */
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(
|
||||
5, g_round_idx,
|
||||
6, g_round_idx,
|
||||
"sched_account_jobs() must have been called exactly once per "
|
||||
"replayed round (5 rounds in this fixture)");
|
||||
"replayed round (5 rounds in this fixture) PLUS once at "
|
||||
"snapshot-load time (task 4.6)");
|
||||
|
||||
TEST_ASSERT_EQUAL_FLOAT_MESSAGE(
|
||||
0.0f, g_round_resinuse[0],
|
||||
"snapshot load: no RUN-state jobs exist yet at the replay's own "
|
||||
"starting snapshot -- resInUse must read 0.0 (task 4.6's own "
|
||||
"load-time call, not a replayed round)");
|
||||
TEST_ASSERT_EQUAL_FLOAT_MESSAGE(
|
||||
0.0f, g_round_resinuse[1],
|
||||
"round 1: A/C dispatched but still PEND -- resInUse must read 0.0, "
|
||||
"not fabricated or prematurely charged (Requirement 1.2)");
|
||||
TEST_ASSERT_EQUAL_FLOAT_MESSAGE(
|
||||
0.0f, g_round_resinuse[1],
|
||||
0.0f, g_round_resinuse[2],
|
||||
"round 2: idle filler round -- the deferred-zero state from round "
|
||||
"1 must persist, not drift, across an EXTRA round");
|
||||
TEST_ASSERT_EQUAL_FLOAT_MESSAGE(
|
||||
0.0f, g_round_resinuse[2],
|
||||
0.0f, g_round_resinuse[3],
|
||||
"round 3: a SECOND idle filler round -- still 0.0, widening the "
|
||||
"gap before A's confirmation below to genuinely several rounds");
|
||||
TEST_ASSERT_EQUAL_FLOAT_MESSAGE(
|
||||
JOBA_WANT, g_round_resinuse[3],
|
||||
JOBA_WANT, g_round_resinuse[4],
|
||||
"round 4: job A's JOB_STATE_CHANGE to RUN just replayed, 3 rounds "
|
||||
"after A's own round-1 DISPATCH -- resInUse must now reflect A's "
|
||||
"8.0 usage (Requirement 1.3), and ONLY A's -- job C is still PEND");
|
||||
TEST_ASSERT_EQUAL_FLOAT_MESSAGE(
|
||||
WORKER_CAPACITY, g_round_resinuse[4],
|
||||
WORKER_CAPACITY, g_round_resinuse[5],
|
||||
"round 5: job C's JOB_STATE_CHANGE to RUN just replayed, 4 rounds "
|
||||
"after C's own round-1 DISPATCH (a DIFFERENT separation than A's 3 "
|
||||
"rounds, proving this isn't a single-job coincidence) -- resInUse "
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue