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 opts = SqliteConnectOptions::from_str(&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)
.await
.context("Failed to connect to SQLite database")?;

View File

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