-
-
Notifications
You must be signed in to change notification settings - Fork 754
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Don't abruptly shutdown active websockets on graceful shutdown. #1140
Conversation
@euri10 Let me know what you think. |
so if I follow correctly the stream of things here we have
now in the http protocols we
and in case of the ws protocols the transport will be closed as soon as I'm just trying to see if there would be case we'd like to close the transport in htat also on a side note would that incidentalyy fix #596 ? |
self.queue.put_nowait({"type": "websocket.disconnect", "code": 1012}) | ||
output = self.conn.send(wsproto.events.CloseConnection(code=1012)) | ||
self.transport.write(output) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this needed ? won't it leave the protocol in a state it should not be in, how it will know it's in a closing state ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's same as the other implementation - run_asgi
cleans up each time. The current flow just injects data into an active connection causing worse issues :(
It seems like it should fix #596. def ws_path(ws):
while True:
data = ws.recv()
ws.send(data) But in this case, same behavior will happen - it'll be shutdown abruptly so I think we should tackle this later on (maybe by having a prior signal besides the shutdown?) |
it does not seem that simple to me, but again take this comments with a grain of salt, it's quite hard to follow the flow of things I find here I just tested 596 minimal example from this comment with this PR and here's now the traceback with wsproto (the same happens with websockets by the way), so the gut feeling I was having earlier about leaving the server in a "bad" state seems revlevant.
Now to try advance on that, from the stdlib
so maybe one solution would be to catch
|
Interesting case. I think that apart from catching the cancelled error, we should move the lifespan shutdown event before waiting on open connections, so if the developer creates a persistent websocket, which means |
@euri10 any further feedback? |
I fixed #596 |
Sorry for being pushy.. but this is quite a problematic issue in production when we shutdown alive websockets. |
@aviramha To speed up, would you mind adding tests to cover your changes? I will review it in the next days. |
Thanks @Kludex. I will try to add tests tomorrow. |
@Kludex Took me some time but I added a test :) |
if not connection_lost.cancelled() | ||
else None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will this be hit ?
@@ -201,6 +201,11 @@ async def run_asgi(self): | |||
""" | |||
try: | |||
result = await self.app(self.scope, self.asgi_receive, self.asgi_send) | |||
except (concurrent.futures.CancelledError, asyncio.CancelledError): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dont understand the concurrent.futures.CancelledError
here, why isnt asyncio.CancelledError
enough ?
sorry for the long time @aviramha , if @florimondmanca could also take a lookk that would be awesome, my little brain tends to collapse easily in callbacks |
I don't think that's desirable behaviour, or the model I'd expect with ASGI websockets. If you do this it's not clear to me how they're expected to shutdown. A good point of reference here would be taking a look at what other ASGI servers do here. In particular eg. what behaviour you see with Django + Daphne serving websockets. |
@tomchristie |
@tomchristie following up on aviramha's comment |
except (concurrent.futures.CancelledError, asyncio.CancelledError): | ||
self.logger.error("ASGI callable was cancelled while running") | ||
if not self.handshake_started_event.is_set(): | ||
self.send_500_response() | ||
self.transport.close() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This block is not needed. This will be part of the BaseException
block anyway, just with the fact that it doesn't wait the handshake to complete...
Given that we removed I'll be closing this PR because the core logic here is actually on the Extremely sorry for taking this long here. Also, can someone create an issue with an MRE that this PR tries to solve? |
I want to confirm this idea was solved. |
I believe this is solved. |
Following #1137
Edit by @Kludex:
shutdown
in wsproto impl #596