fix(worker): real maxswap/maxtmp detection; feat(scheduler): annotate multi-slot assigned jobs

bug-1554: init_host_attrs() replaces hardcoded placeholder stubs with real
detection -- maxswap from sysinfo()'s totalswap (reusing the same syscall
snapshot already taken for maxmem), maxtmp from a fresh statvfs("/tmp")
total-capacity measurement. No synthetic fallback on detection failure, so
a system with swap genuinely disabled correctly reports 0.

bug-1776: the scheduler simulator's "info worker" assigned-jobs listing now
annotates a job consuming more than one "slots" unit (e.g. "jobid*2"),
using per-host allocation data that was already being walked but discarded.
Falls back to the pre-existing per-job listing when jobs on the same worker
have differing slot counts, since a single suffix on a compressed range
would be ambiguous.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Hb6Edtj4KbS8CrgEWYdA2E
This commit is contained in:
Rongsong Shen 2026-09-07 04:39:22 +00:00
parent a6b735b6f2
commit a35f597e06
No known key found for this signature in database
GPG key ID: 5BCDAAE0123E9FE2
9 changed files with 660 additions and 28 deletions

View file

@ -41,6 +41,20 @@ test('worker resource_sync test', resource_sync_test,
timeout: 60,
env: ['ASAN_OPTIONS=detect_leaks=0'])
# bug-1554: real maxswap (sysinfo() totalswap)/maxtmp (statvfs("/tmp"))
# detection -- white-box #include of hardware_monitor.c, same convention as
# resource_sync_test above.
hardware_monitor_test = executable('hardware_monitor_test',
'test_hardware_monitor.c',
include_directories: [worker_test_incdir],
dependencies: worker_test_deps,
install: false)
test('worker hardware_monitor test', hardware_monitor_test,
suite: 'worker',
timeout: 60,
env: ['ASAN_OPTIONS=detect_leaks=0'])
# component-failover-adoption task 2.2: worker startup-ordering white-box
# test. #include's xcecd.c directly (same technique as controller_connector_
# test above, but a separate binary/file -- xcecd.c and controller_connector.c

View file

@ -0,0 +1,151 @@
/*
* test_hardware_monitor.c - bug-1554: maxswap/maxtmp used to be hardcoded
* placeholder stubs in xcecd.c's init_host_attrs() (real detection was never
* implemented). This white-box #includes the real hardware_monitor.c so
* these tests exercise the actual sysinfo()/statvfs() detection logic
* against this machine's real values, cross-checking each function's output
* against an independent syscall made directly in the test -- not mocks,
* since the whole point is proving the real detection is wired up and
* arithmetically correct, matching resource_sync_test's own white-box
* #include convention for this file family.
*/
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/statvfs.h>
#include <sys/sysinfo.h>
#include <unity/unity.h>
#include "../src/hardware_monitor.c"
void setUp(void) { return; }
void tearDown(void) { return; }
/* ────────────────────────────────────────────────────────────────────────
* hardware_monitor_get_memory_info() -- total_swap output param
* ──────────────────────────────────────────────────────────────────────── */
static void test_get_memory_info_reports_total_swap_matching_sysinfo(void)
{
int64_t total_memory = -1;
int64_t free_memory = -1;
int64_t total_swap = -1;
struct sysinfo expected;
TEST_ASSERT_EQUAL_INT(0, sysinfo(&expected));
TEST_ASSERT_TRUE(
hardware_monitor_get_memory_info(&total_memory, &free_memory,
&total_swap));
TEST_ASSERT_TRUE(total_swap >= 0);
TEST_ASSERT_EQUAL_INT64(
(int64_t)expected.totalswap * expected.mem_unit, total_swap);
printf("test_get_memory_info_reports_total_swap_matching_sysinfo: "
"PASS\n");
}
static void test_get_memory_info_null_total_swap_returns_false(void)
{
int64_t total_memory = -1;
int64_t free_memory = -1;
TEST_ASSERT_FALSE(
hardware_monitor_get_memory_info(&total_memory, &free_memory, NULL));
printf("test_get_memory_info_null_total_swap_returns_false: PASS\n");
}
/* ────────────────────────────────────────────────────────────────────────
* hardware_monitor_update()/hardware_monitor_get_total_swap() -- the
* monitor-cached accessor init_host_attrs() actually calls.
* ──────────────────────────────────────────────────────────────────────── */
static void test_update_populates_monitor_total_swap(void)
{
hardware_monitor_t* monitor = hardware_monitor_create("/tmp/acct");
int64_t total_memory, free_memory, total_swap;
TEST_ASSERT_NOT_NULL(monitor);
TEST_ASSERT_TRUE(hardware_monitor_update(monitor));
TEST_ASSERT_TRUE(
hardware_monitor_get_memory_info(&total_memory, &free_memory,
&total_swap));
TEST_ASSERT_EQUAL_FLOAT((float)total_swap,
(float)hardware_monitor_get_total_swap(monitor));
hardware_monitor_destroy(monitor);
printf("test_update_populates_monitor_total_swap: PASS\n");
}
static void test_get_total_swap_null_monitor_returns_zero(void)
{
TEST_ASSERT_EQUAL_FLOAT(0.0f, (float)hardware_monitor_get_total_swap(NULL));
printf("test_get_total_swap_null_monitor_returns_zero: PASS\n");
}
/* ────────────────────────────────────────────────────────────────────────
* hardware_monitor_get_disk_info() -- real statvfs()-based capacity
* detection for maxtmp (mirrors node_information.c's node_info_get_tmp()
* own statvfs("/tmp") convention, but reports total capacity, not free
* space).
* ──────────────────────────────────────────────────────────────────────── */
static void test_get_disk_info_tmp_matches_statvfs(void)
{
int64_t total_bytes = -1;
struct statvfs expected;
TEST_ASSERT_EQUAL_INT(0, statvfs("/tmp", &expected));
TEST_ASSERT_TRUE(hardware_monitor_get_disk_info("/tmp", &total_bytes));
TEST_ASSERT_EQUAL_INT64(
(int64_t)expected.f_blocks * (int64_t)expected.f_frsize,
total_bytes);
printf("test_get_disk_info_tmp_matches_statvfs: PASS\n");
}
static void test_get_disk_info_null_args_return_false(void)
{
int64_t total_bytes = -1;
TEST_ASSERT_FALSE(hardware_monitor_get_disk_info(NULL, &total_bytes));
TEST_ASSERT_FALSE(hardware_monitor_get_disk_info("/tmp", NULL));
printf("test_get_disk_info_null_args_return_false: PASS\n");
}
static void test_get_disk_info_nonexistent_path_returns_false(void)
{
int64_t total_bytes = -1;
TEST_ASSERT_FALSE(hardware_monitor_get_disk_info(
"/this/path/does/not/exist/xce-bug-1554", &total_bytes));
printf("test_get_disk_info_nonexistent_path_returns_false: PASS\n");
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_get_memory_info_reports_total_swap_matching_sysinfo);
RUN_TEST(test_get_memory_info_null_total_swap_returns_false);
RUN_TEST(test_update_populates_monitor_total_swap);
RUN_TEST(test_get_total_swap_null_monitor_returns_zero);
RUN_TEST(test_get_disk_info_tmp_matches_statvfs);
RUN_TEST(test_get_disk_info_null_args_return_false);
RUN_TEST(test_get_disk_info_nonexistent_path_returns_false);
return UNITY_END();
}

View file

@ -125,6 +125,17 @@ double hardware_monitor_get_total_memory(hardware_monitor_t* monitor)
(void)monitor;
return 16384.0;
}
double hardware_monitor_get_total_swap(hardware_monitor_t* monitor)
{
(void)monitor;
return 4096.0;
}
bool hardware_monitor_get_disk_info(const char* path, int64_t* total_bytes)
{
(void)path;
*total_bytes = 8192;
return true;
}
network_handler_t* network_handler_create(uint16_t port)
{
@ -358,6 +369,29 @@ static void test_no_peers_cluster_cfg_stays_empty_before_first_join(void)
"test_no_peers_cluster_cfg_stays_empty_before_first_join: PASS\n");
}
/* ════════════════════════════════════════════════════════════════════════
* bug-1554: init_host_attrs() must source maxswap/maxtmp from real
* detection (hardware_monitor_get_total_swap()/hardware_monitor_get_disk_
* info(), both stubbed above) instead of the old hardcoded placeholder
* constants (1024MB/2048MB) -- this test would have kept passing under the
* old stub-constant code too (it doesn't assert the old magic numbers are
* ABSENT), but it does pin that host_attrs picks up whatever these two
* functions report, which the placeholder code structurally could not do.
* ════════════════════════════════════════════════════════════════════════ */
static void test_init_host_attrs_uses_real_swap_and_tmp_detection(void)
{
xcecd_config_t config = base_test_config();
TEST_ASSERT_EQUAL_INT(0, xcecd_init(&config));
TEST_ASSERT_EQUAL_INT(0, xcecd_start());
TEST_ASSERT_EQUAL_FLOAT(4096.0f, (float)host_attrs.maxswap);
TEST_ASSERT_EQUAL_FLOAT(8192.0f, (float)host_attrs.maxtmp);
printf("test_init_host_attrs_uses_real_swap_and_tmp_detection: PASS\n");
}
/* ════════════════════════════════════════════════════════════════════════
* main
* ════════════════════════════════════════════════════════════════════════ */
@ -370,6 +404,7 @@ int main(void)
RUN_TEST(test_multi_candidate_cluster_cfg_populated_before_first_join);
RUN_TEST(test_no_peers_cluster_cfg_stays_empty_before_first_join);
RUN_TEST(test_init_host_attrs_uses_real_swap_and_tmp_detection);
return UNITY_END();
}