diff --git a/src/sql/pool.rs b/src/sql/pool.rs index 2a428cd8c..0feb305e0 100644 --- a/src/sql/pool.rs +++ b/src/sql/pool.rs @@ -71,6 +71,24 @@ struct InnerPool { /// This mutex is locked when write connection /// is outside the pool. pub(crate) write_mutex: Arc>, + + /// WAL checkpointing mutex. + /// + /// This mutex ensures that no more than one thread + /// runs WAL checkpointing at the same time. + /// + /// Normal procedures acquire either one read connection + /// or one write connection with a write mutex, + /// and return the resources without trying to acquire + /// more connections or trying to acquire write mutex + /// without returning the read connection first. + /// WAL checkpointing is special, it tries to acquire all + /// connections and the write mutex, + /// so two threads doing this at the same time + /// may result in a deadlock with one thread + /// waiting for a write lock and the other thread + /// waiting for a connection. + wal_checkpoint_mutex: Mutex<()>, } impl InnerPool { @@ -191,6 +209,7 @@ impl Pool { connections: parking_lot::Mutex::new(connections), semaphore, write_mutex: Default::default(), + wal_checkpoint_mutex: Default::default(), }); Pool { inner } } diff --git a/src/sql/pool/wal_checkpoint.rs b/src/sql/pool/wal_checkpoint.rs index ff83d0e54..c32e92656 100644 --- a/src/sql/pool/wal_checkpoint.rs +++ b/src/sql/pool/wal_checkpoint.rs @@ -34,6 +34,7 @@ pub(crate) struct WalCheckpointStats { /// Runs a checkpoint operation in TRUNCATE mode, so the WAL file is truncated to 0 bytes. pub(super) async fn wal_checkpoint(pool: &Pool) -> Result { + let _guard = pool.inner.wal_checkpoint_mutex.lock().await; let t_start = Time::now(); // Do as much work as possible without blocking anybody.