Repository navigation
Conversation
…stead of hanging test_reads_racing_writes_do_not_raise_sqlite_misuse joined the writer futures before setting stop. A writer timeout — exactly what a reintroduced SQLite race produces — left the with-block with the readers still spinning on an event that was never set, and ThreadPoolExecutor.__exit__ then blocked in shutdown(wait=True) joining them, so the run hung until the job timeout instead of failing in two minutes. Move stop.set() into a finally, matching the second test in the file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
One test in
controller/tests/test_db_concurrency.pycould hang CI for hours exactly when it detects the bug it exists to detect.What
test_reads_racing_writes_do_not_raise_sqlite_misusejoins the eight writer futures withf.result(timeout=120), and calledstop.set()only after that loop, inside thewith concurrent.futures.ThreadPoolExecutor(max_workers=16)block:Why it hangs
A writer that exceeds its timeout — which is exactly what a reintroduced shared-connection race produces, since the unguarded read blocks inside somebody's transaction — raises
TimeoutErrorout off.result(). That exception leaves thewithblock without ever reachingstop.set(), soThreadPoolExecutor.__exit__callsshutdown(wait=True), which joins all sixteen worker threads. The eight writers are done or dying, but the eight readers loop onwhile not stop.is_set()and only exit oncestopis set.stopis never set.shutdownwaits on them forever, so the run hangs until GitHub's job timeout (hours) rather than failing in two minutes with the assertion that names the bug.The failure mode is inverted: the more reliably the test detects a regression, the less likely CI is to report it.
Fix
Set
stopin afinally, so the readers are released whether the writers succeeded or timed out:The
TimeoutErrorstill propagates — the test still fails — but it now fails instead of wedging. This is the same shape the second test in the file,test_reads_see_committed_writes_only, already uses.Verification
Full file, unmodified timeouts:
Temporary local patch (never committed, reverted afterwards) to force the timeout path:
timeout=120→timeout=0.01andtime.sleep(0.2)as the first line ofwriter.Before (pristine
main, same patch), killed by a 20s watchdog:After (this commit, same patch):
So the timeout path now reports in ~1s instead of hanging. The committed diff is only the
try/finally.Full suite (
controller/tests/):The deselected test is
tests/test_emos_update.py::test_trialcheck_passes, which compilesemos/init/trialcheck.cwith the hostcc. It fails identically on pristinemainon macOS because the source includessys/sysmacros.h, a glibc header that does not exist there; it is unrelated to this change and passes on the Linux CI image.