Repository navigation
feat(client): add :keepalive_interval to keep the connection active during long waits - #671
bensheldon wants to merge 2 commits into
Conversation
…uring long waits A proxy between Ruby and Chrome with an idle timeout (e.g. Heroku's router at 55 seconds) drops the websocket when a single wait sends nothing for longer than that, such as a slow Page.navigate response or a navigation started by a click. With :keepalive_interval set, these waits are split into slices with a Browser.getVersion command sent between them. The total wait is still bounded by :timeout.
|
Thanks for the detailed write-up, Ben. The problem is real, but I don't think it belongs in Ferrum. Ferrum talks CDP to whatever sits at Some concerns with the approach itself, apart from scope:
You can do this today without changes to Ferrum, e.g. a background thread in your setup: Thread.new do
loop do
sleep 25
browser.client.command("Browser.getVersion")
end
endOr fix it where the problem is: raise the proxy's idle timeout, run Chrome on the same network as the tests, or warm up whatever makes the wait so long. Some questions so we understand your setup:
I'll leave this and rubycdp/cuprite#349 open for now. If your answers show this is common across setups and not specific to one deployment, I'm happy to revisit it as a connection-level keepalive. |
|
Thanks for the review. And sorry, I should have checked that
For context, none of those are possible.
I appreciate the review and feedback. Regardless, you have resolved my problem ❤️ |
When Chrome is reached through a proxy or router with an idle timeout, any Ferrum wait longer than that timeout drops the connection. Heroku's router, for example, closes websocket connections that are idle for 55 seconds unless "either client or server [sends] an occasional ping packet". Many load balancers default to 60 seconds.
Two waits send nothing over the websocket for their whole duration:
Client#send_messagewaiting for a slow command response, e.g.Page.navigateto a page that takes a long time to respond.Page#commandwaiting for a navigation started by the command, e.g. a click that submits a slow form.With
timeout: 120, either wait can outlast the proxy's limit. The only workaround today is to keep:timeoutbelow that limit and retry in application code. Retrying a click can submit a form twice.This adds an opt-in
:keepalive_intervaloption (defaultnil, no behavior change):When set, both waits are split into slices of at most
keepalive_intervalseconds, and aBrowser.getVersioncommand is sent between slices. The total wait is still bounded by:timeout, andTimeoutErroris raised at the same point as before.Browser.getVersionis used because it's read-only and works at the browser level, so it still works while the page is between documents. As a real CDP round-trip, it creates traffic on every hop between Ruby and Chrome, including intermediate proxies that might answer websocket ping frames themselves.