perf(app): better memory management
This commit is contained in:
103
specs/11-layout-prefetch-memory-budget.md
Normal file
103
specs/11-layout-prefetch-memory-budget.md
Normal file
@@ -0,0 +1,103 @@
|
||||
## Layout prefetch memory budget
|
||||
|
||||
Reduce sidebar hover prefetch from ballooning message caches
|
||||
|
||||
---
|
||||
|
||||
### Summary
|
||||
|
||||
`packages/app/src/pages/layout.tsx` prefetches message history into `globalSync.child(directory)`.
|
||||
|
||||
On hover and navigation, the current implementation can prefetch many sessions (each up to `prefetchChunk = 600` messages + parts). Since the global sync store has no eviction today, scanning the sidebar can permanently grow memory.
|
||||
|
||||
This spec limits how much we prefetch (chunk size + number of sessions) and adds debounced hover prefetch to avoid accidental flooding.
|
||||
|
||||
---
|
||||
|
||||
### Scoped files (parallel-safe)
|
||||
|
||||
- `packages/app/src/pages/layout.tsx`
|
||||
|
||||
---
|
||||
|
||||
### Goals
|
||||
|
||||
- Reduce the maximum amount of data prefetched per session
|
||||
- Limit the number of sessions that can be prefetched per directory per app lifetime
|
||||
- Avoid triggering prefetch for brief/accidental hovers
|
||||
- Keep changes local to `layout.tsx`
|
||||
|
||||
---
|
||||
|
||||
### Non-goals
|
||||
|
||||
- Implementing eviction of already-prefetched data inside global sync (separate work)
|
||||
- Changing server APIs
|
||||
|
||||
---
|
||||
|
||||
### Current state
|
||||
|
||||
- `prefetchChunk` is 600.
|
||||
- Hovering many sessions can enqueue many prefetches over time.
|
||||
- Once prefetched, message/part data remains in memory until reload.
|
||||
|
||||
---
|
||||
|
||||
### Proposed approach
|
||||
|
||||
1. Lower the prefetch page size
|
||||
|
||||
- Change `prefetchChunk` from 600 to a smaller value (e.g. 200).
|
||||
- Rationale: prefetch is for fast first render; the session page can load more as needed.
|
||||
|
||||
2. Add a per-directory prefetch budget
|
||||
|
||||
- Track a small LRU of prefetched session IDs per directory (Map insertion order).
|
||||
- Add `PREFETCH_MAX_SESSIONS_PER_DIR` (e.g. 8-12).
|
||||
- Before queueing a new prefetch:
|
||||
- if already cached in `store.message[sessionID]`, allow
|
||||
- else if budget exceeded and priority is not `high`, skip
|
||||
- else allow and record in LRU
|
||||
|
||||
3. Debounce hover-triggered prefetch
|
||||
|
||||
- For sidebar session entries that call `prefetchSession(..., "high")` on hover:
|
||||
- schedule after ~150-250ms
|
||||
- cancel if pointer leaves before the timer fires
|
||||
|
||||
---
|
||||
|
||||
### Implementation steps
|
||||
|
||||
1. Update constants in `layout.tsx`
|
||||
|
||||
- `prefetchChunk`
|
||||
- add `prefetchMaxSessionsPerDir`
|
||||
|
||||
2. Add `prefetchedByDir: Map<string, Map<string, true>>` (or similar)
|
||||
|
||||
- Helper: `markPrefetched(directory, sessionID)` with LRU behavior and max size.
|
||||
- Helper: `canPrefetch(directory, sessionID, priority)`.
|
||||
|
||||
3. Integrate checks into `prefetchSession(...)`
|
||||
|
||||
4. Debounce hover prefetch in the sidebar session item component (still in `layout.tsx`)
|
||||
|
||||
---
|
||||
|
||||
### Acceptance criteria
|
||||
|
||||
- Prefetch requests never fetch more than the new `prefetchChunk` messages per session
|
||||
- For a given directory, total prefetched sessions does not exceed the configured budget (except current/adjacent high-priority navigations if explicitly allowed)
|
||||
- Rapid mouse movement over the session list does not trigger a prefetch storm
|
||||
|
||||
---
|
||||
|
||||
### Validation plan
|
||||
|
||||
- Manual:
|
||||
- Open sidebar with many sessions
|
||||
- Move cursor over session list quickly; confirm few/no prefetch requests
|
||||
- Hover intentionally; confirm prefetch happens after debounce
|
||||
- Confirm the number of prefetched sessions per directory stays capped (via dev logging or inspecting store)
|
||||
Reference in New Issue
Block a user