Repository navigation
fix(sqlite3): run BEGIN, COMMIT and ROLLBACK through exec so a failed COMMIT releases its lock - #362
Open
just-some-random-pal wants to merge 1 commit into
Conversation
… COMMIT releases its lock On a local file database, a COMMIT that failed with SQLITE_BUSY left the database write-locked until the process exited: every later write from any connection failed with SQLITE_BUSY, even after the failed transaction was rolled back and the reader that caused the busy error was gone. Through @prisma/adapter-libsql this surfaces as every query timing out. Transaction control went through db.prepare(sql).run(). When a prepared COMMIT throws, libsql leaves the statement un-reset, and that statement keeps the connection's lock past a successful ROLLBACK and past close(), until garbage collection finalizes it. exec() finalizes its statement before returning, failed or not. Route every BEGIN, COMMIT and ROLLBACK, including the pool's rollback on release, through exec(). migrate() now always returns its connection to the pool, even when the rollback after a failed COMMIT itself fails. Fixes tursodatabase#361
This branch has not been deployed
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.
Fixes #361.
On a local file database, a
COMMITthat fails withSQLITE_BUSYleaves the database write-locked until the process exits. Every later write from any connection fails withSQLITE_BUSY, even after the failed transaction is rolled back and the reader that caused the busy error is gone. Through@prisma/adapter-libsqlthis shows up as every later query timing out.The cause is the one the issue pins down in the
libsqlbinding: transaction control went throughdb.prepare(sql).run(), and when a preparedCOMMITthrows, the statement is left un-reset. An un-reset statement keeps the connection's lock past a successfulROLLBACKand pastclose(), until garbage collection finalizes it.db.exec()finalizes its statement before returning, whether it succeeded or failed.This adds
executeControl, which runs a statement throughexec()and maps the error likeexecuteStmt, and uses it for everyBEGIN,COMMITandROLLBACK: inSqlite3Transaction.commit(),rollback()andclose(), inSqlite3Client.transaction(),batch()andmigrate(), and in the pool's rollback when a connection is released mid-transaction. 0.18.0's pool already rolls back and drops a connection whose rollback fails, so with the lock no longer stuck, that recovery now works.migrate()also returns its connection to the pool in every case; before, a throwingROLLBACKin itsfinallyskipped the release.The
libsql0.6 incompatibility mentioned in the issue is separate and not touched here.Tests: three cases next to the existing busy-timeout test, one each for a failed
COMMITintransaction().commit(),batch()andmigrate(). Each holds aSHAREDlock from a read transaction on a second client, lets theCOMMITfail withSQLITE_BUSY, releases the reader, and then writes from the same client and from a new one. All three fail onmainwithSQLITE_BUSY: database is lockedand pass with this change. The local test suite passes (200 passed, 11 network tests skipped without a server), andtsc --noEmitandprettier --checkare clean.