Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 27 additions & 8 deletions crates/rustmail-server/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1689,8 +1689,7 @@ async fn serve_exits_while_another_process_holds_the_migration_lock() {
legacy_database(&db_path).await;
let bytes_before = std::fs::read(&db_path).unwrap();
let lock_path = sidecar(&db_path, ".migration-lock");
let lock = std::fs::File::create(&lock_path).unwrap();
lock.try_lock().unwrap();
let lock = hold_migration_lock(std::fs::File::create(&lock_path).unwrap()).await;

let output = tokio::time::timeout(
STARTUP_FAILURE_TIMEOUT,
Expand Down Expand Up @@ -1758,6 +1757,24 @@ async fn legacy_v0_7_0_database(db_path: &std::path::Path) -> Vec<String> {
}

/// Migrates a legacy database in this process, the way `serve` does at startup.
/// Takes the migration lock on `file`, waiting for any earlier holder.
///
/// A lock the test's own migration just dropped can stay held for a moment
/// while a parallel test forks a `rustmail` child, which inherits every open
/// descriptor until it execs, so a single `try_lock` is racy here.
async fn hold_migration_lock(file: std::fs::File) -> std::fs::File {
tokio::time::timeout(
STARTUP_FAILURE_TIMEOUT,
tokio::task::spawn_blocking(move || {
file.lock().unwrap();
file
}),
)
.await
.expect("the migration lock was not released in time")
.unwrap()
}

async fn migrated_database(db_path: &std::path::Path) {
legacy_v0_7_0_database(db_path).await;
let preparation = rustmail_storage::prepare_database_file(db_path, || false)
Expand Down Expand Up @@ -1956,12 +1973,14 @@ async fn restore_backup_refuses_while_another_process_holds_the_migration_lock()
migrated_database(&db_path).await;
let backup_bytes = std::fs::read(sidecar(&db_path, ".schema0.bak")).unwrap();
let lock_path = sidecar(&db_path, ".migration-lock");
let lock = std::fs::OpenOptions::new()
.read(true)
.write(true)
.open(&lock_path)
.unwrap();
lock.try_lock().unwrap();
let lock = hold_migration_lock(
std::fs::OpenOptions::new()
.read(true)
.write(true)
.open(&lock_path)
.unwrap(),
)
.await;

let output = run_restore_backup(&db_path).await;
drop(lock);
Expand Down
Loading