fix(sqlite): concurrent migrate retries
This commit is contained in:
@@ -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())),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user