Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions sqlx-sqlite/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@
//!
//! [rusqlite-readme-building]: https://github.com/rusqlite/rusqlite?tab=readme-ov-file#notes-on-building-rusqlite-and-libsqlite3-sys
//!
//! ### Warning about Transactions
//!
//! Using write transactions with SQLx, SQLite, and concurrent writers may lead to serious performance degradation.
//!
//! A write statement within a transaction (or starting a transaction with `BEGIN IMMEDIATE`) takes an EXCLUSIVE lock on the database.
//! 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).
//!
//! ### Optional Features
//!
//! The following features
Expand Down Expand Up @@ -144,6 +154,16 @@ pub trait SqliteExecutor<'c>: Executor<'c, Database = Sqlite> {}
impl<'c, T: Executor<'c, Database = Sqlite>> SqliteExecutor<'c> for T {}

/// An alias for [`Transaction`][sqlx_core::transaction::Transaction], specialized for SQLite.
///
/// ### Warning about Transactions
///
/// Using write transactions with SQLx, SQLite, and concurrent writers may lead to serious performance degradation.
///
/// A write statement within a transaction (or starting a transaction with `BEGIN IMMEDIATE`) takes an EXCLUSIVE lock on the database.
/// 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).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

pub type SqliteTransaction<'c> = sqlx_core::transaction::Transaction<'c, Sqlite>;

// NOTE: required due to the lack of lazy normalization
Expand Down
Loading