fix(sqlite): concurrent migrate retries

This commit is contained in:
2026-10-26 01:40:40 +01:00
parent 6ae3f54a36
commit 0908b7046a
3 changed files with 77 additions and 7 deletions

View File

@@ -1,10 +1,34 @@
use std::time::Duration;
use domain::errors::DomainError;
use sqlx::SqlitePool;
/// Server and worker both migrate at startup, so two processes can race on the
/// same database. The loser's migration fails (e.g. duplicate column) because
/// the winner already applied it; re-running re-reads `_sqlx_migrations` and
/// skips what is done. Only an error that persists across all retries is returned.
const RETRY_BACKOFFS: [Duration; 5] = [
Duration::from_millis(100),
Duration::from_millis(250),
Duration::from_millis(500),
Duration::from_secs(1),
Duration::from_secs(1),
];
pub(crate) async fn run(pool: &SqlitePool) -> Result<(), DomainError> {
sqlx::migrate!("./migrations")
.set_ignore_missing(true)
.run(pool)
.await
.map_err(|e| DomainError::InfrastructureError(e.to_string()))
let mut migrator = sqlx::migrate!("./migrations");
migrator.set_ignore_missing(true);
let mut backoffs = RETRY_BACKOFFS.iter();
loop {
match migrator.run(pool).await {
Ok(()) => return Ok(()),
Err(e) => match backoffs.next() {
Some(delay) => {
tracing::warn!("migration failed, retrying (concurrent migrator?): {e}");
tokio::time::sleep(*delay).await;
}
None => return Err(DomainError::InfrastructureError(e.to_string())),
},
}
}
}