sqlx-sqlite: warning about write transaction deadlocks#4166
sqlx-sqlite: warning about write transaction deadlocks#4166emschwartz wants to merge 1 commit intolaunchbadge:mainfrom
Conversation
| /// Calling `.await` causes the task to yield so the async runtime can run other tasks. | ||
| /// However, the runtime does not know that the given task is holding the lock and must be run before other write transactions or statements can be executed. | ||
| /// Other tasks may be scheduled to run before the waiting task, but other writers cannot make progress until the lock is released. | ||
| /// This deadlock will only be resolved when the waiting tasks reach their [`busy_timeout`](SqliteConnectOptions::busy_timeout). |
There was a problem hiding this comment.
I am confused what problem this is describing. Is a literal deadlock, in that the new tasks block synchronously on the write lock, thus preventing the waiting task from continuing at all? Or is it the case there have somehow been five CPU-seconds worth of work scheduled on the same core in between obtaining the write lock and sqlx calling the waiting task’s waker?
In theory what should be happening is that new tasks will do useful work, see the write lock is taken, and then yield. Then once the write lock is released, the first task to yield will be woken. But that’s just how a contended mutex normally operates, so what about this is special to transactions?
|
For context, this is actively being discussed on Reddit: https://www.reddit.com/r/rust/comments/1r7eh9v/psa_write_transactions_are_a_footgun_with_sqlx/o5y8kr3/ |
|
Following that Reddit discussion, I benchmarked different approaches and found that the real culprit was having any amount of lock contention at the SQLite level. A better approach seems to be splitting a single writer connection out from the read pool. I'll close this PR. |
This adds a warning that using write transactions with SQLx + SQLite can cause serious performance degradation. Calling
.awaiton a statement that includes a write or starting a transaction withBEGIN IMMEDIATEtakes an EXCLUSIVE lock on the SQLite database. However, the runtime does not know this and may schedule another task before. If another task tries to write, it will cause a deadlock.