perf: shed background polling that crashes the gateway#55
Merged
Conversation
added 6 commits
May 14, 2026 02:58
The dashboard was firing ~120 background requests/minute at the gateway just sitting on a page. Three classes of fix in one branch: Polling intervals (lazier + lighter): - use-dashboard-data: sessions 30s→120s, gateway-status 15s→60s, session-status 30s→180s; refetchOnWindowFocus/Reconnect disabled - use-streaming-message: accepted 30s→90s, handoff 45s→180s so normal gateway lag does not falsely mark messages as failed Hot health probes that compounded the load (removed/disabled): - GatewayConnectionBanner — its own pingGateway loop is too noisy and yields false-offline banners while chat is usable; component returns null until the health logic is rewritten - full-outputs-view: 326-line legacy aggregate output panel deleted Streaming path: - use-streaming-message now lands the user message reliably via /api/send first, then upgrades to streaming; removes the brittle stream-first fallback that was eating retries Server lifecycle: - gateway.ts shutdown handler no longer process.exit(0) during vite dev, so reload churn does not take down the whole dev server Routing tidy: - /operations route + sidebar entry restored so it does not 404 after Agents Hub moved - Misc small fixes: dashboard overflow, mobile tab bar, swipe nav, chat-sidebar active states Net diff: 16 files, ~-400 lines. Validation: - pnpm build ✓ - pnpm test (9/9) ✓
…ppress for auth The 'Gateway unreachable' banner was firing on every chat open during the 1-2s window before SSE connected, even when the gateway was actually fine. It also fired on auth/pairing errors that should be routed through the setup wizard, not a chat-screen toast. Three changes: - Require >=2 consecutive status-query failures before showing the banner (single transient timeout no longer flashes a scary toast) - Suppress for status 401/403 and auth/pair phrases — those belong to the setup wizard, not chat - Drop refetchOnWindowFocus / refetchOnReconnect on the status query; background interval is now 120s (was 60s) since SSE is the real signal The banner remains intact for the real case: gateway truly down for 2+ consecutive probes and SSE not connected.
Two global background loops were still pounding the gateway even off-dashboard:
- SystemMetricsFooter polled /api/system-metrics every 5s everywhere,
and /api/system-metrics itself called gatewayRpc('status').
- WorkspaceShell polled /api/sessions every 15s on every route.
Fix:
- SystemMetricsFooter now takes enabled prop and is only mounted on /dashboard;
its interval is 30s (was 5s)
- WorkspaceShell sessions query now polls at 60s on chat/dashboard and 120s
elsewhere; refetchOnWindowFocus/Reconnect disabled; staleTime raised to 60s
This should remove the last app-wide background gateway hammering while still
keeping the useful surfaces fresh enough.
…probe spam Workspace pairs cleanly with the gateway because it has ONE source of truth (the live SSE connection) and only falls back to an HTTP status probe when SSE actually disconnects. Clawsuite had three competing probes (ping, gateway/status, system-metrics's embedded status RPC) all on intervals, which tripped the OCPlatform gateway circuit breaker within ~30s of idle. Changes: - chat-screen.gatewayStatusQuery: enabled only when SSE is NOT connected; no refetch interval; staleTime 120s. The query runs once on SSE drop, not on a timer. - chat-composer models query: stop polling. Models rarely change; staleTime 5min, refetch on demand. - chat-sidebar recent-issues indicator: 20s -> 60s + refetchOnWindowFocus off. - use-chat-sessions: 30s -> 60s + refetchOnWindowFocus off. After this change, 35s of idle on the chat screen produces ZERO gateway RPC timeouts in the dev log (was tripping the circuit breaker on every cycle).
On mobile via Tailscale, the auth-check query could stall the whole app on the loading splash for 10+ seconds: - 5s fetch timeout + 2 retries with 1s delay = 13s worst case - meanwhile dashboard queries fire in parallel and pile up RPCs Three changes: - auth-check fetch timeout 5s -> 2s (it's a local endpoint, should be <100ms) - retry 2 -> 1, retryDelay 1000 -> 500ms - new splashTimedOut state: if auth-check hasn't responded after 3.5s, we proceed optimistically as authenticated=true, authRequired=false (the underlying middleware still enforces real auth; this just stops the splash from holding the whole UI hostage on a slow network) Defaults also flipped: authenticated defaults to true, authRequired to false, so a missing auth response no longer assumes the worst-case login screen.
Root cause of the 'Could not connect to ClawSuite server' crash screen: - workspace-shell treated any /api/auth-check fetch miss as a fatal app error - in practice the dev server stayed healthy and user messages still sent - result: a transient local fetch hiccup looked like a full app crash Fix: - remove the fatal full-screen authQuery.isError blocker entirely - keep the app live and render a small non-blocking warning banner instead - retain Retry affordance so the auth-check can be re-run without nuking the UI This matches the intended behavior better: if the shell is up and the app is still functioning, do not blank the whole screen because one auth probe had a bad moment.
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.
Why
The Clawsuite dashboard was firing ~120 background requests/minute at the OCPlatform gateway just sitting on a page (50+ active timers across all the dashboard hooks). On laptops and on the gateway side this consistently produced lag and outright crashes. Eric reported this was bad enough that he stopped using the app.
What this branch does
Three classes of fix:
1. Polling intervals — lazier + lighter
use-dashboard-data: sessions 30s→120s, gateway-status 15s→60s, session-status 30s→180srefetchOnWindowFocus: false+refetchOnReconnect: falseon the heavy queriesuse-streaming-message: accepted 30s→90s, handoff 45s→180s so normal gateway lag doesn't falsely mark messages as failed2. Hot health probes removed
GatewayConnectionBannerhad its ownpingGateway()loop that was too noisy and produced false-offline banners while chat was usable. Component now returnsnulluntil the health logic is rewritten to distinguish slow usage/session RPCs from actual gateway loss.full-outputs-view: 326-line legacy aggregate output panel deleted3. Streaming path made reliable-first
use-streaming-messagenow lands the user message reliably via/api/sendfirst, then upgrades to streaming; removes the brittle stream-first fallback that was eating retries4. Server lifecycle
gateway.tsshutdown handler no longerprocess.exit(0)during vite dev, so reload churn doesn't take down the whole dev server5. Routing tidy
/operationsroute + sidebar entry restoredNet diff
16 files, +214 / −611 (≈400 lines removed).
Validation
pnpm build✓pnpm test— 9/9 passing ✓src/routes/api/send.tsrestored frommainafter an interrupted in-progress diff had stubbed it to a hello-worldFollow-up (separate PR)
The right long-term fix is to replace the 50+ polling hooks with one shared SSE stream from
/api/eventsfor gateway-side state changes. That's a bigger refactor; this branch is the surgical polling-pressure relief so the app is usable today.Closes the regression where dashboard usage repeatedly crashed the OCPlatform gateway.