Repository navigation
Pool workers exit with the server; shutdown stops the scan pool - #477
Merged
Merged
Conversation
…an pool
An idle multiprocessing pool worker holds a write end of its own task queue,
so it never sees EOF when its parent dies; it leaves only on the pool's stop
message. The library-scan pool's worker outlives each sync, and nothing
stopped that pool at shutdown (only an atexit handler, which did not deliver
the message): after a SIGTERM it was re-parented to PID 1 with its database
connection open. Four such orphans held test databases for a day. Familiar
Server stops and restarts the server the same way.
- process_setup.exit_with_parent: a daemon thread in every pool worker
_exits once its parent PID changes (macOS has no PR_SET_PDEATHSIG).
scan_worker_init and analysis_worker_init both start it.
- BackgroundManager.shutdown stops the scan pool too.
- tests/test_workers_exit_with_parent.py: the real scan pool and the
analysis initialiser each take a task, the parent is SIGKILLed, the worker
must be gone within 10 s. Against the previous code both fail ("worker
outlived its killed parent"). A real server synced then SIGTERMed leaves no
child and no database connection.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UpUascYWiPxuH2vtav72eV
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.
The leak
Four Python processes, each orphaned to PID 1, held test databases open for a day. They blocked recreating
familiar_test(#474). They came from E2E servers stopped withpkill(SIGTERM).Reproduced:
Why:
multiprocessingpool worker waits on its task queue, and it holds a write end of that queue itself, so it never sees EOF when the parent dies. It leaves only if the pool sends it a stop message.library_sync._get_scan_executor) is a module singleton whose worker outlives each sync.BackgroundManager.shutdownnever stopped it; only anatexitshutdown(wait=False)did, and the worker never received that stop.Who else it affects: Familiar Server stops and restarts the Python server with SIGTERM, then SIGKILL after 15 s (
ServerController.stopServer). So a Mac install probably gained an orphan holding a Postgres connection on every stop or restart after a sync. I haven't checked this on a real Familiar Server install. Docker is unaffected, because stopping a container ends every process in it.Fix
process_setup.exit_with_parent: a daemon thread in every pool worker callsos._exitonce its parent PID changes, checking once a second. macOS has noPR_SET_PDEATHSIG. Bothscan_worker_initandanalysis_worker_initstart it, so this holds however the server dies.BackgroundManager.shutdownnow also stops the scan pool.Tests
tests/test_workers_exit_with_parent.py, run on the real scan pool and on the analysis initialiser: each takes a task, then the parent SIGKILLs itself. The worker must be gone within 10 s.🤖 Generated with Claude Code
https://claude.ai/code/session_01UpUascYWiPxuH2vtav72eV