Skip to content

perf(session): speed up session switching with warm cache and staged rendering#15474

Draft
kitlangton wants to merge 3 commits intoanomalyco:devfrom
kitlangton:kitlangton/session-switch-perf
Draft

perf(session): speed up session switching with warm cache and staged rendering#15474
kitlangton wants to merge 3 commits intoanomalyco:devfrom
kitlangton:kitlangton/session-switch-perf

Conversation

@kitlangton
Copy link
Contributor

Summary

  • speed up initial session switch by using warm-cache hydration and a smaller initial message window
  • split message loading into phases (messages first, parts chunked) to unblock first paint
  • cap first turn render and backfill older turns on scroll-to-top instead of eager backfill
  • stage timeline growth for deferred turn windows and keep scroll/hash behavior aligned
  • switch Session.messages to bulk list query path (MessageV2.list) for lower sync overhead

Validation

  • bun run typecheck (packages/app)
  • bun run typecheck (packages/opencode)
  • bun test src/pages/session/use-session-hash-scroll.test.ts src/pages/session/scroll-spy.test.ts (packages/app)
  • bun test test/session/message-v2.test.ts test/session/session.test.ts (packages/opencode)

sync.tsx:
- Remove redundant .filter((m) => !!m?.id) in fetchMessages — the
  preceding filter already guarantees x.info.id is truthy, so this
  was a wasted array allocation + iteration on every session switch.
  Removing it noticeably speeds up large session loads.
- Simplify part loading from a multi-chunk rAF/setTimeout scheduler
  to a single requestAnimationFrame + batch. The chunking was needed
  when parts used reconcile, but with direct assignment the cost is
  low enough to do in one pass. Still defers to the next frame so
  message skeletons paint first.
- Remove fresh flag — reconcile against empty is cheap enough that
  the conditional wasn't worth the complexity.
- Minor cleanups: if/else instead of two independent ifs in
  applyOptimisticAdd, inline hasSession IIFE, use cached variable
  in todo() instead of re-reading globalSync, inline nextLimit.

message-v2.ts:
- Extract fetchParts() and assembleMessages() helpers shared by
  stream() and list(), eliminating duplicated Map-building and
  row-to-WithParts assembly logic.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant