Skip to content

fix(sessions): stop the Hub background-refresh re-render loop - #566

Open
RonenMars wants to merge 2 commits into
mainfrom
fix/hub-render-loop
Open

fix(sessions): stop the Hub background-refresh re-render loop#566
RonenMars wants to merge 2 commits into
mainfrom
fix/hub-render-loop

Conversation

@RonenMars

Copy link
Copy Markdown
Owner

Closes #564 and #565.

Problem

On-device tracing of the Hub while it shows "Showing cached data — syncing" (both servers paired) found ProjectsHub re-rendering continuously — hundreds of commits at ~6/sec during warm-up, never settling.
A per-commit "why did this render" probe attributed the churn to two independent sources, neither addressed by the throttle/memo work in #563.

Fixes

  1. serverFetchStatus store (serverFetchStatus store re-renders subscribers on every HTTP response (fresh object on unchanged status) #564): recordSuccess/recordReady allocated a new statuses object with a fresh Date.now() on every successful HTTP response, even when the status was already ok, so every API call re-rendered every subscriber.
    Both now return the same state when nothing changed, so zustand does not notify — recordReady still clears warming_upok.
  2. refreshEagerConversations (conversation_updated WS frames trigger a full eager-conversations re-drain per frame (sustained Hub re-render loop) #565): every conversation_updated WS frame invalidated the whole conversations-eager query, re-draining every page of every server per liveness ping.
    It is now debounced (trailing 1s) so a burst of frames collapses into a single re-drain.
    The long-term follow-up noted in conversation_updated WS frames trigger a full eager-conversations re-drain per frame (sustained Hub re-render loop) #565 is to patch the single affected conversation in the eager cache instead of re-draining everything.

Verification

Unit: 12 tests pass across serverFetchStatus and eagerCacheSync, including new cases for same-identity no-op writes, warming_upok still clearing, and debounce/coalesce behavior.
On-device (iOS simulator, two real servers, EXPO_PUBLIC_OPEN_TRACE=1): render rate dropped from ~6/sec to ~0.65/sec and fetchStatuses disappeared from the render-driver breakdown after the store guard.

On-device tracing of the "Showing cached data — syncing" state found ProjectsHub re-rendering continuously (hundreds of commits, ~6/sec, never settling) from two sources a why-did-this-render probe pinned down.
serverFetchStatus.recordSuccess/recordReady wrote a fresh statuses object with a new Date.now() on every successful HTTP response even when the status was already ok, so every API call re-rendered every subscriber (closes #564).
Guard both so an unchanged ok status returns the same state and zustand does not notify — recordReady still clears warming_up.
conversation_updated WS frames each invalidated the whole conversations-eager query, re-draining every page of every server per liveness ping (closes #565).
Debounce refreshEagerConversations so a burst of frames collapses into one trailing re-drain.
Measured effect on-device: render rate dropped from ~6/sec to ~0.65/sec and fetchStatuses disappeared as a render driver.
…ent bundle-cache traps

Two on-device tracing traps that surface as "the thing I'm tracing never happened" rather than an error, both found while verifying the Hub re-render fixes.
A shell-exported EXPO_PUBLIC_* flag does not inline into the bundle — Expo reads these from .env/.env.local files, so the flag must live there and be verified by grepping the served expo-router entry bundle.
The dev client serves a disk-cached bundle that ignores deep-link, /reload, and plain launch, so a reinstall is needed to force a fresh fetch from Metro.
Adds both to docs/troubleshooting.md under "Measuring the wrong thing" and a pointer in CLAUDE.md.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

serverFetchStatus store re-renders subscribers on every HTTP response (fresh object on unchanged status)

1 participant