Skip to content

Reader: optimize ReaderPostTable.comparePosts#22810

Open
nbradbury wants to merge 6 commits intotrunkfrom
nick/improve-discover-performance
Open

Reader: optimize ReaderPostTable.comparePosts#22810
nbradbury wants to merge 6 commits intotrunkfrom
nick/improve-discover-performance

Conversation

@nbradbury
Copy link
Copy Markdown
Contributor

@nbradbury nbradbury commented Apr 21, 2026

Background

ReaderPostTable.comparePosts() is the gate that ReaderPostLocalSource.saveUpdatedPosts uses to decide what to do after the Reader fetches posts from the server. Its three-valued result drives three distinct behaviors in the caller:

  • HAS_NEW / CHANGED — runs the full write path: gap detection, delete-and-reinsert for REQUEST_REFRESH, addOrUpdatePosts, bookmark pseudo-id fixups, gap-marker placement, and the UI change events those trigger.
  • UNCHANGED — skips the write path entirely, avoiding re-inserting rows we already have and suppressing UI refreshes when nothing has actually changed.
  • UNCHANGED + REQUEST_OLDER_THAN_GAP — a special case where a gap-fill request returned nothing new, signalling that the gap marker was bogus and should be removed.

In short, comparePosts answers "does the server response differ from what's already stored?" — used to short-circuit writes and UI refreshes on no-op refreshes, and to distinguish "gap filled nothing" from a real update.

Description

ReaderPostTable.comparePosts() previously ran one getBlogPost() SQL query per incoming post, each materializing a full ReaderPost that was thrown away after the isSamePost check. For a typical Reader refresh of ~20 posts that's 20 round-trips to SQLite.

This change replaces those N queries with a single SELECT keyed by (blog_id, post_id) via OR clauses, loads the matching rows into an in-memory map (using the existing COLUMN_NAMES_NO_TEXT projection and getPostFromCursor), and has the comparison loop probe the map.

Fix to isSamePost text comparison

While optimizing this, we noticed comparePosts was also effectively never returning UNCHANGED for full-text streams. It loads existing rows with excludeTextColumn=true, but ReaderPost.isSamePost unconditionally compared post.getText().equals(existingPost.getText()). Since the stored side was always empty, any incoming post with a non-empty body was reported as CHANGED — forcing saveUpdatedPosts down the full write path on every refresh.

To fix this, isSamePost now has an overload with a compareText flag so UNCHANGED now fires correctly - and more often - on true no-op refreshes and the caller can skip re-inserting rows and firing UI change events.

Testing instructions

Reader refresh smoke test:

  1. Open the Reader and pull to refresh a followed tag / site stream.
  • Verify posts load and any new/changed posts are reflected in the list.
  1. Scroll to trigger paging / background refresh of the same stream.
  • Verify no duplicate or missing posts appear compared to prior behavior.

Replaces N per-post getBlogPost() lookups with a single SELECT that
fetches only the columns compared by ReaderPost.isSamePost, probed via
an in-memory map during the comparison loop.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@dangermattic
Copy link
Copy Markdown
Collaborator

dangermattic commented Apr 21, 2026

1 Warning
⚠️ PR is not assigned to a milestone.

Generated by 🚫 Danger

@nbradbury nbradbury changed the title Reader: optimize ReaderPostTable.comparePosts with a single SQL query Reader: optimize ReaderPostTable.comparePosts Apr 21, 2026
nbradbury and others added 3 commits April 21, 2026 16:03
Fetch all non-text columns in the single batched query and reuse
ReaderPost.isSamePost, removing the bespoke PostComparisonRow helper.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the StringBuilder loop with String.join + Collections.nCopies
and drop the descriptive comment that duplicated the helper's name.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Callers that loaded an existing row without the text column (comparePosts)
previously forced every post with a non-empty body to be reported as
CHANGED because the stored text was always empty. The new overload lets
those callers opt out of the text comparison, so UNCHANGED now fires on
true no-op refreshes. Default remains true to preserve existing callers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@wpmobilebot
Copy link
Copy Markdown
Contributor

wpmobilebot commented Apr 21, 2026

App Icon📲 You can test the changes from this Pull Request in Jetpack Android by scanning the QR code below to install the corresponding build.

App NameJetpack Android
Build TypeDebug
Versionpr22810-b6bdf43
Build Number1488
Application IDcom.jetpack.android.prealpha
Commitb6bdf43
Installation URL05p7jaddn753g
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@wpmobilebot
Copy link
Copy Markdown
Contributor

wpmobilebot commented Apr 21, 2026

App Icon📲 You can test the changes from this Pull Request in WordPress Android by scanning the QR code below to install the corresponding build.

App NameWordPress Android
Build TypeDebug
Versionpr22810-b6bdf43
Build Number1488
Application IDorg.wordpress.android.prealpha
Commitb6bdf43
Installation URL6ivcgpmbken38
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 21, 2026

Codecov Report

❌ Patch coverage is 0% with 19 lines in your changes missing coverage. Please review.
✅ Project coverage is 37.15%. Comparing base (79d417d) to head (b6bdf43).

Files with missing lines Patch % Lines
...rg/wordpress/android/datasets/ReaderPostTable.java 0.00% 17 Missing ⚠️
.../java/org/wordpress/android/models/ReaderPost.java 0.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##            trunk   #22810      +/-   ##
==========================================
- Coverage   37.15%   37.15%   -0.01%     
==========================================
  Files        2314     2314              
  Lines      124294   124310      +16     
  Branches    16882    16884       +2     
==========================================
  Hits        46185    46185              
- Misses      74366    74382      +16     
  Partials     3743     3743              

☔ View full report in Codecov by Sentry.
📢 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.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@nbradbury nbradbury marked this pull request as ready for review April 22, 2026 18:13
@nbradbury nbradbury requested a review from adalpari April 22, 2026 18:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants