Skip to content

Fix: Resolve infinite sync loops causing gigantic unnecessary restorations#1640

Open
Lolle2000la wants to merge 4 commits into
komikku-app:masterfrom
Lolle2000la:fix-unnecessary-restores
Open

Fix: Resolve infinite sync loops causing gigantic unnecessary restorations#1640
Lolle2000la wants to merge 4 commits into
komikku-app:masterfrom
Lolle2000la:fix-unnecessary-restores

Conversation

@Lolle2000la

Copy link
Copy Markdown
Contributor

Currently, the synchronization functionality suffers from a severe "ping-pong" loop where devices continuously trigger full restorations of hundreds or thousands of mangas unnecessarily. This causes massive, long-running sync operations even when no actual changes have been made by the user.

Screenshot showing the concluding notification after a restoration with 21:40 minutes.

The above screenshot shows why I created this PR, with a synchronization of literally nothing taking 21 minutes and 40 seconds.

This runaway restoration loop is caused by a few specific oversights in how the sync system compares and updates local vs. remote states:

The problems

  1. Chapter Version Skipping (MangaRestorer.kt): When a remote chapter is downloaded that has an identical read/bookmark state to the local database but a higher version, the restorer optimized the process by completely skipping the chapter. Because it was skipped, the local chapter's version was never updated to match the remote. On the very next sync, SyncManager.areChaptersDifferent detected the mismatched versions and falsely flagged the entire manga as modified, triggering another massive restoration.
  2. Missing Title Mapping (MangaRestorer.kt): The Manga.copyFrom method was missing several critical fields, most notably ogTitle and the sync state flags (chapterFlags, viewerFlags, updateStrategy). When a remote manga was restored, its title was never saved to the local database.
  3. Volatile Lookup Keys (SyncManager.kt & SyncService.kt): The sync logic used display metadata like title, author, and chapter name in its lookup maps. Because the local database's ogTitle was never updating (due to the bug above), SyncManager would look for the old title in memory, fail to find the remote's new title, assume it was a brand-new manga, and trigger a full restoration. The database would correctly deduplicate it upon insertion, but the loop would repeat indefinitely.

Bug-Flow

sequenceDiagram
    participant A as Device A
    participant S as Sync Server
    participant B as Device B

    Note over A, B: Both devices initially up-to-date
    A->>A: Sets 1 chapter to read
    A->>S: Synchronizes changes

    B->>S: Starts synchronization
    S-->>B: Downloads backup
    Note over B: Bug triggered: Mismatched keys and skipped versions
    B->>B: Restores hundreds/thousands of unaffected mangas
    B->>S: Uploads "merged" state with mismatched versions

    Note over A: User makes NO changes
    A->>S: Synchronizes again
    S-->>A: Downloads backup from B
    Note over A: Bug triggered: Mismatched keys and skipped versions
    A->>A: Restores hundreds/thousands of unaffected mangas
    A->>S: Uploads "merged" state again

    Note over A, B: The Ping-Pong Loop continues indefinitely...
Loading

The Solution

This PR implements the minimal structural fixes required to permanently terminate these sync loops:

  • Chapter Version Alignment: Updated MangaRestorer.restoreChapters to explicitly adopt the remote chapter's version (via a lightweight .copy(id = dbChapter.id)) even if the content state is identical, permanently breaking the version mismatch loop.
  • Stable Identifiers: Changed mangaCompositeKey in SyncService and the localMangaMap in SyncManager to strictly use Pair(source, url). Chapter keys were reverted to url. This guarantees mangas and chapters are matched purely by their immutable backend IDs, preventing volatile display names from tricking the app into restoring them.
  • Correct Database Mapping: Fixed Manga.copyFrom in MangaRestorer to correctly map to ogTitle, ogAuthor, etc. instead of the frontend override fields, and included the missing chapterFlags, viewerFlags, and updateStrategy mappings so the database properly adopts the remote sync state.

This is a cherry-pick of my PR to TachiyomiSY. I resolved merge conflicts that come from Komikku specific features and changes.

The synchronization process was previously suffering from runaway loops
where devices would continuously restore hundreds of unchanged manga and
chapters. This commit implements the fixes needed to prevent
these loops:

- In MangaRestorer, ensure that chapter versions are properly updated
  locally when the chapter's read/bookmark contents are identical to
  the remote, preventing the loop where a device fails to update its
  local version and continuously resyncs the chapter.
- Update SyncManager and SyncService to use stable identifiers (URLs
  and source IDs) instead of volatile metadata (titles, authors) for
  composite keys to avoid mistakenly recreating matching items.
- In MangaRestorer, properly copy internal og* fields rather than
  frontend override fields during restoration, and properly restore
  chapterFlags, viewerFlags, and updateStrategy.

@sourcery-ai sourcery-ai Bot 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.

Sorry @Lolle2000la, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a critical synchronization bug where devices would enter an infinite 'ping-pong' loop, triggering massive, redundant restoration processes. By refining how the system identifies mangas and chapters and ensuring that version and sync-state metadata are correctly persisted during restoration, the changes stabilize the synchronization flow and eliminate the runaway operations that previously caused significant performance degradation.

Highlights

  • Chapter Version Alignment: Updated the restoration logic to explicitly adopt remote chapter versions even when content state is identical, preventing unnecessary re-sync loops.
  • Stable Identifier Migration: Replaced volatile display-based composite keys (title, author, chapter name) with immutable backend identifiers (source, URL) to ensure consistent matching.
  • Database Mapping Fixes: Corrected Manga.copyFrom to properly map sync-critical fields like ogTitle and sync flags, ensuring the local database accurately reflects remote states.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.


The sync loop spun in endless night, / With titles shifting out of sight. / Now keys are fixed and versions stay, / To keep the ping-pong ghosts at bay.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request updates the manga restoration process to include additional metadata fields such as ogTitle and chapterFlags, and refines chapter comparison logic to handle version mismatches during synchronization. Additionally, it simplifies the composite keys used for manga and chapter identification by reducing them to source and URL-based identifiers. A review comment noted an unused import for BackupCategory in SyncManager.kt.

Comment thread app/src/main/java/eu/kanade/tachiyomi/data/sync/SyncManager.kt Outdated
Without this, very small unnecessary restorations are still left. It is also expected behavior
This means that when for example `savedSearchesFeeds` is disabled on one device but not the other, then the device where setting is disabled will restore the saved searches from the device where it isn't.

In fact, just syncing once will poising the entire remote store, with it becoming impossible to disable anymore
@cuong-tran

Copy link
Copy Markdown
Collaborator

is this related to your same DB restoration speed PR #1160 or different?

@Lolle2000la

Lolle2000la commented May 19, 2026

Copy link
Copy Markdown
Contributor Author

This is unrelated to #1160. This is about fixing a bug in the synchronization mechanism.

(It does fix the issue that also made me create #1160 though 😅)

@Lolle2000la

Copy link
Copy Markdown
Contributor Author

@cuong-tran The upstream PR to TachiyomiSY I made just got merged, but please consider not cherry picking it and instead merging this, since there were some komikku specific changes necessary.

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