Skip to content

Add opt-in reconnect when the server's heart-beats stop - #217

Merged
vrslev merged 11 commits into
community-of-python:mainfrom
GLeurquin:feat/check-server-heartbeat
Nov 10, 2026
Merged

vrslev merged 11 commits into
community-of-python:mainfrom
GLeurquin:feat/check-server-heartbeat

Conversation

@GLeurquin

@GLeurquin GLeurquin commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Closes #214. Builds on #216, whose two commits ("Negotiate heart-beats as STOMP 1.2 specifies", "Describe heart-beat negotiation in the README") are also in this branch; only "Add opt-in reconnect when the server's heart-beats stop" is new here. I'll rebase once that PR is merged.

Why

STOMP 1.2, Heart-beating: any new data received shows the remote end is alive; when heart-beats are expected every <n> ms and no data arrives inside a window of at least <n> ms, the receiver MAY consider the connection dead, and SHOULD be tolerant of timing inaccuracies. Today the client never acts on that; a stalled server is noticed only by a caller polling Client.is_alive().

Summary

  • Client.check_server_heartbeat: bool = False, passed to ConnectionManager. Off by default.
  • When the lifespan reports the agreed heart-beat (_restart_background_tasks), and the option is on and the agreed receive interval is above 0, a monitor task starts. It wakes every receive interval; when ActiveConnectionState.is_alive(check_server_alive_interval_factor) fails, it drops the connection through _discard_failed_connection_state (reason "no server heart-beat within timeout"). The read loop then reconnects and subscriptions are restored, as for any lost connection, and the next connection starts its own monitor.
  • is_alive counts any data, since last_read_time is updated on every read, heart-beat or frame.
  • The tolerance is the existing check_server_alive_interval_factor. With the option on, a factor below 1 raises ValueError at construction: the specification allows declaring a connection dead only after a window of at least <n> ms.
  • README: one line under Handling Connectivity Issues, and the option in the parameters example.

Validation

  • just lint
  • just check-types
  • just test-fast: 467 passed. New tests:
    • test_server_heartbeat_check_reconnects_after_silence: no data for more than three agreed intervals, then a reconnect. It fails without the change;
    • test_server_heartbeat_check_keeps_a_connection_that_receives_data;
    • test_server_heartbeat_check_inactive: option off, or no server heart-beats agreed;
    • test_server_heartbeat_check_needs_a_window_of_at_least_the_interval.
  • just test (integration) not run.
  • By hand, against a server sending heart-beats every 10 s: 75 s on one connection, no false reconnect.

GLeurquin and others added 11 commits November 3, 2026 12:41
The client sent heart-beats at the server's wanted interval alone, so a server
answering "heart-beat:x,0" (it wants none) made the client send them in a loop
that never sleeps. The aliveness check read the server's interval alone too, so
a server sending none ("heart-beat:0,y") made every connection look dead, and a
CONNECTED frame without a heart-beat header raised KeyError.

STOMP 1.2, Heart-beating: client heart-beats come every MAX(cx, sy) ms, and
none if cx or sy is 0; server heart-beats every MAX(sx, cy) ms, the same way;
a missing heart-beat header is "heart-beat:0,0". AgreedHeartbeat.negotiate does
exactly that once, in the connection lifespan. The heart-beat task starts only
when the client sends any, and is_alive waits on the agreed server interval and
holds the connection alive when none is agreed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The client reads the server's heart-beats but does nothing when they stop:
since community-of-python#138 and community-of-python#154 a stalled server is noticed only through Client.is_alive(),
by a caller polling it.

STOMP 1.2, Heart-beating: "if, inside a time window of at least <n>
milliseconds, the receiver did not receive any new data, it MAY consider the
connection as dead", and it "SHOULD be tolerant". With check_server_heartbeat,
a monitor wakes every agreed receive interval and, when is_alive fails (no data
for check_server_alive_interval_factor intervals), drops the connection like
any lost one, so the client reconnects and subscribes again. It runs only when
server heart-beats are agreed, and the factor must then be at least 1, the
window the specification requires. Off by default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rtbeat

# Conflicts:
#	packages/stompman/test_stompman/test_aliveness.py
@vrslev
vrslev merged commit 8c86f72 into community-of-python:main Nov 10, 2026
7 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.

Reconnect when the server's heart-beats stop (opt-in)

2 participants