Skip to content

fix(offline): keep download finalize cheap and stop on database failures - #1333

Merged
everpcpc merged 2 commits into
mainfrom
fix/offline-download-queue-db-failures
Nov 10, 2026
Merged

everpcpc merged 2 commits into
mainfrom
fix/offline-download-queue-db-failures

Conversation

@everpcpc

Copy link
Copy Markdown
Member

Why

Follow-up to the offline report in #1330. Besides the deletion fixed in #1331, the reporter sees each download hang for ~30s after the file itself finishes, getting faster as the queue shrinks, and a persistent SQLite error 14: unable to open database file whose cause is still unknown.

Download finalize no longer scales with the queue

  • Queue bookkeeping (Live Activity counts, per-file background callbacks, queue advance) loaded every pending book as a full row, several times per finished book, only to read a count or the first element. It now uses count queries and a single-row fetch.
  • A series or read list with an offline policy reloaded every member book whenever one book changed status. Offline policies only queue books that are not downloaded, so a book settling as downloaded now updates the rollups by delta.
  • Policy handling did a linear lookup per member book; it now uses an index.
  • Launch-time orphan cleanup reads only the book id and status columns.

Behavior note: policy-owned series and read lists used to get a full recount after every finished book. They now get one only when a failed state is involved or the delta goes out of bounds.

Database failures are no longer read as state

  • Download status and queue reads throw instead of defaulting to "not downloaded" or "empty queue". OfflineManager stops at an unreadable status.
  • Cancel and delete remove files only after the status write landed, so a book the database still lists keeps its files.
  • A download whose final status write failed no longer advances the queue. Previously the queue picked the same book again (files already present, finalize immediately), looping for as long as writes kept failing.
  • In-transaction rollups throw, so a failed member read rolls the transaction back instead of saving zeroed counts.
  • The Live Activity is not updated, and never reports "finished", when the queue cannot be read.

These failures are logged only; no new user-facing alerts.

Diagnosing SQLite error 14

SQLite's own error log is now routed into the Database log category. Unlike the thrown DatabaseError, it carries the failing system call, path, and errno, which should tell disk-full apart from descriptor exhaustion or a permission problem. Entries are capped per launch because persisting one writes to the log database.

Validation

make build-ios, make build-macos, and make build-tvos pass. Not exercised on a device, with a large library, or against a failing database.

Includes build 685.

🤖 Generated with Claude Code

Finishing a download loaded the whole pending queue several times and, for series and read lists with an offline policy, reloaded every member book. Queue bookkeeping now reads counts or the single next book, and a book settling as downloaded updates rollups by delta.

Download status and queue reads now throw instead of defaulting to not-downloaded or empty. OfflineManager stops on an unreadable status, removes files only after the status write landed, and does not advance the queue when the final status write failed, which previously re-picked the same book in a loop. In-transaction rollups throw so a failed member read rolls back instead of saving zeroed counts.

Route SQLite's own error log into the Database log category so open failures carry the failing path and errno.
@everpcpc
everpcpc marked this pull request as ready for review November 10, 2026 00:53
@everpcpc
everpcpc merged commit 4e308dc into main Nov 10, 2026
3 checks passed
@everpcpc
everpcpc deleted the fix/offline-download-queue-db-failures branch November 10, 2026 00:53
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