feat(scheduler-sim-diagnostics): display per-host usage for host-scoped limits

repl_print_limit_usage_per_host() fans a single host-scoped xce_vres_t out
across every worker with a populated capacity entry at that vres's shared
resource index (w->resvals[idx]/w->resInUse[idx]), rather than constructing
any per-host vres object -- matching design.md's (xce_vres_t, worker) pair
cardinality model exactly. A worker with no populated capacity entry produces
no line at all (not a fabricated zero); a worker with capacity but no
charged usage shows accounted=0. Uses the corrected idx<0 + .caps-only
bounds-check pattern task 3.1 established, avoiding bug-1824's latent
imprecision. This closes out all of Requirement 3 (3.1-3.4).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RLSNJhNoULvVcUhXEFafQC
This commit is contained in:
Rongsong Shen 2026-09-17 13:22:12 +00:00
parent 716abe121f
commit 4f60f148fb
No known key found for this signature in database
GPG key ID: 5BCDAAE0123E9FE2
3 changed files with 424 additions and 10 deletions

View file

@ -1246,14 +1246,122 @@ static void repl_print_limit_host_scope_note_if_implicit(
}
/*
* repl_print_limit_usage - task 3.1, Requirement 1.4/2.1/2.2/2.3/3.3:
* prints `limit`'s live accounted usage against its configured capacity,
* one line per distinct GLOBAL/SHARED xce_vres_t instance found in
* limit_get_usage_instances() whose `->limit` pointer-equals `limit`.
* A host-scoped instance (`vres->vres` not flagged GLOBAL/SHARED) is
* task 3.2's job and is silently skipped here, per design.md's own
* per-instance filter ("skip -- do not display -- any per-host-scoped
* vres in this task").
* repl_print_limit_usage_per_host - task 3.2, Requirement 1.4/2.1/2.2/2.3/
* 3.1/3.2/3.4: for ONE host-scoped xce_vres_t (`vres->vres` NOT flagged
* GLOBAL/SHARED), prints one usage line per worker that has a populated
* capacity entry at `vres->vres->idx` in its OWN resvals zarray --
* design.md's Data Models section: "a displayed 'instance' is therefore a
* (xce_vres_t, worker) pair when host-scoped." There is exactly ONE
* xce_vres_t per matched (user, queue, project) combination regardless of
* host-scoping (build_vres_name(), xcelimit.c, never encodes host) -- this
* function does NOT create a vres per host; it fans a SINGLE vres out over
* every worker by reading each worker's own resvals/resInUse at the SAME
* vres->vres->idx.
*
* Existence-of-instance is gated ONLY on a worker's CAPACITY entry
* (w->resvals[idx]) being present -- never on its in-use entry
* (w->resInUse[idx]), the same existence-vs-zero-usage distinction
* repl_print_limit_usage()'s own GLOBAL/SHARED branch below establishes,
* applied per worker here: a worker with a populated capacity but no
* in-use entry yet gets a genuine accounted=0 line; a worker with no
* populated capacity entry at all (this limit's shadow resource has never
* been written for that host -- the host is configured but untouched) gets
* no line at all, never a fabricated zero (Requirement 2.2 at per-host
* granularity).
*
* Bounds safety mirrors repl_print_limit_usage()'s own corrected pattern
* (bug-1824 is the still-unfixed older mistake in repl_info_resource_one(),
* NOT repeated here): relies on zarray_get()/zarray_get_ex()'s own
* `.caps`-based bound rather than a `.count`-based pre-check (imprecise for
* a sparsely-populated zarray) -- no manual upper-bound pre-check is
* performed at all, matching the GLOBAL/SHARED branch's own omission of
* one.
*
* Returns the number of lines printed (0 if no worker has a populated
* capacity entry at this vres's idx).
*/
static int repl_print_limit_usage_per_host(xce_sched_sim_driver_t* sim,
xce_vres_t* vres)
{
iterator_t it;
int rc;
int n_shown = 0;
if (vres->vres->idx < 0) {
/* No capacity index allocated at all -- this vres has never
* actually been evaluated to a usable capacity on any host; do not
* fabricate a display for it (Requirement 2.2). */
return 0;
}
rc = iter_make(&it, sim->core.workers);
if (rc < 0) {
return 0;
}
for (;;) {
xce_sched_worker_t* w = NULL;
xce_val_t* cap_v = NULL;
rc = iter_next(&it, (void**)&w);
if ((rc < 0) || (rc == RTTI_ITER_NO_DATA) || (w == NULL)) {
break;
}
cap_v = (xce_val_t*)zarray_get(&(w->resvals), vres->vres->idx);
if ((cap_v == NULL) || (cap_v->which != Value_u_fvalue)) {
/* This worker's capacity entry for this vres does not exist --
* the limit has never been evaluated against a job on this
* host. Must not appear as a line at all (Observable: "a third
* configured-but-untouched host shows no line at all"). */
continue;
}
{
xce_val_t* used_v = NULL;
double used = 0.0;
char value_str[80];
char used_str[80];
used_v = (xce_val_t*)zarray_get(&(w->resInUse), vres->vres->idx);
if ((used_v != NULL) && (used_v->which == Value_u_fvalue)) {
used = used_v->u.fvalue;
}
/* used_v == NULL (or wrong-typed) here is genuine zero usage on
* this host, never "no instance" -- the capacity check above
* already gated existence for this worker; existence must not
* be re-derived from this read. */
repl_format_consumable_value_and_used(
repl_current_units_mode(sim), vres->vres->name,
cap_v->u.fvalue, used, value_str, sizeof(value_str),
used_str, sizeof(used_str));
/* Requirement 3.4: label with BOTH the scope values already
* encoded in the vres's own name (user/queue/project, via
* build_vres_name()) AND this worker's own hostname, so each
* per-host line is distinguishable from every other. */
fprintf(stdout,
"[sim] usage %s host=%s: accounted=%s capacity=%s\n",
vres->vres->name, w->name, used_str, value_str);
}
n_shown++;
}
iter_destroy(&it);
return n_shown;
}
/*
* repl_print_limit_usage - task 3.1/3.2, Requirement 1.4/2.1/2.2/2.3/3.1/
* 3.2/3.3/3.4: prints `limit`'s live accounted usage against its configured
* capacity, one section per distinct xce_vres_t instance found in
* limit_get_usage_instances() whose `->limit` pointer-equals `limit`. A
* GLOBAL/SHARED vres prints exactly one line (this function's own inline
* logic below); a host-scoped vres (`vres->vres` not flagged GLOBAL/
* SHARED) fans out into zero or more per-host lines via
* repl_print_limit_usage_per_host() above (task 3.2).
*
* Existence-of-instance is gated ONLY on the matched vres's CAPACITY entry
* (core.sharedInst[idx]) being present -- never on the in-use entry
@ -1300,7 +1408,13 @@ static void repl_print_limit_usage(xce_sched_sim_driver_t* sim,
}
if ((vres->vres->flags & (XCE_RESF_GLOBAL | XCE_RESF_SHARED)) == 0) {
/* Host-scoped instance -- task 3.2's branch, not this one. */
/* Host-scoped instance (task 3.2): fan out over every worker
* with a populated capacity entry at this vres's shared idx --
* see repl_print_limit_usage_per_host()'s own doc comment.
* There is still exactly ONE xce_vres_t here; the fan-out
* happens entirely inside that helper by reading each worker's
* OWN resvals/resInUse zarrays. */
n_shown += repl_print_limit_usage_per_host(sim, vres);
continue;
}

View file

@ -598,6 +598,97 @@ static void put_shared_fvalue(zarray_t* array, int idx, double val)
TEST_ASSERT_TRUE(rc >= 0);
}
/*
* instantiate_and_register_host_vres - task 3.2's own fixture, mirroring
* instantiate_and_register_global_vres() above but for a HOST-SCOPED
* xce_vres_t: drives the real, already-linked instantiate_limit()
* (xcelimit.c) against `matched_limit` the same way, then key_add()s it
* into `plugin`'s privdata->instances.
*
* `matched_limit` must be configured the way add_configured_percent_limit()
* shapes it (percent-based, on a resource register_per_host_resource()
* registered as non-GLOBAL/non-SHARED) so is_host_based_limit() returns > 0
* and instantiate_limit()'s internal `shared` local stays 0 -- add_vres()
* then flags the resulting vres->vres XCE_RESF_DEFAULT_HOST (never GLOBAL/
* SHARED), the exact case task 3.2's own scope covers.
*/
static xce_vres_t* instantiate_and_register_host_vres(
xce_sched_sim_driver_t* sim, xce_sched_plugin_t* plugin,
xce_limit_t* matched_limit)
{
limit_plugin_data_t* privdata = (limit_plugin_data_t*)plugin->data;
xce_vres_t* vres = NULL;
int rc;
rc = instantiate_limit(&vres, sim->core.resmgr, matched_limit, "", "",
"");
TEST_ASSERT_TRUE(rc >= 0);
TEST_ASSERT_NOT_NULL(vres);
TEST_ASSERT_NOT_NULL(vres->vres);
TEST_ASSERT_TRUE(vres->vres->idx >= 0);
TEST_ASSERT_TRUE_MESSAGE(
(vres->vres->flags & (XCE_RESF_GLOBAL | XCE_RESF_SHARED)) == 0,
"fixture sanity: this helper only ever produces a host-scoped "
"(non-GLOBAL/non-SHARED) vres -- a GLOBAL/SHARED vres would mean "
"the fixture's own limit shape drifted from "
"add_configured_percent_limit()'s implicit-host-scope contract");
rc = key_add(privdata->instances, (void*)vres->vres->name, (void*)vres);
TEST_ASSERT_TRUE(rc >= 0);
return vres;
}
/*
* build_bare_worker - registers a real xce_sched_worker_t named `hostname`
* into `sim`'s core via the real XCE_CORE_EVENT_WORKERS_PUSH path
* (core_events.c's core_upsert_worker()/core_build_worker(), the exact
* production path test_repl_resource_table.c's own
* build_worker_with_one_numeric_resource() already drives) -- this
* zarray_init()s a real resvals/resInUse pair on the new worker, each with
* a real killer (kill_sched_resval, stubbed above), so this file's own
* put_shared_fvalue() can safely zarray_put() directly into a returned
* worker's OWN resvals/resInUse afterward (task 3.2's fan-out reads
* exactly those two zarrays per worker) without this fixture needing to
* drive the real put_resvals()/update_limit_res_value() (resource.c,
* deliberately not linked into this TU, see this file's own header
* comment). No wire resvals are carried on the push itself
* (nresvals_to_sched=0) -- this fixture only needs the worker to exist with
* its own zarrays ready for a direct put_shared_fvalue() write at the
* limit's own shadow-resource idx.
*/
static xce_sched_worker_t* build_bare_worker(xce_sched_sim_driver_t* sim,
const char* hostname)
{
xce_worker_t* wire_worker;
xce_worker_t* worker_ptrs[1];
xce_core_event_t events[1];
int rc;
wire_worker = (xce_worker_t*)calloc(1, sizeof(*wire_worker));
TEST_ASSERT_NOT_NULL(wire_worker);
wire_worker->name = (char*)hostname;
wire_worker->status = 0;
wire_worker->nresvals_to_sched = 0;
wire_worker->resvals_to_sched = NULL;
worker_ptrs[0] = wire_worker;
memset(events, 0, sizeof(events));
events[0].type = XCE_CORE_EVENT_WORKERS_PUSH;
events[0].u.workers_push.nworkers = 1;
events[0].u.workers_push.workers = worker_ptrs;
rc = xce_core_push_events(&(sim->core), events, 1);
TEST_ASSERT_EQUAL_INT(0, rc);
{
xce_sched_worker_t* w
= sched_get_worker(&(sim->core), (char*)hostname);
TEST_ASSERT_NOT_NULL(w);
return w;
}
}
/* ---------------------------------------------------------------------- */
/* Tests */
/* ---------------------------------------------------------------------- */
@ -1200,6 +1291,207 @@ test_info_limit_named_shows_accounted_zero_for_matched_but_never_charged_instanc
"never_charged_instance: PASS\n");
}
/*
* Task 3.2's own central Observable (Requirement 3.1, 3.2, 3.4): a
* host-scoped limit matched against jobs on TWO distinct hosts shows TWO
* SEPARATE usage lines, each labeled with its own host -- and a THIRD
* configured-but-untouched host (registered as a real worker, but never
* given a populated capacity entry at this limit's shadow-resource idx)
* shows no line at all, proving existence is gated on a per-worker
* capacity read, not on the worker merely existing.
*/
static void
test_info_limit_named_shows_separate_per_host_usage_lines_for_touched_hosts_and_none_for_untouched_host(
void)
{
xce_sched_sim_driver_t sim;
xce_sched_plugin_t plugin;
xce_scheduler_t group_sched;
xce_limit_t* matched = NULL;
var limits_handle;
xce_vres_t* vres;
xce_sched_worker_t* wA;
xce_sched_worker_t* wB;
char* captured;
int rc;
memset(&sim, 0, sizeof(sim));
rc = xce_sched_core_init(&(sim.core));
TEST_ASSERT_EQUAL_INT(0, rc);
register_per_host_resource(&sim, "mem");
register_limit_plugin(&sim, &plugin, &group_sched);
add_configured_percent_limit(&plugin, "host_usage_limit", "mem");
limits_handle = limit_get_configured_limits(&(sim.core));
rc = key_find(limits_handle, (void*)"host_usage_limit", (void**)&matched);
TEST_ASSERT_TRUE(rc > 0);
TEST_ASSERT_NOT_NULL(matched);
vres = instantiate_and_register_host_vres(&sim, &plugin, matched);
wA = build_bare_worker(&sim, "hostA");
wB = build_bare_worker(&sim, "hostB");
/* hostC is registered as a real worker but deliberately never given a
* populated capacity entry below -- configured, but untouched by this
* limit. */
(void)build_bare_worker(&sim, "hostC");
put_shared_fvalue(&(wA->resvals), vres->vres->idx, 5.0);
put_shared_fvalue(&(wA->resInUse), vres->vres->idx, 2.0);
put_shared_fvalue(&(wB->resvals), vres->vres->idx, 5.0);
put_shared_fvalue(&(wB->resInUse), vres->vres->idx, 3.0);
captured = capture_info_limit(&sim, "host_usage_limit");
TEST_ASSERT_NOT_NULL_MESSAGE(
strstr(captured, "host=hostA"),
"Requirement 3.2/3.4: a host-scoped limit matched on hostA must "
"show a usage line labeled with hostA");
TEST_ASSERT_NOT_NULL_MESSAGE(
strstr(captured, "host=hostB"),
"Requirement 3.2/3.4: a host-scoped limit matched on hostB must "
"show a SEPARATE usage line labeled with hostB");
TEST_ASSERT_NULL_MESSAGE(
strstr(captured, "host=hostC"),
"Observable: a third configured-but-untouched host must show no "
"line at all, not a fabricated zero");
TEST_ASSERT_NOT_NULL_MESSAGE(
strstr(captured, "accounted=2.00"),
"Requirement 2.1: hostA's own accounted usage must be shown");
TEST_ASSERT_NOT_NULL_MESSAGE(
strstr(captured, "accounted=3.00"),
"Requirement 2.1: hostB's own accounted usage must be shown, "
"distinct from hostA's");
free(captured);
printf("test_info_limit_named_shows_separate_per_host_usage_lines_for_"
"touched_hosts_and_none_for_untouched_host: PASS\n");
}
/*
* Task 3.2's own existence-vs-zero-usage distinction, applied per host
* (mirrors task 3.1's central Observable #3 for the GLOBAL/SHARED case):
* a worker with a genuinely populated CAPACITY entry at this limit's
* shadow-resource idx, but no in-use entry (never charged), must display a
* genuine accounted=0 line for that host -- not omit the host entirely.
*/
static void
test_info_limit_named_shows_accounted_zero_for_host_with_capacity_but_never_charged(
void)
{
xce_sched_sim_driver_t sim;
xce_sched_plugin_t plugin;
xce_scheduler_t group_sched;
xce_limit_t* matched = NULL;
var limits_handle;
xce_vres_t* vres;
xce_sched_worker_t* wA;
char* captured;
int rc;
memset(&sim, 0, sizeof(sim));
rc = xce_sched_core_init(&(sim.core));
TEST_ASSERT_EQUAL_INT(0, rc);
register_per_host_resource(&sim, "mem");
register_limit_plugin(&sim, &plugin, &group_sched);
add_configured_percent_limit(&plugin, "host_uncharged_limit", "mem");
limits_handle = limit_get_configured_limits(&(sim.core));
rc = key_find(limits_handle, (void*)"host_uncharged_limit",
(void**)&matched);
TEST_ASSERT_TRUE(rc > 0);
TEST_ASSERT_NOT_NULL(matched);
vres = instantiate_and_register_host_vres(&sim, &plugin, matched);
wA = build_bare_worker(&sim, "hostA");
put_shared_fvalue(&(wA->resvals), vres->vres->idx, 5.0);
/* Deliberately no put_shared_fvalue() into wA->resInUse -- a genuinely
* populated capacity entry, but nothing charged against it yet. */
captured = capture_info_limit(&sim, "host_uncharged_limit");
TEST_ASSERT_NOT_NULL_MESSAGE(
strstr(captured, "host=hostA"),
"hostA has a genuinely populated capacity entry and must appear as "
"a line, even though nothing has been charged against it yet");
TEST_ASSERT_NOT_NULL_MESSAGE(
strstr(captured, "accounted=0.00"),
"the existence-vs-zero-usage distinction applies per host too: a "
"populated capacity entry with an absent in-use entry must display "
"a genuine accounted=0, not omit the host");
free(captured);
printf("test_info_limit_named_shows_accounted_zero_for_host_with_"
"capacity_but_never_charged: PASS\n");
}
/*
* Requirement 2.2 applied to the host-scoped case as a whole: a host-scoped
* xce_vres_t that genuinely exists (instantiated, real capacity idx
* allocated) but for which NO worker has ever been given a populated
* capacity entry must report the same "no active usage instance yet"
* message the GLOBAL/SHARED case reports for an unmatched limit -- proving
* repl_print_limit_usage_per_host()'s own zero-lines-printed return value
* correctly threads through to repl_print_limit_usage()'s outer n_shown
* fallback rather than silently printing nothing with no explanation.
*/
static void
test_info_limit_named_shows_no_active_usage_instance_when_host_scoped_vres_exists_but_no_worker_populated(
void)
{
xce_sched_sim_driver_t sim;
xce_sched_plugin_t plugin;
xce_scheduler_t group_sched;
xce_limit_t* matched = NULL;
var limits_handle;
char* captured;
int rc;
memset(&sim, 0, sizeof(sim));
rc = xce_sched_core_init(&(sim.core));
TEST_ASSERT_EQUAL_INT(0, rc);
register_per_host_resource(&sim, "mem");
register_limit_plugin(&sim, &plugin, &group_sched);
add_configured_percent_limit(&plugin, "host_untouched_limit", "mem");
limits_handle = limit_get_configured_limits(&(sim.core));
rc = key_find(limits_handle, (void*)"host_untouched_limit",
(void**)&matched);
TEST_ASSERT_TRUE(rc > 0);
TEST_ASSERT_NOT_NULL(matched);
(void)instantiate_and_register_host_vres(&sim, &plugin, matched);
/* A worker exists, but its capacity entry at this vres's idx is
* deliberately never populated -- the limit's shadow resource has
* never actually been written for any host. */
(void)build_bare_worker(&sim, "hostA");
captured = capture_info_limit(&sim, "host_untouched_limit");
TEST_ASSERT_NOT_NULL_MESSAGE(
strstr(captured, "no active usage instance yet"),
"Requirement 2.2: a host-scoped vres that exists but has no worker "
"with a populated capacity entry must report no active usage "
"instance, not a fabricated zero and not silence with no "
"explanation");
TEST_ASSERT_NULL_MESSAGE(
strstr(captured, "host=hostA"),
"hostA has no populated capacity entry and must not appear as a "
"line");
free(captured);
printf("test_info_limit_named_shows_no_active_usage_instance_when_host_"
"scoped_vres_exists_but_no_worker_populated: PASS\n");
}
int main(void)
{
UNITY_BEGIN();
@ -1230,5 +1522,11 @@ int main(void)
test_info_limit_named_shows_no_active_usage_instance_for_never_matched_limit);
RUN_TEST(
test_info_limit_named_shows_accounted_zero_for_matched_but_never_charged_instance);
RUN_TEST(
test_info_limit_named_shows_separate_per_host_usage_lines_for_touched_hosts_and_none_for_untouched_host);
RUN_TEST(
test_info_limit_named_shows_accounted_zero_for_host_with_capacity_but_never_charged);
RUN_TEST(
test_info_limit_named_shows_no_active_usage_instance_when_host_scoped_vres_exists_but_no_worker_populated);
return UNITY_END();
}