fix(sqlite): stop SqliteAiVectorStorage.putBulk deadlocking on its own chain slot - #846
Merged
sroussey merged 1 commit intoAug 20, 2026
Conversation
…n chain slot `guardedWrite` (added with withConnectionTransaction) routes every public write through `runOnConnection`, but `SqliteAiVectorStorage._putBulkInternal` had taken the shared connection-chain slot itself since before anything above it did. `putBulk` therefore took the slot twice on one handle: the inner `runOnConnection` awaits the outer's promise, which only resolves once the inner returns. The `finally` never runs, so `state.chain` is left unresolved and the handle is poisoned for every later write on it — which is why the suite's `afterEach` `deleteAll()` timed out alongside the three `putBulk` tests. The parent `SqliteTabularStorage._putBulkInternal` does not re-take the chain; this restores that symmetry. Both entry paths already hold the slot: the public `putBulk` via `guardedWrite`, and a call arriving through the `tx` proxy runs inside the transaction that holds it — which is why the removed branch was reachable only from the deadlocking path. `runVectorPutBulkOnHandle` still reads `db.inTransaction` to decide whether to open its own `db.transaction`, so SQLite transaction nesting is unchanged. Co-Authored-By: Claude <noreply@anthropic.com>
Coverage Report
File CoverageNo changed files found. |
sroussey
merged commit Aug 20, 2026
159af0b
into
claude/multi-repo-transactions-708
11 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
SqliteAiVectorStorage.putBulktakes the shared connection-chain slot twice, and the inner take awaits a promise that only resolves after the inner call returns. Four vector tests hang for their full 60 s timeout, and the poisoned handle then hangsafterEach'sdeleteAll()on a 15 s hook timeout.This removes the redundant inner take, restoring symmetry with
SqliteTabularStorage._putBulkInternal.Root cause
Two things independently take the chain slot for one logical write:
putBulktakes it inguardedWrite(added with the connection-mutex work), and_putBulkInternaltook it again viarunOnConnection(present since3bfca03fd, when it was the only take and therefore correct).The inner
runOnConnectionawaits the outer holder's promise. That promise resolves only when the outer call returns — which cannot happen until the inner one does. A call arriving through thetxproxy is in the same position: it runs inside a transaction that already holds the slot.Whoever reaches
_putBulkInternalalready holds the slot, so it must not re-take it.1 file changed, 4 insertions(+), 12 deletions(-).Provenance
This is a pre-existing regression on this branch, not a defect in #845. It was introduced by
1eef8dbb8— the first commit of this branch — and stayed hidden because the branch'sbuild-typesjob was broken, so every test job was skipped.Bisect over
bun scripts/test.ts vitest integration storage:2a9aeba80(parent)1eef8dbb8ff4b971964b22a85454b22a8545+ this fixVerification
Red/green on this exact tree, same command, fix stashed and restored:
Without the fix
With the fix
46.29 s.
Note the fourth failing test —
does not deadlock external putBulk queued behind a running withTransaction— is the branch's own regression test for this property, failing on the branch that added it.Generated by Claude Code