fix(trakt): load the watched cache before a local watched write - #763
Merged
Merged
Conversation
Every local watched write persists the whole in-memory watched cache as the profile's snapshot. Right after launch or a profile switch the cache can still be unloaded, so a mark made then replaced the stored history with just that title - and an unmark removed the stored lists entirely. A profile without Cloud sync cannot get that history back, and the shortened snapshot is also what the next cloud push exports. All seven writers (film, episode and season, watched and unwatched, and the Supabase-only episode path) now load the cache first. The call returns at once when the cache is already loaded; only the first write after launch or a profile switch waits for the load. initializeWatchedCache alone is not enough for a write: when it waits on another caller's load that gets cancelled (Home restarts its tick pass while rows land at launch) it returns unloaded, and a load that overlaps a profile switch or cloud restore can finish with the history from before it. A reset counter lets the write detect both and load again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NtiKMWmfLDYKRRb5CBptwd
added 4 commits
September 27, 2026 16:24
Owner
|
Thanks, this fixes a real history-loss risk. I integrated it with #759 and made the pre-write load local-only, so marking a title does not wait for a full network sync. Snapshot writes merge transactionally, preserve concurrent changes, and reject a changed profile/cache scope. Added coverage for cancellation, cloud restore, concurrent marks, and stale remote history after an unwatch. All eight snapshot tests pass within the 128-test batch; both Android variants compile. Original author commits are preserved. |
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.
Summary
Marking a title watched or unwatched before the watched cache has loaded (the first seconds after launch or a profile switch) overwrote the profile's stored local watched history:
1, 2, 3→41, 2, 3+ episodes → nothing (both keys removed)A profile without Cloud sync cannot get that history back, and the shortened snapshot is also what the next cloud push exports (
exportLocalWatched…ForProfiles).Found while preparing the follow-up to #758; this is a separate fix in
TraktRepositoryonly.Cause
Every local watched write updates the in-memory cache and then persists the whole cache as the snapshot (
persistLocalWatchedSnapshotForCurrentProfile). Nothing made sure the cache was loaded first;ensureProfileCacheScope/ a profile switch leave it empty until the nextinitializeWatchedCache.Change
loadWatchedCacheBeforeWrite(), called by all seven local writers before they touch the cache:markMovieWatched,markMovieUnwatched,markEpisodeWatched,markEpisodeWatchedWithoutTraktSync,markEpisodeUnwatched,markSeasonWatched,removeSeasonFromHistory. The player's auto-mark goes throughmarkMovieWatched/markEpisodeWatched. It returns at once when the cache is loaded.initializeWatchedCache()alone is not enough for a write, for two reasons:clearAllProfileCaches,invalidateWatchedCache) can finish with the history from before it.watchedCacheResets, bumped inclearProfileScopedMemoryCachesandinvalidateWatchedCache. It lets the write detect either case and load again: up to three tries, then a final plain load.Price
Testing
TraktLocalWatchedSnapshotTest(Robolectric, same harness asTraktContinueWatchingRefreshTest), 6 tests:mainthe film and episode tests fail ([4], and an unmark leaves nothing); with only a plaininitializeWatchedCache()the last two still fail ([4], and[1, 2, 3, 4]instead of[4, 7, 8]).testSideloadDebugUnitTest+compileSideloadDebugKotlingreen locally; the test-APK run on the fork is green as well.Not changed here (seen while reading)
cacheInitializingis a plain check-then-set, so two loads can still run in parallel. That was already the case and is only narrowed here, not fixed.trakt:/show_trakt:/show_tmdb:), while an unmark removes onlyshow_tmdb:. The snapshot grows, and an unmark from another device may not arrive locally. It needs a separate look.created by Claude (Anthropic) on behalf of @ReichiMD