The four 500-return paths in update_face_handler returned e.to_string()
in the body but never logged. When a face PATCH failed with a 16-byte
body and no log entry, the cause (SQLITE_BUSY from cross-DAO writer
contention exhausting the 5s busy_timeout) was invisible. Surface the
full anyhow chain via {:#} on each path so the diesel cause is in the
log even when the response body only shows the top-level context.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The drain queried `date_taken IS NULL OR date_taken_source = 'fs_time'`
ORDER BY id ASC LIMIT 500 every watcher tick. The resolver is
deterministic on file bytes + filename + fs metadata, so any row that
landed on fs_time once landed there again on every retry — the drain
spun on the same lowest-id rows in perpetuity, never advancing to
rows 501+ while still logging more_remain=true.
Side effect: 500 auto-commit UPDATEs per tick sustained the SQLite
write lock long enough that other writers on separate DAO connections
hit the 5s busy_timeout. Manifested as intermittent 500s on
PATCH /image/faces/{id} that succeeded on retry.
Narrow the partial index and query predicate to `date_taken IS NULL`.
If exiftool installs or a new filename regex lands, an operator can
re-resolve fs_time rows out-of-band rather than re-introducing the
steady-state churn.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cameron
merged commit 74bf693878 into master2026-05-12 18:42:22 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The four 500-return paths in update_face_handler returned e.to_string() in the body but never logged. When a face PATCH failed with a 16-byte body and no log entry, the cause (SQLITE_BUSY from cross-DAO writer contention exhausting the 5s busy_timeout) was invisible. Surface the full anyhow chain via {:#} on each path so the diesel cause is in the log even when the response body only shows the top-level context. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>The drain queried `date_taken IS NULL OR date_taken_source = 'fs_time'` ORDER BY id ASC LIMIT 500 every watcher tick. The resolver is deterministic on file bytes + filename + fs metadata, so any row that landed on fs_time once landed there again on every retry — the drain spun on the same lowest-id rows in perpetuity, never advancing to rows 501+ while still logging more_remain=true. Side effect: 500 auto-commit UPDATEs per tick sustained the SQLite write lock long enough that other writers on separate DAO connections hit the 5s busy_timeout. Manifested as intermittent 500s on PATCH /image/faces/{id} that succeeded on retry. Narrow the partial index and query predicate to `date_taken IS NULL`. If exiftool installs or a new filename regex lands, an operator can re-resolve fs_time rows out-of-band rather than re-introducing the steady-state churn. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>