channels_redis: Update to v4 results in "RuntimeError: Event loop is closed"
After upgrading to channels-redis==4.0.0, our celery tasks are all reporting the following traceback:
future: <Task finished name='Task-9' coro=<Connection.disconnect() done, defined at /usr/local/lib/python3.9/site-packages/red is/asyncio/connection.py:819> exception=RuntimeError('Event loop is closed')>
Traceback (most recent call last):
File "/usr/local/lib/python3.9/site-packages/redis/asyncio/connection.py", line 828, in disconnect
self._writer.close() # type: ignore[union-attr]
File "/usr/local/lib/python3.9/asyncio/streams.py", line 353, in close
return self._transport.close()
File "/usr/local/lib/python3.9/asyncio/selector_events.py", line 698, in close
self._loop.call_soon(self._call_connection_lost, None)
File "/usr/local/lib/python3.9/asyncio/base_events.py", line 751, in call_soon
self._check_closed()
File "/usr/local/lib/python3.9/asyncio/base_events.py", line 515, in _check_closed
raise RuntimeError('Event loop is closed')
RuntimeError: Event loop is closed
[2022-10-12 07:30:31,972] [ERROR] [asyncio] Task exception was never retrieved
Downgrading the image to channels-redis==3.4.1 resolves the issue, so I’m starting out here. This seems probably related to #312.
Image OS is Debian Bullseye, amd64. The django application is running with gunicorn.
Probably related packages:
celery==5.2.7
channels==3.0.5
channels-redis==4.0.0
hiredis==2.0.0
redis==4.3.4
Full Pipfile.lock: https://github.com/paperless-ngx/paperless-ngx/blob/dev/Pipfile.lock
About this issue
- Original URL
- State: closed
- Created 2 years ago
- Reactions: 29
- Comments: 35 (15 by maintainers)
Commits related to this issue
- ⬇️(back) pin channels-redis to version <4 Since we are using channels-redis 4, we have connection issues to redis. We must downgrade it to version <4 and block its upgrade, also django-channels upgra... — committed to openfun/marsha by lunika 2 years ago
- ⬇️(back) pin channels-redis to version <4 Since we are using channels-redis 4, we have connection issues to redis. We must downgrade it to version <4 and block its upgrade, also django-channels upgra... — committed to openfun/marsha by lunika 2 years ago
- Pin channels-redis to 4.3.1 to fix an async issue Refs django/channels_redis#332 Refs #13313 Signed-off-by: Rick Elrod <rick@elrod.me> — committed to relrod/awx by relrod 2 years ago
- Pin channels-redis to 4.3.1 to fix an async issue Refs django/channels_redis#332 Refs #13313 Signed-off-by: Rick Elrod <rick@elrod.me> — committed to relrod/awx by relrod 2 years ago
- Pin channels-redis to 4.3.1 to fix an async issue Refs django/channels_redis#332 Refs #13313 Signed-off-by: Rick Elrod <rick@elrod.me> — committed to relrod/awx by relrod 2 years ago
- Pin channels-redis to 4.3.1 to fix an async issue (#13348) Refs django/channels_redis#332 Refs #13313 Signed-off-by: Rick Elrod <rick@elrod.me> — committed to ansible/awx by relrod 2 years ago
- Assure pools are closed on loop close in core (#332) — committed to sevdog/channels_redis by sevdog a year ago
- Assure pools are closed on loop close in core (#332) — committed to sevdog/channels_redis by sevdog a year ago
- fix tests by downgrading channel-redis as per https://github.com/django/channels_redis/issues/332#issue-1406374416 — committed to lukerohde/gpt-chat by lukerohde a year ago
- Assure pools are closed on loop close in core (#332) — committed to sevdog/channels_redis by sevdog a year ago
- Add regression test for #332 — committed to sevdog/channels_redis by sevdog a year ago
- fix: indexer websocket "event loop closed" The indexer was broken due to (https://github.com/django/channels_redis/issues/332). This resulted in the indexer constantly erroring out, and not having a... — committed to FlipsideCrypto/badger by nftchance a year ago
- Assure pools are closed on loop close in core (#332) — committed to sevdog/channels_redis by sevdog a year ago
- Add regression test for #332 — committed to sevdog/channels_redis by sevdog a year ago
- Upgrade channels-redis to avoid RuntimeError See https://github.com/django/channels_redis/issues/332 — committed to hmpf/Argus by hmpf a year ago
- Upgrade channels-redis to avoid RuntimeError (#618) See https://github.com/django/channels_redis/issues/332 — committed to Uninett/Argus by hmpf a year ago
For anyone else with this issue, it doesn’t exhibit when using the newer
RedisPubSubChannelLayer. It’s not in the Channels docs because it’s still in beta.The issue here is not calling
channel_layer.close_pools().See https://github.com/django/channels/issues/1966#issuecomment-1400903327
If you’re using
aync_to_sync()you’ll need to wrap calls in a function that ensures theclose_pools()call is made at the end, since the event loop is shutdown when the concurrent context ends.This should be mentioned in the docs until an official fix is released.
Edit. Until this is resolved I wouldn’t consider 4.0 a stable release
Same here and sometimes
@carltongibson this a major breaking issue so I’m just trying to bring it more attention.
Tomorrow I’ll go through the source and try make a contribution.
If I use ahaltindis’s workaround detailed here, my code works. So it definitely seems to be a conflict/issue with
async_to_sync.Perhaps channels, or channels_redis documentation could be improved, to provide an example of how users should trigger a
group_sendfrom a synchronous context (such that several calls can be made)? Perhaps a ‘sync’ version ofgroup_sendcould be made available?Can we stop with the “me too” and “any update” comments please.
If you have significant new information to add then please do. (Great!) Otherwise it’s just noise.
I’m planning new releases over the new year, and looking into this is part of that.
@carltongibson You can also reproduce the error using this repo:
https://github.com/realsuayip/zaida/tree/490e0c5a49a750bc56a63f9cba5c9514ed91eee4
Steps to reproduce:
1 - Clone the repo 2 - Run “python3 docker.py up” 3 - Once all the containers are running, run “python3 docker.py test”
Hope it helps.
as a workaround Downgrades
channels-redisto3.4.1,this work for me
Thanks for quick reply. So if I understood correctly, to send a message through the channel outsider a consumer I should do something like:
but the connections returned from
get_channel_layerare cached (inchannels.layers.channel_layersobject) so if there are other threads using the connection (like websockets) they will be also disconnected.I tested this and it seems to work fine:
Did I get it right?
same issue here,
dependencies:
code:
this comment did not worked either, I’m getting:
There is no current event loop in thread 'ThreadPoolExecutor-0_0'.I’m pretty sure the issue we have is very similar to what’s reported above. We do this
async_to_sync(channel_layer.group_send)(group_name, channel_layer_payload)from a sync gunicorn worker and I think that’s where this stems from. The stack trace doesn’t go back to our code though, so kinda hard to tell exactly.I was looking at this just yesterday, refactoring the two layers to share the same connection-handling codebase is a bit complex. The two implementations have a lot of differences and share very few elements. So it may take more time (or at least more concentration) to handle that.
In the meantime I have prepared #347 to address this issue in
RedisChannelLayer.@btel That looks like the example yes. As per https://github.com/django/channels_redis/issues/332#issuecomment-1403375145 I want to look into encapsulating that for the next set of releases
@sevdog That would be nice. There’s no urgency though. poco a poco
@carltongibson thank you for the suggestion. However I am a bit concerned that this may be a bit dirty to force users to call this method when using
async_to_syncbecause theclose_poolsmethod is only defined inRedisChannelLayerand is not part of any specification.Having to make a direct call to
.close_poolsmethod is going to be a problem when someone is willingly to move to an other layer implementation (ie: fromRedisChannelLayertoRedisPubSubChannelLayer.I belive that the
RedisChannelLayershould have a method like that defined in the pub-sub to intercept loop close event: https://github.com/django/channels_redis/blob/a7094c58a15cbf6e621b2129253060fc80cfdfad/channels_redis/pubsub.py#L15-L27This way it will be transparent to users and more robust.
Thanks @carltongibson I can confirm this fixes the problem