mirror of
https://github.com/stack-auth/stack.git
synced 2026-07-20 21:29:36 +08:00
Fixes the session replay player making the page laggy/unresponsive
during playback. Four compounding main-thread bottlenecks:
1. **Mini-tab re-seek every tick (main freeze cause).** Every 200ms TICK
emitted `sync_mini_tabs`, which calls `replayer.pause(offset)` on every
non-active tab. In rrweb v1 that's a full synchronous seek (rebuild from
last FullSnapshot + fast-forward all events), easily 100ms–1s+ of
blocking work per tab, 5×/sec. Now throttled to
`MINI_TAB_SYNC_INTERVAL_MS` (2s) in the state machine, reset on SEEK so
seeks re-sync promptly, and the executor skips tabs that aren't actually
rendered as mini thumbnails.
2. **Full-page re-render every 200ms.** Every dispatch force-rendered
the whole page component. Added `areStatesRenderEquivalent()` —
dispatches that only change playback bookkeeping fields
(`currentGlobalTimeMsForUi`, etc.) skip the React re-render; mini-tab
visibility is refreshed at the throttled sync cadence instead.
3. **Timeline re-rendered at 60fps.** The transport bar rAF loop used
`setState(currentTime)`, re-rendering the whole bar including up to 2000
marker divs every frame. Current time / progress are now written
directly to DOM refs, and the markers lane is extracted into a memoized
`TimelineMarkersLane`.
4. **Synchronous `addEvent` floods.** Background chunk processing now
yields to the event loop between chunks so feeding large batches into
live replayers doesn't block playback.
Added machine tests for the sync throttle, seek re-sync, and render
equivalence (117 passing).
Link to Devin session:
https://app.devin.ai/sessions/7d53fb70217d4ebaa77b9dbac2ba1f54
Requested by: @Developing-Gamer
<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Fixes session replay playback freezing by removing main-thread
bottlenecks in the player and timeline. Playback stays smooth and
responsive; background loads finish faster in hidden tabs.
- **Bug Fixes**
- Throttled mini-tab syncing to `MINI_TAB_SYNC_INTERVAL_MS` (2s);
re-syncs on the next tick after seeks and when buffering resumes
(regardless of page age); skips non-rendered tabs to avoid `rrweb` full
seeks; refreshes mini-tab visibility on the throttled cadence.
- Skipped React re-renders for playback bookkeeping via
`areStatesRenderEquivalent`.
- Stopped 60fps timeline re-renders by writing time/progress to DOM refs
and memoizing the markers lane.
- Yield between event chunks only when the tab is visible; skip yields
in hidden tabs to avoid clamped timers and speed up background loads.
- Added tests for sync throttling, post-seek re-sync (immune to page
age), and render-equivalence.
<sup>Written for commit
|
||
|---|---|---|
| .. | ||
| public | ||
| scripts | ||
| src | ||
| .env | ||
| .env.development | ||
| .eslintrc.cjs | ||
| .gitignore | ||
| .npmrc | ||
| components.json | ||
| DESIGN-GUIDE.md | ||
| instrumentation-client.ts | ||
| LICENSE | ||
| next.config.mjs | ||
| package.json | ||
| postcss.config.js | ||
| tailwind.config.ts | ||
| tsconfig.json | ||
| vitest.config.ts | ||