Skip to content

fix(client): release in-flight commands when the connection dies - #630

Merged
route merged 2 commits into
mainfrom
fix/release-pendings-on-dead-connection
Sep 3, 2026
Merged

fix(client): release in-flight commands when the connection dies#630
route merged 2 commits into
mainfrom
fix/release-pendings-on-dead-connection

Conversation

@route

@route route commented Sep 2, 2026

Copy link
Copy Markdown
Member

Refs #470.

The problem

Client#send_message waits for the response and only afterwards asks whether the connection is still alive:

data = pending.value!(timeout || protocol_timeout)
@pendings.delete(message[:id])

raise DeadBrowserError if data.nil? && @ws.messages.closed?
raise TimeoutError unless data

So when the browser dies, nothing wakes the commands already in flight, and nothing stops new ones from being sent into a closed socket either. Each pays the full :protocol_timeout before raising. In a test suite that means one dead browser turns into hundreds of failures at five seconds apiece, which is a good part of what makes #470's collapse so slow and so hard to read.

The fix

The dispatch loop releases the pending commands as soon as the message queue closes, and a command sent after that raises DeadBrowserError immediately:

raise DeadBrowserError if @ws.messages.closed?
release_pendings   # after the loop breaks on a closed queue

No new error classes and no change to what callers see, only when they see it: DeadBrowserError was already what these paths raised, just five seconds later.

Verification

  • New example in spec/browser_spec.rb #crash: crashing the browser raises DeadBrowserError in under 2s, and so does the next command against the dead browser. Before the fix the first assertion fails at the full 5s timeout.
  • Full suite: 616 examples, 0 failures.

`send_message` waited out `:protocol_timeout` before checking whether the socket was still there, so every command
in flight when the browser died paid the full timeout, and so did every command issued afterwards. A dead browser
turned into a long parade of slow failures instead of one fast one.

The dispatch loop now releases the pending commands once the message queue closes, and a command sent after that
raises `DeadBrowserError` without waiting at all.

Refs #470
…death

The crash spec timed how long Chrome takes to die, which the dockerized browser on CI doesn't answer quickly enough, and it left the shared browser dead for every spec after it. The connection is now closed under a command directly, on its own browser.
@route
route merged commit 65079c9 into main Sep 3, 2026
11 of 12 checks passed
@route
route deleted the fix/release-pendings-on-dead-connection branch September 3, 2026 11:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant