Fix HA event-loop stall: add timeouts to login flow, defer blocking SSL context creation - #113
Open
MathieuTz wants to merge 1 commit into
Open
Conversation
… default SSL context off the event loop thread Every request in the OAuth/MFA login flow (async_login_flows.py) and the WSS-credentials fetch relied on whatever timeout the caller's ClientSession happened to be built with. For callers using Home Assistant's shared session that's a 300s *total* timeout, and this flow can chain up to MAX_REDIRECTS requests -- so a single slow/hung response on GE's login pages can stall a caller for minutes, on a path that (for ha_gehome) runs synchronously inside async_setup_entry. Also stop calling ssl.create_default_context() synchronously in GeWebsocketClient.__init__ -- it reads the system trust store from disk and is a known Home Assistant blocking-call hazard when it runs on the loop thread during integration setup. It is now built lazily in an executor on first connect.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ge_homewas reliably deadlocking Home Assistant's entire asyncio event loop for me — not just the integration, the whole process stopped making progress and neededdocker restarting, roughly 15–25 times a day, concentrated between 02:00 and 05:00 local time, right after HA's startup custom-integration scan.I dug into this over a few days with py-spy stack dumps and a controlled elimination test (disabling only
ge_home: 18 freezes in 18 hours → zero over the next 13h49m, spanning the whole freeze window). That confirmedge_homeas the cause but not the exact line. Digging into the code turned up a concrete, fixable defect: every HTTP call in the OAuth/MFA login flow has no per-request timeout, and this flow runs synchronously insideasync_setup_entry.Root cause
gehomesdk/clients/async_login_flows.pymakes up toMAX_REDIRECTS(10) chainedsession.get/session.postcalls during login (page fetch, credentials POST, redirect-following, MFA/terms pages, code exchange), andwebsocket_client.py's_async_get_wss_credentialsmakes one more — none of them pass atimeout=. They fall back to whatever the caller'sClientSessionwas built with. Forha_gehome, that's Home Assistant's shared session (async_get_clientsession(hass)), which defaults to a 300-second total timeout. Since this whole flow is awaited synchronously as part ofGeHomeUpdateCoordinator.async_setup()→async_setup_entry, a single slow/hung response on GE's login pages can stall HA's own integration-setup task for minutes — directly on the startup critical path, which lines up with what I was seeing (freeze onset right at/afterge_home's setup, lasting several minutes).Separately,
GeWebsocketClient.__init__callsssl.create_default_context()synchronously — a disk read that Home Assistant's own blocking-call detector already flags when it runs on the event loop thread (#297, still reproducible on currentmain).I want to be upfront that I can't prove with 100% certainty this is the only thing going on — I only have OS-thread-level py-spy dumps, not per-
asyncio.Taskstate, so I can't point at the one specific stuckawait. But this is a real, reproducible defect on exactly the code path the evidence points to, and it's correct hardening either way.What this PR does
async_login_flows.py— adds an explicitLOGIN_REQUEST_TIMEOUT = ClientTimeout(total=15)and passes it to all 8session.get/session.postcalls in the login/MFA/token flow, instead of inheriting the caller's session-wide default.websocket_client.py:WSS_CREDENTIALS_TIMEOUT, 15s) to the WSS-credentials fetch.ssl.create_default_context()out of__init__into a new_async_ensure_ssl_context(), built lazily vialoop.run_in_executor(...)on first connect instead of synchronously on construction. Caller-suppliedssl_contextis unaffected (still used as-is, never rebuilt).Both timeouts (15s) are generous for what should be fast login-page/JSON-API round trips, while still being far below the 300s a caller's session might otherwise allow.
Testing
pytest(existing suite, 12 tests) passes unmodified.ssl_context.gehomesdk2026.5.4 in my Home Assistant container and re-enablingge_home: it connected cleanly (no import/setup errors, live-updating oven sensor data confirmed by watching a temperature reading change between polls, not just restored/stale state), and ran freeze-free for 22+ hours, spanning the entire 02:00–05:00 window that had reliably produced 15–25 freezes/day beforehand — with zerodocker restarts and zero even needing a websocket reconnect in that window.Happy to share the raw py-spy dumps or the watchdog/log evidence if useful, and happy to adjust the timeout values or split this into two smaller PRs if you'd prefer the login-timeout and blocking-SSL-context fixes reviewed separately.
Thanks for maintaining this — appliance cloud APIs are a pain to work with and this has otherwise worked well for me.