feat: track lifetime connect counters per scanner - #433
Conversation
Add `_connect_completed_total`, `_connect_failed_total`, and `_last_connect_completed_time` to `BaseHaScanner` and surface them via `async_diagnostics()`. The existing per-address `_connect_failures` is cleared on a subsequent success, so it cannot answer "has this proxy ever completed a connection in the time it has been registered?". The new lifetime counters fill that gap. Combined with `last_detection` and the slot-allocation state, a single diagnostics snapshot now distinguishes: - a busy proxy (slots=0 free, completed_total growing, last_completed recent) - a stuck proxy (slots=0 free, completed_total flat, last_completed ancient or zero) Counters reset on `_clear_connection_history()`, which already fires on scanner register and unregister — so they describe the current registration lifetime, not the host process lifetime. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #433 +/- ##
==========================================
+ Coverage 95.92% 95.94% +0.01%
==========================================
Files 14 14
Lines 1913 1922 +9
Branches 232 232
==========================================
+ Hits 1835 1844 +9
Misses 42 42
Partials 36 36 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Merging this PR will improve performance by 5.94%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ⚡ | test_inject_100_simple_advertisements |
206.9 µs | 195.3 µs | +5.94% |
Tip
Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.
Comparing koan/scanner-lifetime-connect-counters (ea80a6d) with main (47d4b71)1
What
Add lifetime connect counters to
BaseHaScannerand surface them inasync_diagnostics():_connect_completed_total— successful connection attempts since thisscanner was registered with the manager
_connect_failed_total— failed connection attempts since registration_last_connect_completed_time— monotonic timestamp of the most recentsuccessful connection (0.0 if none)
Why
CLAUDE.md flags this as a documented gotcha:
That gap matters for the stuck-proxy class of bugs tracked in #340. The
existing per-address
_connect_failuresdict is cleared the moment alater attempt succeeds, so a single diagnostics snapshot cannot tell you
whether this scanner has ever completed a connection in its current
registration. With these counters a snapshot can distinguish:
slots=0/N free,connect_completed_totalgrowing,last_connect_completed_timerecentslots=0/N free,connect_completed_totalflat atzero,
last_connect_completed_timeancient or0.0This complements the diagnostic work in #407 / #416 / #422 without
changing user-facing connection behavior.
How
BaseHaScanner.__slots__and mirrored inbase_scanner.pxdto keep cython in sync._finished_connecting(existing call site thatalready branches on
connected);monotonic_time_coarse()recordsthe success timestamp.
_clear_connection_history()resets the new state alongside_connect_failures/_connect_in_progress, so the counters describethe current scanner-registration lifetime rather than the host
process lifetime.
async_diagnostics()exposes the three new fields next to theexisting
connect_in_progress/connect_failureskeys.Testing
SKIP_CYTHON=1 poetry run pytest tests/ -W "error::DeprecationWarning" -W "ignore::DeprecationWarning:asyncio"→ 250 passed, 1 skipped.test_lifetime_connect_counters_reset_on_history_clearexercises both the increment paths and the reset path.
test_diagnostics_surface_connect_stateextended to alsoassert the new diagnostics keys.
test_manager.pyandtest_scanner.pyupdated for the three new keys.
🤖 Generated with Claude Code
Quality Report
Changes: 5 files changed, 67 insertions(+)
Code scan: clean
Tests: failed (FAILED)
Branch hygiene: clean
Generated by Kōan post-mission quality pipeline