Revert "Improve DB contention"

This reverts commit 0e1849061b.
This commit is contained in:
Andras Schmelczer 2026-01-18 19:45:50 +00:00
parent 722e7af3e2
commit 750cf8d4ee
2 changed files with 16 additions and 15 deletions

View file

@ -10,7 +10,7 @@ use sqlx::{ConnectOptions, sqlite::SqliteConnectOptions, types::chrono::Utc};
pub mod models;
use sqlx::{Pool, Sqlite, sqlite::SqlitePoolOptions};
use tokio::sync::RwLock;
use tokio::sync::Mutex;
use tokio::time::Instant;
use uuid::fmt::Hyphenated;
@ -39,7 +39,7 @@ impl std::fmt::Debug for PoolWithTimestamp {
pub struct Database {
config: DatabaseConfig,
broadcasts: Broadcasts,
connection_pools: Arc<RwLock<HashMap<VaultId, PoolWithTimestamp>>>,
connection_pools: Arc<Mutex<HashMap<VaultId, PoolWithTimestamp>>>,
}
pub type Transaction<'a> = sqlx::Transaction<'a, Sqlite>;
@ -83,7 +83,7 @@ impl Database {
let database = Self {
config: config.clone(),
connection_pools: Arc::new(RwLock::new(connection_pools)),
connection_pools: Arc::new(Mutex::new(connection_pools)),
broadcasts: broadcasts.clone(),
};
@ -130,12 +130,11 @@ impl Database {
}
async fn get_connection_pool(&self, vault: &VaultId) -> Result<Pool<Sqlite>> {
// Fast path: check if pool exists with a read lock (no blocking other readers)
// First, check if the pool exists without holding the lock during creation
{
let pools = self.connection_pools.read().await;
if let Some(pool_with_timestamp) = pools.get(vault) {
// Skip updating last_accessed here - it's only used for idle cleanup
// and will be updated when the pool is created or reused after recreation
let mut pools = self.connection_pools.lock().await;
if let Some(pool_with_timestamp) = pools.get_mut(vault) {
pool_with_timestamp.last_accessed = Instant::now();
return Ok(pool_with_timestamp.pool.clone());
}
}
@ -145,8 +144,8 @@ impl Database {
// under high concurrency, but only one will be kept
let new_pool = Self::create_vault_database(&self.config, vault).await?;
// Re-acquire lock (write) and insert (or use existing if another task created it)
let mut pools = self.connection_pools.write().await;
// Re-acquire lock and insert (or use existing if another task created it)
let mut pools = self.connection_pools.lock().await;
let pool_with_timestamp = pools
.entry(vault.clone())
.or_insert_with(|| PoolWithTimestamp {
@ -481,19 +480,22 @@ impl Database {
Ok(())
}
/// Cleanup idle connection pools that haven't been accessed in more than 5 minutes
async fn cleanup_idle_pools(&self) {
use crate::consts::IDLE_POOL_TIMEOUT;
let mut pools = self.connection_pools.write().await;
let mut pools = self.connection_pools.lock().await;
let now = Instant::now();
let idle_timeout = Duration::from_secs(5 * 60); // 5 minutes
// Collect vaults to remove
let vaults_to_remove: Vec<VaultId> = pools
.iter()
.filter(|(_, pool_with_timestamp)| {
now.duration_since(pool_with_timestamp.last_accessed) > IDLE_POOL_TIMEOUT
now.duration_since(pool_with_timestamp.last_accessed) > idle_timeout
})
.map(|(vault_id, _)| vault_id.clone())
.collect();
// Close and remove idle pools
for vault_id in &vaults_to_remove {
if let Some(pool_with_timestamp) = pools.remove(vault_id) {
info!("Closing idle database connection pool for vault `{vault_id}`");

View file

@ -7,7 +7,6 @@ pub const DEFAULT_CONFIG_PATH: &str = "config.yml";
pub const DEFAULT_DATABASES_DIRECTORY_PATH: &str = "databases";
pub const DEFAULT_MAX_CONNECTIONS_PER_VAULT: u32 = 12;
pub const DEFAULT_CURSOR_TIMEOUT: Duration = Duration::from_secs(60);
pub const IDLE_POOL_TIMEOUT: Duration = Duration::from_secs(5 * 60);
pub const DEFAULT_HOST: &str = "127.0.0.1";
pub const DEFAULT_PORT: u16 = 3000;