Self-remove event subscriber on WebSocketClosedError - #1680
Merged
Zsailer merged 5 commits intoJul 17, 2026
Merged
Conversation
SubscribeWebsocket.event_listener wrote to the socket without guarding against WebSocketClosedError and only cleaned up through on_close, which doesnt reliably fire when the peer disappears without a clean close handshake. The stale bound event_listener kept its SubscribeWebsocket instance (and everything the instance retained) pinned in the event loggers listener set for the life of the process, producing a memory leak that grew linearly with the number of unclean disconnects. Guard write_message and remove the listener from the logger on WebSocketClosedError. Remove_listener uses set.discard, so double-remove is a no-op.
Zsailer
marked this pull request as ready for review
July 17, 2026 16:50
krassowski
approved these changes
Jul 17, 2026
krassowski
left a comment
Collaborator
There was a problem hiding this comment.
Makes sense to me, just the docstring looks not like something I would want to read in docs. Something shorter documenting behaviour rather than imperative code comment would be better there IMO.
Member
Author
|
In full transparency, I patched this code a while ago internally (human written), then realized I never opened a PR to fix upstream. I used AI to port my changes over (hench, why it went hard on the docstring 😆 ). Acknowledging here that I used AI. |
Co-authored-by: Michał Krassowski <5832902+krassowski@users.noreply.github.com>
Zsailer
commented
Jul 17, 2026
Zsailer
commented
Jul 17, 2026
Zsailer
enabled auto-merge (squash)
July 17, 2026 18:18
WebSocketClosedError
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
SubscribeWebsocket.event_listenerwrites to the subscriber's WebSocket without guarding againstWebSocketClosedError, and only cleans up throughon_close, which isn't reliably fired when the peer disappears without a clean close handshake (network drop, browser tab killed, stream reset).When that happens, the stale bound
event_listenerstays inevent_logger._modified_listeners. Since it's a bound method, it keeps a strong reference to itsSubscribeWebsocketinstance, and therefore to the tornado WebSocket handler, the request, and any state captured during the handshake. Nothing ever removes it, so it lives until the process exits. This is a memory leak that grows linearly with the number of unclean disconnects over the server's lifetime.The fix guards
write_messagewith aWebSocketClosedErrorhandler and removes the listener from the logger when the write fails.EventLogger.remove_listenerusesset.discard, so double-remove is a no-op.