Skip to content

Catch backend failures during Connection#initialize. Fixes #105#106

Merged
jwoertink merged 1 commit into
cable-cr:masterfrom
russ:fix-105-clean-close-on-backend-failure
May 7, 2026
Merged

Catch backend failures during Connection#initialize. Fixes #105#106
jwoertink merged 1 commit into
cable-cr:masterfrom
russ:fix-105-clean-close-on-backend-failure

Conversation

@russ
Copy link
Copy Markdown
Contributor

@russ russ commented May 7, 2026

Summary

Fixes #105 — when the backend (e.g. Redis) connection is dead, every WebSocket upgrade was silently killed mid-handshake with close code 1006 and zero messages received, only recovering on process restart.

Connection#initialize's rescue only caught UnauthorizedConnectionException. The IO::Error raised by subscribe_to_internal_channel against a broken Redis socket escaped to HTTP::WebSocketHandler, which tore down the TCP socket without a close frame.

This widens the rescue to any Exception:

  • marks the connection rejected (so the handler's existing next if … connection_rejected? skips add_connection/welcome cleanly)
  • best-effort unsubscribe_from_internal_channel (swallowing further backend errors so the original isn't masked)
  • sends a proper InternalServerError (1011) close frame so clients get a meaningful signal instead of 1006
  • reports via on_error so operators see the failure

Recovery of the backend itself (issue's options #1 and #3) lives in the cable-redis shard and isn't addressed here — this PR is the close-code fix only.

Test plan

  • New spec in connection_spec.cr using a FailingSubscribeBackend (a Cable::DevBackend subclass that raises IO::Error on subscribe) verifies the rescue path: socket closed, connection rejected, on_error invoked.
  • Manual reproduction from the issue: kill Redis pubsub mid-session and confirm fresh upgrades now emit a clean 1011 close frame.

🤖 Generated with Claude Code

…clean close code instead of 1006. Fixes cable-cr#105

When the backend (e.g. Redis) connection is dead, subscribe_to_internal_channel
raised IO::Error which escaped the narrow rescue (UnauthorizedConnectionException
only) and propagated through HTTP::WebSocketHandler, tearing down the TCP socket
without a close frame. Clients saw close code 1006 with zero messages received,
and recovery only happened on process restart.

Widen the rescue to catch any Exception, mark the connection rejected, send a
proper InternalServerError (1011) close frame, and report via on_error so the
failure is visible to operators.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@jwoertink jwoertink left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense 👍

@jwoertink jwoertink merged commit bfdb55d into cable-cr:master May 7, 2026
3 checks passed
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.

Cable connection dies abruptly (close code 1006) on every upgrade after the Redis backend connection drops

2 participants