refactor: Remove wal_checkpoint_mutex, lock write_mutex before getting sql connection instead

The original idea was to always lock `write_mutex` before acquiring an `InnerPool.semaphore` permit
to avoid ABBA deadlocks, but when refactoring a PR for b696a242fc,
that was forgotten.

This doesn't really change the program flow as we have `Context::housekeeping_mutex` anyway,
just simplifies the code.
This commit is contained in:
iequidoo
2026-03-21 14:45:34 -03:00
committed by iequidoo
parent 98b55ec15f
commit d1f1633c60
2 changed files with 8 additions and 25 deletions

View File

@@ -34,7 +34,6 @@ 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<WalCheckpointStats> {
let _guard = pool.inner.wal_checkpoint_mutex.lock().await;
let t_start = Time::now();
// Do as much work as possible without blocking anybody.
@@ -48,22 +47,25 @@ pub(super) async fn wal_checkpoint(pool: &Pool) -> Result<WalCheckpointStats> {
conn.query_row("PRAGMA wal_checkpoint(PASSIVE)", [], |_| Ok(()))
})?;
// Kick out writers.
const _: () = assert!(Sql::N_DB_CONNECTIONS > 1, "Deadlock possible");
// Kick out writers. `write_mutex` should be locked before taking an `InnerPool.semaphore`
// permit to avoid ABBA deadlocks, so drop `conn` which holds a semaphore permit.
drop(conn);
let _write_lock = Arc::clone(&pool.inner.write_mutex).lock_owned().await;
let t_writers_blocked = Time::now();
let conn = pool.get(query_only).await?;
// Ensure that all readers use the most recent database snapshot (are at the end of WAL) so
// that `wal_checkpoint(FULL)` isn't blocked. We could use `PASSIVE` as well, but it's
// documented poorly, https://www.sqlite.org/pragma.html#pragma_wal_checkpoint and
// https://www.sqlite.org/c3ref/wal_checkpoint_v2.html don't tell how it interacts with new
// readers.
let mut read_conns = Vec::with_capacity(crate::sql::Sql::N_DB_CONNECTIONS - 1);
for _ in 0..(crate::sql::Sql::N_DB_CONNECTIONS - 1) {
let mut read_conns = Vec::with_capacity(Sql::N_DB_CONNECTIONS - 1);
for _ in 0..(Sql::N_DB_CONNECTIONS - 1) {
read_conns.push(pool.get(query_only).await?);
}
read_conns.clear();
// Checkpoint the remaining WAL pages without blocking readers.
let (pages_total, pages_checkpointed) = tokio::task::block_in_place(|| {
conn.query_row("PRAGMA table_list", [], |_| Ok(()))?;
conn.query_row("PRAGMA wal_checkpoint(FULL)", [], |row| {
let pages_total: i64 = row.get(1)?;
let pages_checkpointed: i64 = row.get(2)?;
@@ -71,7 +73,7 @@ pub(super) async fn wal_checkpoint(pool: &Pool) -> Result<WalCheckpointStats> {
})
})?;
// Kick out readers to avoid blocking/SQLITE_BUSY.
for _ in 0..(crate::sql::Sql::N_DB_CONNECTIONS - 1) {
for _ in 0..(Sql::N_DB_CONNECTIONS - 1) {
read_conns.push(pool.get(query_only).await?);
}
let t_readers_blocked = Time::now();