Skip to content

fix(date): release the transaction before acquiring more pool connections - #134

Merged
n-dusan merged 2 commits into
mainfrom
release/0.7.1
Oct 8, 2026
Merged

n-dusan merged 2 commits into
mainfrom
release/0.7.1

Conversation

@n-dusan

@n-dusan n-dusan commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Description (e.g. "Related to ...", etc.)

Closes #133.

The _date handler kept a database connection open for the whole request, then asked the pool for a second one partway through. When enough requests did this at once, each one held a connection while waiting for another, so all of them waited until the 30s timeout and the rest of the server stalled with them.

What was done differently from the issue

The issue suggests keeping the transaction open and passing it down into get_doc_version_dates. This PR does something simpler:

  • The handler now does the database lookups that need the transaction first, then closes it.
  • Only after that does it read the file from git and look up the version dates.

So each request uses one connection at a time and never holds one while waiting for another. That removes the deadlock just like the suggested fix would. It also stops holding a connection during the git read, which the issue said makes the problem worse.

It's also a much smaller change. The suggested fix would mean rewriting four database lookup functions and code that versions() also uses. This one only touches the date handler (8 lines added, 2 removed).

The trade-off is that the version-date lookups no longer run inside the same transaction. That doesn't matter here because the handler only reads data.

Audit

As the issue suggested, I looked for the same pattern elsewhere. The only other transaction is the command-line import in src/history/changes.rs, which never takes a second connection from the pool. serve() and versions() don't open transactions.

Checked

  • cargo fmt --check and cargo clippy --all-targets pass, and all 72 unit tests pass.
  • The integration tests couldn't run locally because of leftover .taf folders on the local drives. CI will run them.
  • No test currently sends concurrent /_date requests.

Code review checklist (for code reviewer to complete)

  • Pull request represents a single change (i.e. not fixing disparate/unrelated things in a single PR)
  • Title summarizes what is changing
  • Commit messages are meaningful (see this for details)
  • Tests have been included and/or updated, as appropriate
  • Docstrings have been included and/or updated, as appropriate
  • Standalone docs have been updated accordingly

Automated tests, benchmarks and linters

You can run the tests, lints and benchmarks that are run on CI locally with:

just ci

…ions

The date handler held its transaction for the whole request, including
the git2 blob read, and meanwhile get_doc_version_dates acquired further
connections from the pool. With 2 * num_cpus concurrent requests each
holding its first connection, all of them waited on a second one until
sqlx's 30s acquire timeout, stalling every other handler on the pool.

Run the handler's three transactional reads (publication, commit, latest
publication) up front, then commit the read-only transaction so no
request ever holds more than one connection at a time and none is held
across the git read.

Fixes #133
@n-dusan
n-dusan merged commit 15ce432 into main Oct 8, 2026
9 checks passed
@n-dusan
n-dusan deleted the release/0.7.1 branch October 8, 2026 08:54
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.

date handler deadlocks the connection pool: holds a transaction while acquiring a second connection (pool is 4)

2 participants