Skip to content

fix(catalog-reader): read Catalogs without SQLite locks - #59

Merged
kiri11 merged 2 commits into
mainfrom
fix-43-lock-free-catalog-reads
Oct 15, 2026
Merged

kiri11 merged 2 commits into
mainfrom
fix-43-lock-free-catalog-reads

Conversation

@kiri11

@kiri11 kiri11 commented Oct 15, 2026

Copy link
Copy Markdown
Owner

Why

Capture One drops a save, and every later change to the document, when another process holds a SQLite read lock as the save commits. The catalog suite polled stored reads during exactly that window, which is what lost its albums and clones (#43). Photographers lose work in the same way: production edits and catalog reads also went through the locking reader.

What changed

  • CatalogReader opens a fresh immutable=1 connection per attempt and keeps a result only when no -journal exists and the change counter, size and mtime match before and after. Otherwise it retries until the 10-second deadline.
  • Tests/stored_read.py does the same for Python. Live suites and probe scripts use it, and stored_read_test.py rejects any other read-only SQLite connection.
  • Docs name the cause and warn against other SQLite tools on an open Catalog. The refuted uptime advice and evidence are gone.

Scope

  • Covers every c1 and repository reader of an open Catalog or Session database.
  • Does not change Capture One's behavior. Other programs that lock the database can still cause a stall, which the Detect when Capture One stops saving a Catalog #45 detector reports for c1 writes.

Tradeoffs

  • Reading a snapshot copy was rejected, because the copy itself needs the same consistency check and costs a full file copy per read.
  • A save that keeps the journal open blocks a read for up to 10 s, then the read fails. The alternative is a torn read.

Blast Radius

The change touches every stored read: catalog commands, the read workflow, and the All Images lookup in Catalog edits. WAL databases now fail closed. Capture One Catalogs and Sessions use rollback-journal mode.

Verification

  • Live probe on 16.8.5.30, disposable Catalogs. A locking read held across the save tick lost the save in 8 of 8 trials, and the same hold through immutable=1 lost it in 0 of 3. Polling reads with the old c1 lost it in 1 of 6 trials, and with this c1 in 0 of 12.
  • make qualify QUALIFY_SUITES=catalog QUALIFY_DOCUMENT_KINDS=catalog passed 5 consecutive package-layout runs and 2 unpackaged runs.
  • make check passes. The new tests fail with the stamp check removed, with the journal check removed, and with a locking reader. The snapshot reservation was later moved out of the retry loop, and that change is verified offline only.

🤖 Generated with Claude Code

kiri11 and others added 2 commits October 15, 2026 13:04
A read transaction held while Capture One saves makes Capture One drop
that save and every later change to the document, even at close. On
16.8.5.30 a read held across the save tick stalled it in 8 of 8 trials,
and polling `catalog collections` twice a second stalled it in 1 of 6.
The same 70-second hold through immutable=1 stalled it in 0 of 3.

Each observation now opens a fresh immutable connection and keeps its
result only when no rollback journal exists and the change counter,
size and mtime match before and after; otherwise it retries until the
10-second deadline. WAL Catalogs fail closed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The catalog suite's lost albums and clones came from its own polling
reads, not from Capture One's uptime. Tests/stored_read.py gives Python
readers the same lock-free, retried read as c1; the live suites and
probe scripts use it, the failure snapshot goes through
`c1 catalog snapshot`, and stored_read_test rejects any other read-only
SQLite connection. Drop the refuted uptime evidence and advice.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kiri11
kiri11 merged commit 9009ac4 into main Oct 15, 2026
1 check passed
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