Skip to content

Reconnect the live update stream after a transient failure - #418

Open
rhammen wants to merge 11 commits into
custom-components:masterfrom
rhammen:fix/issue-417-stream-reconnect
Open

Reconnect the live update stream after a transient failure#418
rhammen wants to merge 11 commits into
custom-components:masterfrom
rhammen:fix/issue-417-stream-reconnect

Conversation

@rhammen

@rhammen rhammen commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Fixes #417.

The live update stream (Service Bus/AMQP connection) currently dies
permanently after any transient failure (e.g. a brief internet outage) —
it swallows the exception, logs it, and just stops, requiring a full
integration reload to recover.

  • stream_main() no longer catches its own exceptions; only the
    existing 403/Forbidden case still returns cleanly ("permanent stop").
  • New _stream_supervisor() in manager.py wraps stream_main() in a
    reconnect loop with exponential backoff + jitter, retrying any
    transient failure indefinitely while leaving CancelledError (task
    cancellation on unload/reload) unaffected.
  • New STREAM_RECONNECT_* constants in const.py.

Personally reviewed the code changes and trimmed AI-generated
docstrings/inline comments for length.

rhammen and others added 11 commits July 28, 2026 21:49
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…them

Only the existing 403/Forbidden case still returns cleanly; any other
failure now raises so a caller can distinguish "retry me" from
"permanent stop". Prep for issue custom-components#417's reconnect supervisor.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Wraps stream_main() in an exponential-backoff retry loop so a transient
connection failure (e.g. a brief home-internet outage) reconnects
automatically instead of permanently killing live updates until the
user reloads the integration. Fixes custom-components#417.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ix, docstring

- Add test coverage for the actual asyncio.sleep delay values (first-call
  value and max-delay cap enforcement across a sustained outage)
- Clamp exponential growth to STREAM_RECONNECT_MAX_DELAY before applying
  jitter, so jitter isn't nullified once backoff saturates
- Document _stream_supervisor()'s actual warn-once-per-outage semantics

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Per this repo's demonstrated review bar (PR custom-components#415, commit 2359337):
maintainers cut explanatory comments even when accurate, not just
stale/duplicative ones. The warn-once-per-outage paragraph explained
a behavior detail already covered by the inline comment at the reset
condition -- kept the return/raise contract and the CancelledError
note since those are safety-critical to not breaking the function.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Same bar as the manager.py docstring trim: keep what's necessary to
not accidentally break the test (the clock-sequence meaning, why the
next(clock, 500.0) fallback exists), cut the restatement/elaboration.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Every other test in this file uses a single-line docstring; these two
stood out at 3 lines each. Kept the issue-custom-components#417 reference as a compact
parenthetical, matching the existing precedent in test_init.py:25.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@rhammen
rhammen marked this pull request as ready for review July 28, 2026 22:09
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.

Live update stream doesn't reconnect after a transient connection failure (e.g. AMQP/network drop)

1 participant