Search code examples
pythondjangodjango-channels

django channels race condition


I'm using django channels to handle websocket connections. If two clients send two messages at the same time I get a race condition using channel's WebsocketConsumer. I'm assuming this happens because every connection to this consumer starts its own thread which is then handled in parallel with the other ones. So I thought I'd switch to AsyncWebsocketConsumer. I put async, await, database_sync_to_async and so on where necessary and everything works, but the race condition issue persists. I thought by using AsyncWebsocketConsumer, every connection would be handled in the same thread and calling async def receive(...) would block the thread so that every received message gets handled in sequence. What am I doing wrong?


Solution

  • I am now using a pg advisory lock, see https://github.com/Xof/django-pglocks.

    import json
    from channels.generic.websocket import AsyncWebsocketConsumer
    from channels.db import database_sync_to_async
    from django_pglocks import advisory_lock
    
    class Consumer(AsyncWebsocketConsumer):
        async def receive(self, text_data=None, bytes_data=None):
            text_data = json.loads(text_data)
            await database_sync_to_async(self.do_stuff)(text_data)
            
        def do_stuff(self, text_data):
            with advisory_lock("a lock"):
                stuff = Stuff.objects.get(pk=text_data["stuff_id"])
                # do things
                stuff.save()
    

    Edit: Because I need a lot of locks and postgresql allows only about a hundred connections I switched to python-redis-lock. The maximum number of connections is now only limited by the availability of file descriptors which can be set using ulimit.