Skip to content

Retry manifest writes on transient database locks - #225

Open
mfafore22 wants to merge 1 commit into
mainfrom
fix/manifest-lock-retry
Open

mfafore22 wants to merge 1 commit into
mainfrom
fix/manifest-lock-retry

Conversation

@mfafore22

Copy link
Copy Markdown
Member

Part of #178 (the lock retry; the deep-crawl checkpoint will be a separate PR).

WAL mode and a 30s busy timeout already absorb most lock contention between site jobs, but a lock can still surface as sqlite3.OperationalError: database is locked, for example at commit time. When that happened, the write simply failed.

Every manifest write now goes through one helper that commits the write and, on a lock error, rolls back and retries after 0.5s, 1s and 2s before giving up. The retry reruns the whole unit of work, not just the statement that failed. That matters for rekey, which deletes the old row and inserts the new one: a retry can't leave it with the old row gone and the new one missing. Any other OperationalError, such as a disk error, is raised straight away.

Lock errors are recognised by SQLite's result code (busy or locked), with a fallback to the message text for Python builds that don't expose the code. Schema setup at startup goes through the same helper, since several site jobs open the file at once.

The wait is a blocking time.sleep, like the existing busy timeout, because the store is synchronous. It adds at most 3.5s on top of the busy timeout, and only when a lock outlasts it.

Tests use a wrapped connection that reports "locked" a set number of times. They cover a write that succeeds on retry, a lock that outlasts every retry, a non-lock error that isn't retried, and a failed rekey that keeps the original row. All crawler tests pass at 96% coverage; ruff and mypy are clean.

@coderabbitai

coderabbitai Bot commented Oct 26, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 68fbbbb6-3a5e-4e3c-a8b1-f2e1d23e07fd
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown

CI Results

Service Check Result
crawler ruff (lint) ✅ Passed
crawler mypy ✅ Passed
crawler pyrefly ✅ Passed
crawler pytest + coverage ✅ Passed
ingest ruff (lint) ✅ Passed
ingest mypy ✅ Passed
ingest pyrefly ✅ Passed
ingest pytest + coverage ✅ Passed
register ruff (lint) ✅ Passed
register mypy ✅ Passed
register pyrefly ✅ Passed
register pytest + coverage ✅ Passed
backend ruff (lint) ✅ Passed
backend mypy ✅ Passed
backend pyrefly ✅ Passed
backend pytest + coverage ✅ Passed
app lint (eslint + prettier) ✅ Passed

All checks passed. 🎉

@mfafore22
mfafore22 requested a review from brylie October 26, 2026 16: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