-
Notifications
You must be signed in to change notification settings - Fork 140
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Resources (psql db connection) from worker threads not closed (gc.collect() works around it for asyncio backend] #373
Comments
Relying on the garbage collector for this seems indeed dubious. What's actually supposed to close these connections? |
Apparently they are meant to be closed at the end of the program implicitly, which fails when threads come into play. The difference between asyncio and trio backends appears to be that trio is using daemon threads, where joining them is not possible, and they are meant to be closed at the end of the program. Using non-daemon threads, and gc'ing manually helped there also. I believe the workaround of explicitly closing the connections manually before tearing down the test databases is the only way to solve this properly/probably. thread = threading.current_thread()
if thread.name == "MainThread":
return
root_task = thread.root_task
root_task.add_done_callback(lambda task: connection._close()) Otherwise a query using Closing the issue here, since it is not really a problem with anyio, but it could maybe only provide a consistent API to register cleanup functions from within threads, but this would still require to invoke/trigger them manually somehow likely, and might not really work with different types of threads (daemon/non-daemon). Thanks for looking into this! :) |
Re-opening: actually I think it would be beneficial in general to trigger the garbage collector automatically, which might happen later eventually anyway, resulting in "flaky behavior". This would help libraries like Starlette/FastAPI and its users then to not getting confused about it, and/or having to add code for this. |
Well, the Python-ish way to handle a connection that needs closing, or a file that needs closing, or … you get my drift, is to use a context manager. Or in this case an async context manager. This is because of "Structured Concurrency" which is pretty much the cornerstone of Trio's, and thus anyio's, design. IMHO it is a very bad idea to add support for "registering cleanup functions" to anyio. There already is a way to register a cleanup function: you use a context manager. Two actually if you count |
What he said. Involving the garbage collector outside of tests is a bad, bad idea and it would be trying to fix the problem from the wrong end. |
Ok, given that it is only about tests to start with (Starlette's TestClient) it should get solved there then maybe. |
I am seeing an issue where database connections in tests with FastAPI/Starlette are still open at the end of the test session, resulting in an error when the test database is trying to get dropped ("There is 1 other session using the database.").
This can be fixed when using the asyncio backend with the following patch:
However, with the trio backend this does not help there still.
A test project is available at https://github.com/ming-tung/experiment-django-with-fastapi.
It is not as minimal as it could be, since probably this does not need Django nor a database really, since the issue appears to be about resource "leaks" from the ThreadPoolExecutor in general.
To reproduce it with the example project, which requires a PostgreSQL DB:
This will have a delay in the end, and show two warnings, which are caused by the failure to delete the DB - the important part being "Error when trying to teardown test databases" (fixed/improved in Django via django/django#14918)
Upgrading starlette still shows the issue:
When installing anyio editable and applying the patch to trigger the garbage collector it works, but still fails when patching the test project to use trio:
For reference: Starlette integrated anyio in 0.15.0 (encode/starlette#1157 (encode/starlette@42592d6)).
Before integration of anyio Starlette was already affected by this, but I've only investigated how to "fix" it with anyio being integrated.
I can see that adding an explicit "gc.collect()" is fishy, and probably not a good fix in general, but wanted to create an issue already, and would be happy to investigate further, preferably after getting some pointers.
refs: pytest-dev/pytest-django#429 (long-standing issue with Django DB connections and threads)
Python 3.9.7
The text was updated successfully, but these errors were encountered: