fix(desktop): stop timeline cap from evicting scrolled-in history mid-read#1473
Open
tlongwell-block wants to merge 1 commit into
Open
fix(desktop): stop timeline cap from evicting scrolled-in history mid-read#1473tlongwell-block wants to merge 1 commit into
tlongwell-block wants to merge 1 commit into
Conversation
…-read A channel timeline cache holding more than MAX_TIMELINE_MESSAGES (2000) content events — i.e. the user paged back to an old day in a busy channel — was silently trimmed back to the newest 2000 by the next capped merge: any live event, optimistic send, or fresh-history revalidation. The rows being read vanished out from under the reader, appearing as randomly missing messages for old dates (all present right after scrollback, partially or fully gone seconds later). Scrollback merges were already uncapped (isOlderHistoryPage special case), which is what made the loss intermittent instead of consistent. Fix: no merge into a potentially-on-screen timeline applies the cap. - mergeTimelineHistoryMessages: sort + dedupe only (drops the isOlderHistoryPage split — both branches now behave identically). - mergeTimelineCacheMessages (live/optimistic/single-event): same. - The DOM bound is enforced where nothing is rendered from the cache: the cold-snapshot paint (already capped) and a new trim-on-leave in useChannelSubscription cleanup, which re-applies normalizeTimelineMessages when the user leaves the channel. Regression test: expanded 2537-event cache + fresh newest-page revalidation merge — all old rows survive. Co-authored-by: Tyler Longwell <tlongwell@block.xyz> Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
wesbillman
approved these changes
Jul 2, 2026
This was referenced Jul 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug (Tyler's "missing messages" report)
Sometimes all of June 12's messages show in #buzz-bugs; sometimes one or two; sometimes none.
Root cause:
MAX_TIMELINE_MESSAGES = 2000cap, applied inconsistently across merge paths. #buzz-bugs holds 2,770 kind-9 events since June 12 (measured via CLI pagination against the relay; the June-12 set itself is a stable 37 ids across 8+ repeated reads — the relay is fine). June 12 sits past the cap.The flicker mechanism:
isOlderHistoryPagespecial case) — you can page back to June 12 and see all 37. Cache now holds 2,700+ content events.mergeTimelineCacheMessages), or a fresh-history revalidation whose merge fell into the non-older branch ofmergeTimelineHistoryMessages— trims the cache back to the newest 2,000, silently evicting June 12 while it's on screen.Reproduced with the repo's own
normalizeTimelineMessages: 37 "June 12" rows + 2,764 newer rows → 0 survivors.Predates the current perf PRs — the cap is from #212; #1105 refined it to count content events; #1452's longer-lived caches (gcTime 60min + persisted snapshots) made caches big enough for people to hit it routinely.
The fix
Principle (per review discussion with Wren): no merge into a potentially-on-screen timeline applies the cap; the cap is enforced only at moments when nothing is rendered from the cache.
mergeTimelineHistoryMessages→ sort + dedupe only. Drops theisOlderHistoryPagebranch split entirely — older-page merges and newest-window revalidations now behave identically, so a background refetch over an expanded cache can no longer evict scrolled-in history (this covers the revalidation-without-live-event case).mergeTimelineCacheMessages(live append / optimistic send) → same, now an alias ofmergeMessages.placeholderData) — already capped, unchanged;useChannelSubscription's cleanup — leaving the channel re-appliesnormalizeTimelineMessages, so a long session can't grow caches unboundedly.Net: while a channel is open its timeline only grows (bounded in practice by scrollback batches, 200/page × 3/pass); the moment you leave, it trims to 2,000 newest.
Validation
pnpm --dir desktop test: 1495/0.pnpm check,pnpm --dir desktop typecheck: clean.Fixes the merge-freeze blocker reported in #buzz-gui-performance.