fix: prune dead transports from the engine on connection loss#1796
fix: prune dead transports from the engine on connection loss#1796bdraco wants to merge 2 commits into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #1796 +/- ##
=======================================
Coverage 99.77% 99.77%
=======================================
Files 33 33
Lines 3540 3549 +9
Branches 498 500 +2
=======================================
+ Hits 3532 3541 +9
Misses 5 5
Partials 3 3 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
37e3ceb to
530b7ff
Compare
PR Review — fix: prune dead transports from the engine on connection lossSolid, well-scoped pruning mechanism; main open question is whether its trigger fires in the case it targets. Strengths:
Needs attention:
🟡 Important
1. connection_lost likely never fires for the EHOSTUNREACH scenario this PR targets
|
bluetoothbot
left a comment
There was a problem hiding this comment.
Blocking issues found.
- connection_lost likely never fires for the EHOSTUNREACH scenario this PR targets
|
Closing per maintainer review. As Kōan correctly flagged, EHOSTUNREACH/ENETUNREACH on send route to asyncio's error_received(), not connection_lost() (verified against CPython _SelectorDatagramTransport), so this hook never fires for the targeted scenario. These errnos are transient route conditions and should not trigger pruning of an otherwise-good interface. The runtime rescan API (#1797) and the optional monitor (#1798) cover genuine interface add/remove/IP-change, and are being reworked to stand alone on master without this change. |
Summary
When an interface goes down or changes IP at runtime, its per interface sender transport stays in the engine forever; every multicast send then raises EHOSTUNREACH (No route to host), logged once then retried silently for the life of the instance, so the only recovery is to recreate the whole
Zeroconf. This makesAsyncListener.connection_lostprune itself, dropping its reader and sender wrappers and its protocol from the engine lists so a dead socket stops being used.Details
AsyncEngine._async_remove_listeneris the first incremental remove path; it mirrors the list semantics of_async_shutdownand matches both wrapper objects by the shared underlying transport identity (wrapper.transport is dead_transport), since one per interface socket is wrapped twice (once intoreaders, once intosenders).Test plan
tests/test_engine.py::test_connection_lost_prunes_transportassertsreaders,senders, andprotocolsshrink and no longer reference the lost transport.tests/test_engine.pygreen under both the compiled Cython build (REQUIRE_CYTHON=1) and pure Python (SKIP_CYTHON=1).pre-commit runclean.