Files
opencode/specs/11-layout-prefetch-memory-budget.md
2026-01-27 15:25:07 -06:00

104 lines
3.1 KiB
Markdown

## 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)