fix: WAL mode + busy_timeout for SQLite, fix rate limiter TOCTOU race

This commit is contained in:
2026-05-04 22:10:19 +02:00
parent d083f8ae3d
commit 3135a15cb3
2 changed files with 10 additions and 7 deletions

View File

@@ -48,7 +48,9 @@ async fn wire_dependencies() -> anyhow::Result<AppState> {
let database_url = std::env::var("DATABASE_URL").context("DATABASE_URL must be set")?; let database_url = std::env::var("DATABASE_URL").context("DATABASE_URL must be set")?;
let opts = SqliteConnectOptions::from_str(&database_url) let opts = SqliteConnectOptions::from_str(&database_url)
.context("Invalid DATABASE_URL")? .context("Invalid DATABASE_URL")?
.create_if_missing(true); .create_if_missing(true)
.journal_mode(sqlx::sqlite::SqliteJournalMode::Wal)
.busy_timeout(std::time::Duration::from_secs(5));
let pool = SqlitePool::connect_with(opts) let pool = SqlitePool::connect_with(opts)
.await .await
.context("Failed to connect to SQLite database")?; .context("Failed to connect to SQLite database")?;

View File

@@ -35,14 +35,15 @@ impl RateLimiter {
.unwrap_or_default() .unwrap_or_default()
.as_secs() .as_secs()
/ 60; / 60;
let prev = self.window.load(Ordering::Relaxed); let prev = self.window.load(Ordering::Acquire);
if now != prev { if now != prev {
self.window.store(now, Ordering::Relaxed); // compare_exchange ensures only one thread wins the window reset
self.count.store(1, Ordering::Relaxed); if self.window.compare_exchange(prev, now, Ordering::AcqRel, Ordering::Relaxed).is_ok() {
true self.count.store(1, Ordering::Release);
} else { return true;
self.count.fetch_add(1, Ordering::Relaxed) + 1 <= self.limit }
} }
self.count.fetch_add(1, Ordering::Relaxed) + 1 <= self.limit
} }
} }