fix(worker): bug-1569 periodic resval reports tagged with wrong host

collect_load_indices() (resource_sync.c) tagged every periodic
mem/r1m/r15m/r15s/ut/pg/ls/it/swp/io/tmp report with gethostname()'s
raw OS/container hostname instead of the worker's own join identity
(xcecd_ctx.virtual_name, the -n/--name value). Every other worker-keyed
lookup downstream (scheduler's sched_get_worker(), and the same
HOOK_DETAIL_RESOURCE_UPDATE payload forwarded on to the Go apiserver)
matches by that join identity, so the mismatch silently dropped every
periodic resource report for the real worker.

Also adds a startup INFO log (main.c) naming the resolved job picker
plugin, used to rule out a legacy-picker hypothesis while chasing a
related (still-open) dispatch issue.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Rongsong Shen 2026-08-17 12:01:52 +00:00
parent 0d1ab09eb7
commit 6e5c3a891a
Signed by: rshen
SSH key fingerprint: SHA256:8Vf2pZ3h6rJa7EhGK+WaAQ9Mta/apZvteW6g9U/WKKk
4 changed files with 180 additions and 3 deletions

View file

@ -327,6 +327,10 @@ int main(int argc, char* argv[])
sched_init(&sched, &config, cluster_cfg_ptr, scheduler_req_handler); sched_init(&sched, &config, cluster_cfg_ptr, scheduler_req_handler);
INFO("active job picker plugin: %s", sched.core.job_pick_plugin
? sched.core.job_pick_plugin
: "(null)");
/* Req 5 AC 10 (scheduler-core spec, task 9.1): stash the resolved /* Req 5 AC 10 (scheduler-core spec, task 9.1): stash the resolved
* --record-dir value on the live xce_scheduler_t -- see scheduler.h's * --record-dir value on the live xce_scheduler_t -- see scheduler.h's
* field comment. Not consumed anywhere yet (task 9.2's job); this * field comment. Not consumed anywhere yet (task 9.2's job); this

View file

@ -203,9 +203,29 @@ static xce_resval_t* collect_load_indices(int* count)
return NULL; return NULL;
} }
char hostname[256]; /* bug-1569: this worker's JOIN identity (the -n/--name value, e.g.
if (gethostname(hostname, sizeof(hostname)) != 0) { * "pc13v" -- xcecd_ctx.virtual_name, set from config->virutal_name in
strcpy(hostname, "unknown"); * xcecd_init(), xcecd.c) is what the controller/scheduler key every
* OTHER per-worker resource value by (mark_host_res()/update_val_res(),
* worker.c; sched_get_worker(), core_events.c). Using gethostname()
* (the raw OS/container hostname, which is NOT the same value and can
* be identical across every worker in a multi-worker-per-host test
* setup, or simply a generic sandbox/container name unrelated to any
* configured worker identity) here instead made every periodic load-
* index report's .host wrong -- the controller forwards it to the
* scheduler unmodified (do_update_resvals(), opupd.c), which then
* fails sched_get_worker(core, rv->host) for the REAL worker and
* silently drops the value ("RESVALS_DELTA: unknown worker <host>,
* skipping", core_events.c) -- live-confirmed: gethostname() here
* returned "xce" in this sandbox, not "pc13v". */
const char* hostname = xcecd_ctx.virtual_name;
char hostname_buf[256];
if (hostname == NULL || hostname[0] == '\0') {
if (gethostname(hostname_buf, sizeof(hostname_buf)) != 0) {
strcpy(hostname_buf, "unknown");
}
hostname = hostname_buf;
} }
for (int i = 0; i < rescount; i++) { for (int i = 0; i < rescount; i++) {

View file

@ -26,6 +26,21 @@ test('worker controller_connector test', controller_connector_test,
timeout: 60, timeout: 60,
env: ['ASAN_OPTIONS=detect_leaks=0']) env: ['ASAN_OPTIONS=detect_leaks=0'])
# bug-1569: collect_load_indices() (resource_sync.c) must tag every periodic
# resource report with xcecd_ctx.virtual_name (the worker's own join
# identity), not gethostname()'s raw OS/container hostname -- see
# test_resource_sync.c's own header comment for the full mechanism trace.
resource_sync_test = executable('resource_sync_test',
'test_resource_sync.c',
include_directories: [worker_test_incdir],
dependencies: worker_test_deps,
install: false)
test('worker resource_sync test', resource_sync_test,
suite: 'worker',
timeout: 60,
env: ['ASAN_OPTIONS=detect_leaks=0'])
# component-failover-adoption task 2.2: worker startup-ordering white-box # component-failover-adoption task 2.2: worker startup-ordering white-box
# test. #include's xcecd.c directly (same technique as controller_connector_ # test. #include's xcecd.c directly (same technique as controller_connector_
# test above, but a separate binary/file -- xcecd.c and controller_connector.c # test above, but a separate binary/file -- xcecd.c and controller_connector.c

View file

@ -0,0 +1,138 @@
/*
* test_resource_sync.c - regression test for bug-1569: collect_load_indices()
* (resource_sync.c) must tag every periodic resource value with the
* worker's own JOIN identity (xcecd_ctx.virtual_name -- the -n/--name value,
* set from config->virutal_name in xcecd_init(), xcecd.c), not
* gethostname()'s raw OS/container hostname.
*
* Why this matters: the controller forwards each report's .host field to
* the scheduler UNMODIFIED (do_update_resvals(), src/controller/opupd.c),
* which keys EVERY other per-worker resource value by this SAME join
* identity (mark_host_res()/update_val_res(), src/controller/worker.c;
* sched_get_worker(), src/scheduler/src/core_events.c). A mismatch here
* silently drops every periodic ("mem"/"r15s"/etc.) resource report for the
* real worker -- live-confirmed against two independent deployments in this
* sandbox: gethostname() returned a generic container hostname ("xce"), not
* the worker's configured "-n pc13v" identity, so the scheduler's own
* "unknown worker xce, skipping" WARN fired every ~15s (resource_sync's own
* LOAD_INDICES_INTERVAL_DEFAULT) and "info worker pc13v" never showed real
* mem/r15s/etc values despite xce_test's live `resval` query showing them
* correctly (a DIFFERENT, correctly-addressed query path).
*
* White-box #include of ../src/resource_sync.c (same convention this
* suite already establishes for controller_connector.c/xcecd.c). The 11
* builtin load-index getters (node_info_get_15s/etc., real implementations
* in node_information.c, not linked here) are stubbed as trivial
* deterministic returns -- this test is about collect_load_indices()'s own
* .host-tagging logic, not the real /proc-reading getters' own values.
*/
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <unity/unity.h>
#include "xcecd.h"
/* Real globals this white-box TU's resource_sync.c references
* (xcecd_ctx.virtual_name) -- not linking xcecd.c itself (its own
* main()/full daemon init is out of scope for this narrow test), so a
* plain definition here satisfies the linker, matching this suite's own
* "provide just the global a white-box #include needs" idiom
* (test_xcecd_startup_order.c's g_dummy_subsystem-style stubs). */
xcecd_context_t xcecd_ctx;
/* Stubs for the 11 builtin load-index getters (node_information.c, not
* linked here) -- deterministic, non-NULL so collect_load_indices() never
* takes its own NULL-getter fallback path (out of scope for this test). */
static char* stub_getter(void)
{
return strdup("1.0");
}
char* node_info_get_15s(void) { return stub_getter(); }
char* node_info_get_1m(void) { return stub_getter(); }
char* node_info_get_15m(void) { return stub_getter(); }
char* node_info_get_ut(void) { return stub_getter(); }
char* node_info_get_pg(void) { return stub_getter(); }
char* node_info_get_ls(void) { return stub_getter(); }
char* node_info_get_it(void) { return stub_getter(); }
char* node_info_get_swap(void) { return stub_getter(); }
char* node_info_get_mem(void) { return stub_getter(); }
char* node_info_get_tmp(void) { return stub_getter(); }
char* node_info_get_io(void) { return stub_getter(); }
/* Linker stub for controller_connector.c's real implementation -- only
* referenced by resource_sync_thread() (the background polling loop),
* never invoked by this test (which calls collect_load_indices()
* directly). */
bool xcecd_send_load_indices(xce_resval_t** vals, int count)
{
(void)vals;
(void)count;
return true;
}
#include "../src/resource_sync.c"
void setUp(void) { return; }
void tearDown(void) { return; }
static void
test_collect_load_indices_tags_host_with_virtual_name_not_gethostname(void)
{
xce_resval_t* vals;
int count = 0;
int i;
memset(&xcecd_ctx, 0, sizeof(xcecd_ctx));
xcecd_ctx.virtual_name = (char*)"pc13v";
vals = collect_load_indices(&count);
TEST_ASSERT_NOT_NULL(vals);
TEST_ASSERT_TRUE(count > 0);
for (i = 0; i < count; i++) {
TEST_ASSERT_NOT_NULL(vals[i].host);
TEST_ASSERT_EQUAL_STRING("pc13v", vals[i].host);
}
free_resvals(vals, count);
printf("test_collect_load_indices_tags_host_with_virtual_name_not_"
"gethostname: PASS\n");
}
static void
test_collect_load_indices_falls_back_to_gethostname_when_virtual_name_unset(
void)
{
xce_resval_t* vals;
int count = 0;
char expected[256];
memset(&xcecd_ctx, 0, sizeof(xcecd_ctx));
xcecd_ctx.virtual_name = NULL;
TEST_ASSERT_EQUAL_INT(0, gethostname(expected, sizeof(expected)));
vals = collect_load_indices(&count);
TEST_ASSERT_NOT_NULL(vals);
TEST_ASSERT_TRUE(count > 0);
TEST_ASSERT_EQUAL_STRING(expected, vals[0].host);
free_resvals(vals, count);
printf("test_collect_load_indices_falls_back_to_gethostname_when_"
"virtual_name_unset: PASS\n");
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_collect_load_indices_tags_host_with_virtual_name_not_gethostname);
RUN_TEST(test_collect_load_indices_falls_back_to_gethostname_when_virtual_name_unset);
return UNITY_END();
}