feat: track allocation zero transitions for stuck-slot diagnostics - #416
feat: track allocation zero transitions for stuck-slot diagnostics#416bluetoothbot wants to merge 3 commits into
Conversation
Adds diagnostics-only instrumentation for investigating ESPHome proxies that report ``slots=0/N free`` indefinitely (#340). Per-source tracking of free->0 and 0->non-zero transitions, plus a repeat counter for allocation updates that arrive while already stuck at zero, is exposed in ``async_diagnostics()`` so the next reporter can attach concrete evidence to a bug report. Allocation changes also emit a DEBUG log line with previous->new state and the affected scanner's ``_connect_in_progress`` / ``_connect_failures`` snapshot, giving habluetooth-side context for each transition. No warnings, no public API, no behavior change. Bookkeeping uses a sibling ``_allocation_transitions`` dict on the manager mutated in place, so the steady state on the allocation path stays allocation-free. Refs #340. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #416 +/- ##
==========================================
+ Coverage 94.50% 94.53% +0.03%
==========================================
Files 14 14
Lines 1911 1941 +30
Branches 230 236 +6
==========================================
+ Hits 1806 1835 +29
Misses 62 62
- Partials 43 44 +1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
| previous_free = previous.free if previous is not None else None | ||
| new_free = ha_slot_allocations.free | ||
| transitions = self._allocation_transitions | ||
| if new_free == 0: |
There was a problem hiding this comment.
@bluetoothbot gh_request
This code block on line 982 - 1007 is overly complex and should be simplified
Collapse the four-branch nested conditional in `async_on_allocation_changed` into a single `transitions.get(source)` lookup plus three flat branches: fresh-entry creation, transition-into-zero refresh, and stuck-at-zero bump. Behavior preserved (last_recovery_at remains untouched on re-entry into zero); -9 lines, no .pxd change, no public API change. Addresses bdraco's review on #416. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Addressed in fdb889a — collapsed the four-branch nested conditional into a single |
| entry[2] += 1 | ||
| elif previous_free == 0 and entry is not None: | ||
| # Recovery from zero. | ||
| entry[1] = monotonic_time_coarse() |
There was a problem hiding this comment.
@bluetoothbot gh_request
Since the shape is defined as 0,1,2 and they are known values use UPPER constants for and make them cdefs so cython doesn't have to lookup Python values
Replace magic 0/1/2 indices into the per-source allocation-transition list with named TRANSITION_LAST_ZERO_AT / TRANSITION_LAST_RECOVERY_AT / TRANSITION_ZERO_REPEAT_COUNT module constants, declared as `cdef int` in manager.pxd so Cython inlines them as C constants rather than looking up Python values on every indexed access. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Addressed in 73b0904 — replaced the 0/1/2 indices with |
What
Diagnostics-only instrumentation for ESPHome proxies that report
slots=0/N freeindefinitely (#340). Tracksfree → 0/0 → non-zerotransitions per source plus a repeat counter for stuck-at-zero updates, and surfaces them inasync_diagnostics(). Allocation changes also emit a DEBUG line with previous→new state and scanner-side_connect_in_progress/_connect_failures.Why
Closes — not yet; this is Phase 1 of the plan on #407. The previous PR detected the symptom but @bdraco asked for the root cause. The slot count is owned by the proxy and travels through
aioesphomeapiandbleak-esphomebefore habluetooth sees it, so a habluetooth-only fix can't address the cause. This PR adds the bare minimum the next #340 reporter needs to attach concrete evidence (transition timestamps + a habluetooth-side connection-state snapshot) without changing user-facing behavior. Phase 2 (lifecycle audit) and Phase 3 (upstreambleak-esphome/ firmware coordination) follow once data lands.How
BluetoothManager._allocation_transitions: dict[str, list]— sibling tracking dict keyed by source, value is[last_zero_at, last_recovery_at, zero_repeat_count], mutated in place to keep the allocation callback path allocation-free in the steady state.async_on_allocation_changedupdates the transition entry and, when_debugis set, logs a single DEBUG line covering both the allocation delta and the scanner-level_connect_in_progress/_connect_failuressnapshot from the affectedBaseHaScanner.async_diagnostics()surfaces atransitionsblock (withlast_zero_seconds_ago/last_recovery_seconds_agorendered relative to read time, pluszero_repeat_count) under each source inallocations._allocationsin_async_unregister_scanner_internal.manager.pxdadds a singlecdef public dict _allocation_transitionsline — no other Cython changes.Testing
→0, repeat updates, recovery, re-entry resets repeat count), DEBUG log content, and cleanup on scanner unregister.test_diagnosticsupdated to include the newtransitionsblock.🤖 Generated with Claude Code
Quality Report
Changes: 3 files changed, 205 insertions(+), 1 deletion(-)
Code scan: clean
Tests: failed ([Errno 13] Permission denied: 'pytest')
Branch hygiene: clean
Generated by Kōan post-mission quality pipeline