fix(testsuite): bug-924 -- auto-clean mkdtemp scratch dirs at test-exit (real fix, not the prune mask)

Unit tests create /tmp/xce_* scratch dirs via mkdtemp() and never remove
them; a full ASAN run leaks 70+ dirs and fills /tmp to ENOSPC, which then
surfaces as unrelated "Expected Non-NULL"-style cascade failures in whatever
test next calls mkdtemp()/persist_init() (precedent: 16238 dirs / 696G /
disk 100%). xce-build.sh's age-gated prune_stale_tmp_fixtures() only sweeps
the debris hours later -- a mask, not a fix.

Fix at the source: a self-contained mkdtemp() replacement
(src/testsuite_support/mkdtemp_clean.c) linked into every unit-test
executable via a declare_dependency() appended to the shared deps lists
(controller_unit_test_deps, scheduler_test_deps). Each scratch dir it creates
is registered and recursively removed by an atexit() handler, so a test
binary leaves /tmp clean. Cleanup runs at process exit -- after all TEST_
functions -- so tests that legitimately inspect a fixture's on-disk contents
across multiple TEST functions in the same file are unaffected (the property
that made per-file teardown retrofitting invasive).

Verified: full controller_unit_test (1269 tests) passes with 0 failures and
zero /tmp/xce_* left at exit (was: intermittent ENOSPC cascade). Scheduler
suite build+run still to be validated in the merge verification pass.

Design notes (rejected alternatives, to avoid re-litigating):
- A mid-run "keep newest N dirs" eviction was tried and dropped: it slowed a
  monolithic run dramatically and one variant hung the suite (rmrf of large
  WAL dirs in the hot path). atexit-only bounds nothing mid-run, but a single
  suite fits in /tmp and cleanup is prompt; parallel meson suites are the
  residual risk, addressed by running meson test with reduced parallelism if
  needed.
- A pthread_atfork registry reset was tried (to stop a forked child's
  inherited atexit from deleting the parent's dirs) and dropped: it
  regressed the controller suite (~98 failures), so the plain atexit version
  is what ships. If the scheduler repl/sim fork suites need it, revisit there
  with a scheduler-scoped mechanism, not a global one.

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
lei.chen 2026-09-05 02:21:04 +00:00
parent a013f4a460
commit 27e81738d7
3 changed files with 202 additions and 0 deletions

View file

@ -232,6 +232,19 @@ controller_unit_test_deps = [
ssl_dep,
]
# bug-924: unit tests create /tmp/xce_* scratch dirs via mkdtemp() and never
# remove them; a full run leaks 70+ dirs and fills /tmp to ENOSPC (seen at
# 16238 dirs / 696G / disk 100%), surfacing as unrelated "Expected Non-NULL"
# cascade failures. Link the mkdtemp auto-clean shim (testsuite_support/
# mkdtemp_clean.c) into every executable that shares this deps list
# (controller_unit_test + the standalone *_unit_test binaries below) so each
# scratch dir is removed at process exit -- fixing the leak at the source
# instead of masking it with xce-build.sh's age-gated prune.
mkdtemp_clean_lib = static_library(
'xce_mkdtemp_clean',
join_paths(meson.source_root(), 'testsuite_support', 'mkdtemp_clean.c'))
controller_unit_test_deps += declare_dependency(link_with: mkdtemp_clean_lib)
controller_unit_test = executable('controller_unit_test',
controller_unit_test_src,
include_directories: [incdir],