-
Notifications
You must be signed in to change notification settings - Fork 12
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
Fix memory leak #51
Fix memory leak #51
Conversation
The `Tasker::Task` that pings each websocket was not being cleaned up and garbage-collected, which meant that the WebSocket itself was also not being GCed since it (and the WebSocketPinger itself) was referenced inside the task block, leading to a leak for every single WebSocket. I had a feeling this was the case because CPU and RAM consumption were not static after a lot of WebSockets had been connected, even if they were *no longer* connected. Turns out every single WebSocket was still being pinged or raising an exception if it were closed. That exception was not causing the `Tasker::Task` to stop.
src/cable/websocket_pinger.cr
Outdated
@@ -23,10 +24,15 @@ module Cable | |||
end | |||
|
|||
def initialize(@socket : HTTP::WebSocket) | |||
Tasker.every(Cable::WebsocketPinger.seconds.seconds) do | |||
@task = Tasker.every(Cable::WebsocketPinger.seconds.seconds) do | |||
Logger.debug { "Pinging websocket" } |
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 can remove this line if desired
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.
makes sense, probably would generate a log of log that brings not much value, 500 clients connected, 500 lines every second
I was totally thinking this same thing! On #48 there's also a RedisPinger, and I was starting with that, but I think I got tripped up by how that one was working. I wonder if it's even needed 🤔 In any case, this is amazing! Thanks for jumping in on this. |
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.
👍
The
Tasker::Task
that pings each websocket was not being cleaned up and garbage-collected, which meant that the WebSocket itself was also not being GCed since it (and the WebSocketPinger itself) was referenced inside the task block, leading to a leak for every single WebSocket.I had a feeling this was the case because CPU and RAM consumption were not static after a lot of WebSockets had been connected, even if they were no longer connected. Turns out every single WebSocket was still being pinged or raising an exception if it were closed. That exception was not causing the
Tasker::Task
to stop.I discovered this by putting some debug logging inside the task block (which I forgot to remove … oops) and performed the very definitely scientific test I outlined in #50 — mashing refresh a bunch of times. With only one WebSocket open to the server (the rest had been closed), I should have seen a log message once every 3 seconds, I was seeing hundreds of them per second. With this patch, I saw them at the expected cadence.
Fixes #50