Skip to content
Advertisement

Serving file asyncronously with Django and uvicorn

I have a Django view that serves the content of a file. The Django application was running with WSGI until recently. This worked fine. Then I adapted my application to use ASGI running uvicorn. The file serving is now broken as it seems to loose the connection.

How can I serve the file asynchronously with Django and uvicorn?

Current view:

class FileServeView(View):
    def get(self, request, *args, **kwargs):
        # ...
        return HttpResponse(
            FileWrapper(file_content), content_type="application/octet-stream"
        )

The server is throwing the following error:

ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/site-packages/uvicorn/protocols/http/h11_impl.py", line 377, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "/usr/local/lib/python3.8/site-packages/uvicorn/middleware/proxy_headers.py", line 75, in __call__
    return await self.app(scope, receive, send)
  File "/usr/local/lib/python3.8/site-packages/uvicorn/middleware/asgi2.py", line 17, in __call__
    await instance(receive, send)
  File "/usr/local/lib/python3.8/site-packages/channels/http.py", line 192, in __call__
    await self.handle(body_stream)
  File "/usr/local/lib/python3.8/site-packages/asgiref/sync.py", line 382, in __call__
    raise RuntimeError(
RuntimeError: Single thread executor already being used, would deadlock

I’m using:

Django                        3.2.13
uvicorn                       0.17.1
channels                      2.3.1
asgiref                       3.5.0

Advertisement

Answer

I can’t reproduce with your example, but this question seems to relate to your problem: channels#1722.

Check the dr-luk response:

As @fmgoncalves has mentioned, one way is to alter how the files are served, but I have found a little more reliable patch that I have implemented in my Django Channels Setup based on the information provided by their post.

It seems that the change of thread_sensitive=False to default to True is causing these deadlock detection messages. As far as I can see, Django Channels doesn’t seem to mind the occurrences of the deadlocks.

That being said, I felt it would be safe to monkey patch the sync_to_async function for my Django installation.

project_app/moneky_patches.py

from asgiref.sync import sync_to_async

def patch_sync_to_async(*args, **kwargs):
  """
  Monkey Patch the sync_to_async decorator
  ---------------------------------------
  ASGIRef made a change in their defaults that has caused major problems
  for channels. The decorator below needs to be updated to use
  thread_sensitive=False, thats why we are patching it on our side for now.
  https://github.com/django/channels/blob/main/channels/http.py#L220
  """
  kwargs['thread_sensitive'] = False
  return sync_to_async(*args, **kwargs)

Then you just overwrite the existing instance of asgiref’s sync_to_async method with our patched wrapper that enforces thread_sensitive=False

project_app/__init__.py

from . import monkey_patches
import asgiref

### Monkey Patches
asgiref.sync.sync_to_async = monkey_patches.patch_sync_to_async

This is make channels, and all of Django run in a insensitive manner like it did before the ASGIRef update.

User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement