mirror of
https://github.com/semantica-agi/semantica.git
synced 2026-08-30 04:40:16 +00:00
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:
@@ -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}"
|
||||
|
||||
Reference in New Issue
Block a user