1

I have a Django 5 application that use websockets using channels, and I'm trying to switch to AsyncConsumers to take advantage of asyncronous execution for I/O or external API tasks.

I already wrote a demo project and everything it's working fine, however in my application I use Django signals, and I have a long I/O task to be performed in the post_save of MyModel, previously implemented using threads:

from asyncio import sleep

@receiver(dj_models.signals.post_save, sender=MyModel)
async def auto_process_on_change(sender, instance, **kwargs):
  logger.log("Starting Long Save task");
  await sleep(30)
  logger.log("Starting Long Save task");

The websocket consumer that is serving the request is similar to the following:

from channels.generic import websocket

class AsyncGenericConsumer(websocket.AsyncJsonWebsocketConsumer):

   async def connect(self):
      await self.accept()
   
   ...
   
   async def receive_json(self, message):
      ...
      user = await User.objects.aget(id=...)
      # Authentication code
      # ....
      # (eventually) Create MyModel, depending on request type
      mod = await my_models.MyModel.objects.acreate(...)

Now, the problem is as follow:

When Alice's request is still performing a post_save operation (i.e., awaiting the long task contained in it) and a Bob user opens up the browser and makes a request to the same consumer, the computation gets stuck to user = await User.objects.aget(id=...) until the long task (e.g., the asyncio.sleep(30)) terminates for Alice.

The actual application is much more complex than this and we can't get rid of the post_save. I would like to understand how the .aget() on a model could get locked by a post_save on another model, when they should be asynchronous calls and be processed as such. Are there ways to avoid this while still using asyncio code?

I am using the latest versions of all the packages involved.

Thanks for your support.

4
  • First thing I'd look there is if "long_save" really calls asynchronous code, and awaits properly on it - if for some reason, it is CPU bound, or have synchronous long tasks, the effects would be as you describe. Commented Apr 22, 2024 at 23:15
  • Yes, I both checked into the django code and run a debug session, the signal is called asynchronously as advertised... Commented Apr 23, 2024 at 13:35
  • What's the long task doing? Might be a database level lock. What DB are you using? Commented Apr 23, 2024 at 14:10
  • 1
    @EDG956 The long task is simply a call to an external API, but the problem persists also when I substitute that call with an await asyncio.sleep(). The database is postgres. Commented Apr 23, 2024 at 15:20

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.