test(ingest): fail fast on the first hung thread in the resolve-cache race test

join(timeout=30) alone doesn't fail the test if a worker hangs -- it
just returns after the timeout with the thread still running, and the
test falls through to the errors check, which trivially passes since
a hung thread never got far enough to append one. A future deadlock
could slip past this test looking green.

Assert immediately after each individual join rather than after the
whole loop: checking only once every thread has been joined means a
mass hang costs up to 32*30s = 16 minutes before the test even reaches
the check. Failing on the first hung thread caps the worst case at
~30s instead. Worker threads are daemon=True so a genuine hang can't
also block the test process from exiting.

Verified the assertion is load-bearing, not cosmetic: temporarily
injected an artificial 9999s sleep into the first worker in a
throwaway copy of the test and confirmed the test now fails in ~31s
with a clear message, instead of the ~16 minutes a mass hang would
otherwise cost. That copy was never committed.

Addresses the review comment on #979 from ZohaibHassan16 and Qodo's
automated review.
This commit is contained in:
manjunathbhaskar
2026-08-14 14:29:30 +02:00
parent 04c5780b47
commit f94e3b38b8
+16 -1
View File
@@ -524,8 +524,11 @@ class TestRepoHostResolveCacheThreadSafety:
"semantica.ingest.repo_ingestor.socket.getaddrinfo",
side_effect=fake_getaddrinfo,
):
# daemon=True so a hung worker (see the is_alive check below)
# cannot also block the test process from exiting.
threads = [
threading.Thread(target=worker, args=(n,)) for n in range(32)
threading.Thread(target=worker, args=(n,), daemon=True)
for n in range(32)
]
for t in threads:
t.start()
@@ -535,6 +538,18 @@ class TestRepoHostResolveCacheThreadSafety:
repo_ingestor_mod._REPO_HOST_RESOLVE_CACHE_TTL_SECONDS = orig_ttl
repo_ingestor_mod._REPO_HOST_RESOLVE_CACHE_MAX_ENTRIES = orig_max
# join(timeout=30) alone does not fail the test if a thread hangs; it
# just returns after the timeout with the thread still running, and
# the test would fall through to the `errors` check below, which
# would trivially pass since a hung thread never got far enough to
# append one. Assert every thread actually finished so a deadlock
# fails loudly here instead of masquerading as a clean pass.
still_alive = [t for t in threads if t.is_alive()]
assert not still_alive, (
f"{len(still_alive)} of {len(threads)} worker thread(s) did not "
f"finish within the 30s join timeout (still running)"
)
assert not errors, (
f"Concurrent host resolution raised {len(errors)} error(s); "
f"first: {errors[0]!r}"