perf(poll): adaptive back-off when the backend responds slowly - #119
Merged
Conversation
Polling /api/state at the 500ms modal-open cadence against a backend that takes seconds to answer (e.g. host under memory pressure) just stacks requests it can't keep up with — starving the keystroke round-trip that shares the backend. Add an adaptive floor: when responses are slow, widen the poll cadence so the backend can recover. usePolling now measures each poll's latency (EWMA) and counts superseded polls (aborted before completing because the next tick fired — the signal the EWMA can't see when latency exceeds the interval), exposing a `degraded` flag via hysteresis (enter > 800ms or 2 supersedes; exit only when clearly recovered). App threads it into pickPollInterval, which floors the cadence at DEGRADED_POLL_MS (5s) when degraded — overriding the modal/active/input tiers but never lowering an already-slower one. With batched collect_state (~30ms /api/state) this never trips in the common case; it's a safety net for degraded conditions. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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
A safety net for responsiveness under load: when
/api/stateis slow, the dashboard now automatically widens its poll cadence instead of stacking requests onto a struggling backend.The dashboard polls
/api/stateevery 500 ms while a terminal modal is open. If the backend is slow to answer (e.g. the host is swapping, or pre-#118 whencollect_stateforked a tmux subprocess per window), those polls pile up and contend with the keystroke round-trip that shares the backend — which is what makes typing lag. Backing off lets the backend recover.How
usePollingnow measures each poll's wall-clock latency (EWMA) and counts superseded polls — ones aborted by the next tick before they completed. The supersede count is the signal the EWMA alone can't see: when latency exceeds the poll interval, fetches never finish to be measured, so "we keep aborting in-flight polls" is what tells us we're outpacing the backend.nextDegraded(current, ewmaMs, supersedes)applies hysteresis — enter degraded when smoothed latency > 800 ms or ≥ 2 consecutive supersedes; exit only when latency is clearly low and polls are completing again (no flapping).usePollingexposes adegradedflag;pickPollInterval(..., degraded)floors the cadence atDEGRADED_POLL_MS(5 s) when degraded — overriding the modal/active/input tiers, but never lowering an already-slower interval.With #118 (batched
collect_state, ~30 ms/api/state) this never trips in the common case — it only engages when the backend is genuinely degraded.Tests (TDD)
pollTier):updateLatencyEwmaseeding/smoothing;nextDegradedhysteresis + supersede escalation + null-sample handling;pickPollIntervaldegraded floor (overrides modal/input, never lowers a slower interval, no-op when not degraded). 22 tests.usePolling: flags degraded on high latency; stays clear on fast responses; flags degraded after repeated supersedes. Watched each fail (nodegraded), then pass.Notes
/api/statespeedup) — this is the resilience layer on top.🤖 Generated with Claude Code