Opened 4 months ago
Closed 4 months ago
#35583 closed Bug (needsinfo)
asgiref.sync.sync_to_async cannot be affected by close_old_connections
Reported by: | Alexandr Onufrienko | Owned by: | |
---|---|---|---|
Component: | Database layer (models, ORM) | Version: | |
Severity: | Normal | Keywords: | |
Cc: | Carlton Gibson | Triage Stage: | Unreviewed |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description (last modified by )
Rel. to https://code.djangoproject.com/ticket/34914#comment:3
partially rel. to this implementation https://github.com/django/channels/blob/main/channels/db.py
- Let's assume, that we have django command, that creates a "server" that listening for a message from queue, or periodically do some job.
- Second point - our service have async function and requires DB calls (dummy example in _test function)
- let's assume. that we have Postgres DB running in docker compose - and to reproduce this bug, we should call docker compose restart db
Similar to Django itself and, for example, to Django channels https://github.com/django/channels/blob/main/channels/db.py we want to be able to close closed connection (simplest way to reproduce - DB was restarted).
Expected behavior: we are assuming, that we do same steps as we can see in Django project examples and we should see success message in logs.
Actual: we will see failure: the connection is closed
What we can see in code itself:
- sync_to_async class will use its own isolated ThreadPoolExecutor
- django connections will be created withing this ThreadPoolExecutor and will be unavailable for us (this is my assumption)
Found workaround
Calling this code will implicitly have access to hidden DB pool and will close closed connections
asyncio.run(sync_to_async(close_old_connections)())
Notes:
- I'm not really sure - is it a feature request or a bug (taking into account, how hard to find theoretical reason of it)
- I didn't dive into details of async_to_sync function/class
Code snippet for Django command
import asyncio import time from asgiref.sync import sync_to_async from django.db import close_old_connections from django.core.management.base import BaseCommand # This can be any model from django.contrib.auth.models import User class Command(BaseCommand): def handle(self, *args, **options): while True: process_message() time.sleep(3) def process_message(): print("close_old_connections") close_old_connections() print("run test") try: test() print("success") except Exception as e: print("failure:", str(e)) print("done test") def test(): asyncio.run(_test()) async def _test(): """ Synthetic case - most likely, we will use ``sync_to_async`` as decorator for part of functions and will use it directly for other calls ``sync_to_async``. """ await User.objects.afirst() await sync_to_async( User.objects.first )() await sync_to_async( User.objects.first, thread_sensitive=False, )()
Change History (2)
comment:1 by , 4 months ago
Description: | modified (diff) |
---|
comment:2 by , 4 months ago
Cc: | added |
---|---|
Component: | Uncategorized → Database layer (models, ORM) |
Resolution: | → needsinfo |
Status: | new → closed |
Type: | Uncategorized → Bug |
Hello Alexandr, thank you for your report.
From the provided description, I can't quite understand the exact issue you are reporting. Could you please describe the use case and what you're trying to achieve in detail? This will help us understand the problem better and find the right solution.
There are a few things to consider:
CONN_MAX_AGE
specifically), Django version, etc. I have tried the code sample provided with a postgresql database and I don't get the error you have reported (I do get ResourceWarning: connection <psycopg.Connection [IDLE] ...> was deleted while still open. Please use 'with' or '.close()' to close the connection).Initially I thought this could be similar to #31905 but after some further thinking I don't think it's the case.