Skip to content

Make library sync all-or-nothing so an interrupted sync keeps the previous cache - #32

Open
claude-on-call wants to merge 1 commit into
ecwilsonaz:mainfrom
claude-on-call:fix/atomic-library-sync
Open

claude-on-call wants to merge 1 commit into
ecwilsonaz:mainfrom
claude-on-call:fix/atomic-library-sync

Conversation

@claude-on-call

Copy link
Copy Markdown

Problem

sync_library deletes every cached track and commits, then inserts and commits in batches of 500. If a sync is interrupted partway (the container is restarted or updated mid-sync, the process is OOM-killed, or Plex drops the connection), the cache is left with a partial track table and track_count = 0. Nothing resumes the sync after a restart, so the UI reports the library as not synced until someone re-syncs by hand.

We hit this on a ~25k-track library: a container update during a sync left 10,500 tracks and a count of 0.

Change

The delete, the inserts and the sync_state update now run in one transaction, committed once at the end.

  • On an exception the transaction is rolled back.
  • On a hard kill, SQLite discards the uncommitted transaction.

Either way the previous complete cache is kept. The database is already in WAL mode, so while a sync runs, readers (filters, counts, generation) keep seeing the previous snapshot instead of a half-filled table.

Behaviour change

test_failed_sync_resets_cache_state deliberately asserted the opposite: a failed sync empties the cache "to avoid using stale data". This PR keeps the last complete sync instead. A day-old snapshot of a music library is almost always more useful than an empty or partial one, and saving a playlist already skips tracks that no longer exist in Plex.

The test is renamed to test_failed_sync_keeps_previous_cache with its asserts inverted. Happy to rework this if you'd rather keep the old behaviour.

Testing

  • New test_sync_failing_midway_keeps_previous_cache: several batches are written, then the sync fails; the previous tracks and count are intact.
  • Checked outside the suite: a sync process hard-killed (os._exit) after 1,000 of 4,000 tracks leaves the previous cache intact, and the next sync succeeds.
  • pytest: all tests pass. Running on a live instance.

🤖 Generated with Claude Code

sync_library deleted the cache and committed, then committed each batch, so
a sync interrupted partway (container restart, OOM kill, Plex dropping out)
left a partial track table with track_count 0, and nothing resumed it. The
delete, inserts and sync_state update now run in one transaction: a failure
rolls back, a hard kill is discarded by SQLite, and the previous complete
cache is kept. WAL mode keeps readers on that snapshot until the commit.

A failed sync now keeps the previous cache instead of emptying it, so
test_failed_sync_resets_cache_state is renamed and inverted, and a test for
a failure after several batches is added.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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