Skip to content

perf(persistence): Speed up hydrating a page of cached messages#2820

Open
VelikovPetar wants to merge 2 commits into
masterfrom
feature/optimize_reactions_and_quoted_messages_hydration
Open

perf(persistence): Speed up hydrating a page of cached messages#2820
VelikovPetar wants to merge 2 commits into
masterfrom
feature/optimize_reactions_and_quoted_messages_hydration

Conversation

@VelikovPetar

@VelikovPetar VelikovPetar commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Submit a pull request

Linear: FLU-

Github Issue: #

CLA

  • I have signed the Stream CLA (required).
  • The code changes follow best practices
  • Code changes are tested (add some information if not applicable)

Description of the pull request

Two behavior-preserving optimizations to message-page hydration in MessageDao and PinnedMessageDao (the read path behind getMessagesByCid, thread loads, and getMessageById). Both cut redundant SQLite work when turning a page of cached rows into Message objects.

1. Derive own reactions in memory instead of a second query.
Hydration previously fired two reaction queries per page: one for all reactions (getReactionsForMessages) and a second for the current user's subset (getReactionsForMessagesByUserId). The current user's reactions are already contained in the first result, so we now filter them in memory (_ownReactionsFrom) and drop the second query entirely. Reaction.userId maps directly from the reactions.userId column, so the in-memory filter is exactly equivalent to the old SQL predicate, and iteration order (createdAt ASC) is preserved.

2. Don't re-fetch quoted messages already in the page.
Quoted-message previews were always fetched and re-hydrated via a separate query, even when the quoted message was already one of the rows on the current page. _resolveQuotedMessages now rebuilds in-page quotes from the maps already loaded for the page (reactions, poll, user, location) and only queries the DB for quotes that fall outside the page. In-page reuse is gated on fetchSharedLocation == true because a quote must always carry its shared location — when the page didn't load locations, those quotes fall through to the DB branch (which always fetches locations), exactly matching the previous behavior. Quotes remain hydrated a single level deep and never carry a draft.

Correctness

  • No behavior change: own-reactions filtering, single-level quote hydration, no-draft-on-quotes, and "a quote always keeps its shared location even when the parent page opts out" are all preserved.
  • getReactionsForMessagesByUserId is left in place (still public, still directly tested); it's just no longer part of the hydration path.

Testing

  • Existing message_dao / pinned_message_dao suites pass (93 tests), including the quote/reaction/location edge cases (hydrates sharedLocation for quoted message even when fetchSharedLocation=false on the parent, hydrates quotes to a single level only, does not hydrate drafts for quoted messages, hydrates multiple latest and own reactions).
  • dart analyze is clean on both changed files.
  • Micro-benchmark (file-backed SQLite, 400 iterations, old 2-query path vs. new 1-query + filter path) confirmed the reaction change is never slower and the gain grows with page size: ~1.22x on a typical page (30 msg × 6 reactors), and ~0.9 ms saved per page on a large one (100 msg × 12 reactors). The win widens further on slower mobile storage and reduces contention on the single main-isolate SQLite connection.

Screenshots / Videos

No UI changes.

Summary by CodeRabbit

  • Performance
    • Improved cached message-page hydration by reducing redundant reaction and quoted-message lookups.
    • Faster loading for message and pinned-message lists, including quoted-message previews.
    • Preserved shared-location details while avoiding unnecessary refetching of quoted messages already in the loaded page.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: aecefcff-b577-4b0c-b415-98ce94271792

📥 Commits

Reviewing files that changed from the base of the PR and between 5f7f33c and eb15e86.

📒 Files selected for processing (3)
  • packages/stream_chat_persistence/CHANGELOG.md
  • packages/stream_chat_persistence/lib/src/dao/message_dao.dart
  • packages/stream_chat_persistence/lib/src/dao/pinned_message_dao.dart

📝 Walkthrough

Walkthrough

Cached message and pinned-message hydration now fetches reactions once per batch, derives current-user reactions in memory, and reuses loaded page messages when resolving quoted previews. Out-of-page quotes continue to load with shared locations.

Changes

Message hydration optimization

Layer / File(s) Summary
Batch reaction and quote hydration
packages/stream_chat_persistence/lib/src/dao/message_dao.dart
MessageDao derives own reactions from fetched reactions and resolves quoted messages through page reuse or out-of-page loading.
Pinned message hydration
packages/stream_chat_persistence/lib/src/dao/pinned_message_dao.dart
PinnedMessageDao applies the same reaction and quoted-message hydration strategy.
Performance changelog
packages/stream_chat_persistence/CHANGELOG.md
Adds a note describing the faster cached hydration behavior.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

Suggested reviewers: xsahil03x, renefloor

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main performance change to cached message-page hydration in persistence.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/optimize_reactions_and_quoted_messages_hydration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@VelikovPetar
VelikovPetar marked this pull request as ready for review July 16, 2026 13:16
@codecov

codecov Bot commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 71.28%. Comparing base (5f7f33c) to head (eb15e86).

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #2820   +/-   ##
=======================================
  Coverage   71.28%   71.28%           
=======================================
  Files         430      430           
  Lines       26934    26967   +33     
=======================================
+ Hits        19199    19223   +24     
- Misses       7735     7744    +9     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant