channels_redis & async_to_sync performance problem

128 views
Skip to first unread message

Chris

unread,
Mar 29, 2020, 12:20:08 PM3/29/20
to Django users
Hello,

I have a worker process that uses async_to_sync(group_send) to send messages to my Channels consumers. The worker process is a simple long-running synchronous loop (not based on Django Channels) that is started via a Django management command.

When I use runserver and the in-memory channel layer, performance is great. But when I use channels_redis in production, each call to group_send takes 2 seconds, because each time it needs to create a new redis connection. Here is the call stack:

  File "c:\my_project\core\my_project\channels\utils.py", line 14, in sync_group_send_wrapper
   
return _sync_group_send(group, {'type': type, **event})
 
File "c:\my_project\ve-dj2\lib\site-packages\asgiref\sync.py", line 79, in __call__
   
return call_result.result()
 
File "C:\Users\user\AppData\Local\Programs\Python\Python37-32\lib\concurrent\futures\_base.py", line 428, in result
   
return self.__get_result()
 
File "C:\Users\user\AppData\Local\Programs\Python\Python37-32\lib\concurrent\futures\_base.py", line 384, in __get_result
   
raise self._exception
 
File "c:\my_project\ve-dj2\lib\site-packages\asgiref\sync.py", line 98, in main_wrap
    result
= await self.awaitable(*args, **kwargs)
 
File "c:\my_project\ve-dj2\lib\site-packages\channels_redis\core.py", line 625, in group_send
    async
with self.connection(self.consistent_hash(group)) as connection:
 
File "c:\my_project\ve-dj2\lib\site-packages\channels_redis\core.py", line 839, in __aenter__
   
self.conn = await self.pool.pop()
 
File "c:\my_project\ve-dj2\lib\site-packages\channels_redis\core.py", line , in pop
    conns
.append(await aioredis.create_redis(**self.host, loop=loop))

This is happening because the connection is deleted after each call to async_to_sync:

  File "c:\my_project\core\my_project\channels\utils.py", line 14, in sync_group_send_wrapper
   
return _sync_group_send(group, {'type': type, **event})
 
File "c:\my_project\ve-dj2\lib\site-packages\asgiref\sync.py", line 71, in __call__
    loop
.close()
 
File "c:\my_project\ve-dj2\lib\site-packages\channels_redis\core.py", line 32, in _wrapper
   
self.run_until_complete(pool.close_loop(self))
 
File "C:\Users\user\AppData\Local\Programs\Python\Python37-32\lib\asyncio\base_events.py", line 579, in run_until_complete
   
return future.result()
 
File "c:\my_project\ve-dj2\lib\site-packages\channels_redis\core.py", line , in close_loop
   
del self.conn_map[loop]

When async_to_sync is called from a synchronous Django view, the connection is NOT deleted. That's because in my worker process, self.main_event_loop.is_running() is False, but in my Django views, it's True. That affects this if-statement in async_to_sync.__call__:

        if not (self.main_event_loop and self.main_event_loop.is_running()):
           
# Redis connection gets deleted inside here...

So, how could I solve this? This may be an obvious question but I don't know asyncio well; I just want to get some guidance before I spend too much time looking in the wrong direction.

Thank you!

Reply all
Reply to author
Forward
0 new messages