fix(scheduler): freeze mem-usage reconciliation for a RECONCILING/UNAVAIL worker too
sched_reconcile_host_resource_usage() -- the per-job mem-usage reconciliation driven by JOB_RUSAGE events -- was missing the same IS_WORKER_UNAVAIL/IS_WORKER_RECONCILING guard bug-1801 already added to sched_update_account(), the regular per-round charge/uncharge path. core_reset_workers() (bug-1751) deliberately freezes a RECONCILING worker's resInUse each round so a clean rebuild can restore it once the worker's real state is confirmed; bug-1801 extended that freeze to the charge path, but this third, independent resInUse-mutating path kept applying deltas regardless. Combined with core_events.c's JOBS_PUSH resync-merge RUN branch, which can replace a job's whole account object (fresh used=0.0 baseline) without uncharging the old one first -- safe only because the next clean rebuild is supposed to correct it, exactly the rebuild that stays skipped while RECONCILING -- a rusage tick landing in that window kept adding a full recharge on top of whatever was already frozen in the pool, with nothing to ever subtract it back out until the worker escaped RECONCILING for good or the job finished. Live-confirmed via a user's own multi-session replay trace: a job's "mem used" figure never self-corrected until the job finished. Added the identical guard, mirroring bug-1801 verbatim. RED confirmed by temporarily disabling it (exactly the new test failed, "Expected 0 Was 5", 76 others green); GREEN confirmed restored (77/77). bug-1815, follow-up to bug-1814/bug-1801. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RLSNJhNoULvVcUhXEFafQC
This commit is contained in:
parent
9369b87152
commit
dfabb67447
2 changed files with 95 additions and 0 deletions
|
|
@ -663,6 +663,74 @@ static void test_reconcile_host_resource_usage_null_args_return_einval(void)
|
|||
&(sched.core), NULL, &acct_entry, 0.0));
|
||||
}
|
||||
|
||||
/*
|
||||
* test_reconcile_host_resource_usage_skips_reconciling_or_unavail_worker -
|
||||
* bug-1814 follow-up: sched_reconcile_host_resource_usage() is a THIRD
|
||||
* w->resInUse-mutating path (alongside sched_charge_account()/sched_
|
||||
* uncharge_account(), both already gated by bug-1801) that must respect the
|
||||
* same "frozen while RECONCILING/UNAVAIL" invariant core_reset_workers()
|
||||
* (bug-1751) and sched_update_account() (bug-1801) already share -- a live
|
||||
* replay showed a job's "mem used" growing and never self-correcting until
|
||||
* the job finished, root-caused to this exact gap: a rusage tick landing
|
||||
* mid-resync kept mutating resInUse (and advancing acct_entry->used) on top
|
||||
* of a pool a concurrent per-round rebuild was deliberately leaving alone.
|
||||
*/
|
||||
static void
|
||||
test_reconcile_host_resource_usage_skips_reconciling_or_unavail_worker(void)
|
||||
{
|
||||
xce_scheduler_t sched;
|
||||
xce_sched_worker_t* w;
|
||||
xce_res_account_t acct_entry = { 0 };
|
||||
int rc;
|
||||
|
||||
memset(&sched, 0, sizeof(sched));
|
||||
setup_core_with_worker(&(sched.core), WORKER_NAME);
|
||||
w = sched_get_worker(&(sched.core), WORKER_NAME);
|
||||
TEST_ASSERT_NOT_NULL(w);
|
||||
|
||||
acct_entry.residx = MEM_RESIDX;
|
||||
acct_entry.reserved = MEM_RESERVED_MB;
|
||||
acct_entry.used = 0.0;
|
||||
|
||||
/* --- RECONCILING: no mutation at all, not to the pool, not to
|
||||
* acct_entry->used --- */
|
||||
w->status = XCE_WORKER_RECONCILING;
|
||||
rc = sched_reconcile_host_resource_usage(&(sched.core), w,
|
||||
&acct_entry, 9.0);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
TEST_ASSERT_EQUAL_FLOAT_MESSAGE(
|
||||
0.0f, get_worker_resinuse(w, MEM_RESIDX),
|
||||
"bug-1814: a RECONCILING worker's resInUse must not be touched by a "
|
||||
"rusage tick -- it is deliberately frozen until a clean per-round "
|
||||
"rebuild can reconcile it, same invariant bug-1801 already "
|
||||
"restores for the regular charge path");
|
||||
TEST_ASSERT_EQUAL_FLOAT_MESSAGE(
|
||||
0.0, (float)acct_entry.used,
|
||||
"the account's own used baseline must also stay untouched while "
|
||||
"skipped, so the eventual real reconcile call sees a clean delta");
|
||||
|
||||
/* --- UNAVAIL: same skip --- */
|
||||
w->status = XCE_WORKER_UNAVAIL;
|
||||
rc = sched_reconcile_host_resource_usage(&(sched.core), w,
|
||||
&acct_entry, 9.0);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
TEST_ASSERT_EQUAL_FLOAT_MESSAGE(
|
||||
0.0f, get_worker_resinuse(w, MEM_RESIDX),
|
||||
"bug-1814: an UNAVAIL worker's resInUse must not be touched either");
|
||||
|
||||
/* --- status clears: reconciliation resumes normally on the very next
|
||||
* call, not a permanent block --- */
|
||||
w->status = 0;
|
||||
rc = sched_reconcile_host_resource_usage(&(sched.core), w,
|
||||
&acct_entry, 9.0);
|
||||
TEST_ASSERT_EQUAL_INT(0, rc);
|
||||
TEST_ASSERT_EQUAL_FLOAT_MESSAGE(
|
||||
5.0, (float)acct_entry.used,
|
||||
"overage = 9 - 4 reserved = 5, applied on the first call once the "
|
||||
"worker is no longer RECONCILING/UNAVAIL");
|
||||
TEST_ASSERT_EQUAL_FLOAT(5.0, get_worker_resinuse(w, MEM_RESIDX));
|
||||
}
|
||||
|
||||
/*
|
||||
* test_reconcile_job_resource_usage_* - the rusages-report wrapper: resname
|
||||
* mapping ("memory_rss" on the wire -> "mem" in the account), the single-
|
||||
|
|
@ -1230,6 +1298,8 @@ int main(void)
|
|||
RUN_TEST(
|
||||
test_reconcile_host_resource_usage_repeated_growth_applies_only_delta);
|
||||
RUN_TEST(test_reconcile_host_resource_usage_null_args_return_einval);
|
||||
RUN_TEST(
|
||||
test_reconcile_host_resource_usage_skips_reconciling_or_unavail_worker);
|
||||
RUN_TEST(test_reconcile_job_resource_usage_maps_memory_rss_to_mem_account);
|
||||
RUN_TEST(test_reconcile_job_resource_usage_skips_multihost_jobs);
|
||||
RUN_TEST(test_reconcile_job_resource_usage_charges_unreserved_job_full_usage);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue