Use immediate rather than deferred transaction in sqlitemigration ensureAppID #98
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.
This fixes an issue that can occur when using multiple
sqlitemigration
pools attempt to connect to the same database file concurrently. When opening asqlitemigration
pool while another pool is writing to the database, the pool may fail to startup with aSQLITE_BUSY
error (shown asdatabase is locked
).In my use case, I had some end-to-end tests running that started an API server and modified the database using a CLI. Both the CLI and the API server started a
sqlitemigration
pool and occasionally the CLI calls would fail due to this issue.From my testing, the culprit is the
ensureAppID
function, which opens a deferred transaction that will later be upgraded to a write transaction. At the point of upgrading to a write transaction, if the database is locked, SQLite will fail immediately withSQLITE_BUSY
[1] [2] and the busy timeout will have no effect, causing the pool to fail to start.If
ensureAppID
is switched use an immediate transaction, that tells SQLite this is a write transaction. SQLite will attempt to lock the database immediately. If a lock cannot be acquired, the busy timeout is used allowing the transaction to wait until it can get a lock, thus allowing the pool to start successfully.I've also included a test case. Please let me know on feedback on the test, it is a bit unwieldy. I'm not sure if it fits with the standards of this project.