35 lines
1.2 KiB
Rust
35 lines
1.2 KiB
Rust
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> {
|
|
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())),
|
|
},
|
|
}
|
|
}
|
|
}
|