Skip to content

Stabilize watchlist paging during sync updates#129

Open
anod wants to merge 1 commit into
masterfrom
fix/watchlist-recent-paging-overlap
Open

Stabilize watchlist paging during sync updates#129
anod wants to merge 1 commit into
masterfrom
fix/watchlist-recent-paging-overlap

Conversation

@anod

@anod anod commented Jun 10, 2026

Copy link
Copy Markdown
Owner

Summary

  • Fix the duplicate Lazy key crash by making each WatchListPagingSource generation page over a stable app row-id snapshot
  • Prevent sync/status updates from moving the same app across OFFSET windows while Paging appends more pages
  • Keep recently-discovered ordering valid for row snapshots and add regression coverage for rows moving across page offsets

Crash investigation

Crashlytics logs for the sample event showed manual sync running while the watchlist was paging. During sync, apps including Chrome were marked as viewed, changing app status and reordering the STATUS DESC list between an initial 60-item load and later appends. That can emit the same app row in multiple pages and trigger Compose duplicate Lazy keys.

Validation

  • ./gradlew :app:assembleDebug
  • ./gradlew :app:testDebugUnitTest
  • ./gradlew :app:lintDebug
  • ./gradlew ktlintCheck

Copilot AI review requested due to automatic review settings June 10, 2026 19:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the app-list SQL used by watchlist-style paging to ensure each app_list row joins to at most one changelog row per (app_id, version_code), preventing duplicate emitted rows that can trigger Compose Lazy key collisions when legacy/invalid changelog duplicates exist.

Changes:

  • Update AppListTable.Queries.createAppsListQuery() to join via a derived “latest changelog row per (app_id, code)” subquery, then join back to changelog by _id.
  • Add a regression test that simulates legacy duplicate changelog rows (by dropping the unique index) and asserts the paging result does not duplicate apps.
  • Update query-shape assertions in AppListTableQueriesTest to reflect the new join structure.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
app/src/main/java/com/anod/appwatcher/database/AppListTable.kt Changes the dynamic app-list query to join changelog through a “latest per version” derived table to prevent duplicated app rows.
app/src/test/java/com/anod/appwatcher/database/AppListTableQueriesTest.kt Updates assertions to validate the new derived-table join shape.
app/src/test/java/com/anod/appwatcher/watchlist/WatchListPagingSourceRoomTest.kt Adds a regression test covering duplicate changelog versions and a helper to insert legacy-duplicate data.

Comment thread app/src/main/java/com/anod/appwatcher/database/AppListTable.kt
@anod anod force-pushed the fix/watchlist-recent-paging-overlap branch from b5facaf to a47be1e Compare June 10, 2026 19:38
@anod anod changed the title Prevent duplicate watchlist rows from changelog joins Stabilize watchlist paging during sync updates Jun 10, 2026
@anod anod force-pushed the fix/watchlist-recent-paging-overlap branch from a47be1e to f62c7ba Compare June 11, 2026 07:38
Copilot AI review requested due to automatic review settings June 11, 2026 07:38

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

Comment thread app/src/main/java/com/anod/appwatcher/watchlist/WatchListPagingSource.kt Outdated
@anod anod force-pushed the fix/watchlist-recent-paging-overlap branch from f62c7ba to 671138a Compare June 11, 2026 08:10
Capture a stable app row-id snapshot per WatchListPagingSource generation and page by those row IDs so status changes during sync cannot move the same app across offset windows and emit duplicate Lazy keys.

Add regression coverage for rows moving across paging offsets while keeping recently-discovered row snapshot ordering valid.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@anod anod force-pushed the fix/watchlist-recent-paging-overlap branch from 671138a to de06f77 Compare June 11, 2026 08:57
Copilot AI review requested due to automatic review settings June 11, 2026 08:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 8 changed files in this pull request and generated 1 comment.

Comment on lines 56 to +70
val sortId = prefs.sortIndex
var limit = initialLimit
val items = mutableListOf<SectionItem>()
if (offset == 0 && config.showRecentlyInstalled) {
items.add(SectionItem.Recent)
// limit is already reduced in calculateOffsetAndLimit, but keep max guard
limit = max(0, limit)
}

val data = AppListTable.Queries.loadAppList(
sortId, config.showRecentlyDiscovered, config.tagId, filterQuery, SqlOffset(offset, limit), database.apps()
)
val rowIds = loadRowIdSnapshot(sortId)
val pageRowIds = if (offset >= rowIds.size) {
emptyList()
} else {
rowIds.subList(offset, minOf(rowIds.size, offset + limit))
}
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.

2 participants