Avoid no-op recency migration repair writes

This commit is contained in:
Zanie Blue
2026-07-08 15:44:39 -05:00
parent 8d83687aaa
commit 024294b951
2 changed files with 67 additions and 0 deletions

View File

@@ -62,6 +62,27 @@ pub(crate) async fn repair_legacy_recency_migration_version(
return Ok(());
}
let legacy_recency_needs_repair = sqlx::query_scalar::<_, i64>(
r#"
SELECT 1
FROM _sqlx_migrations
WHERE version = ?
AND checksum = ?
AND NOT EXISTS (
SELECT 1 FROM _sqlx_migrations WHERE version = ?
)
"#,
)
.bind(38_i64)
.bind(recency_migration.checksum.as_ref())
.bind(recency_migration.version)
.fetch_optional(pool)
.await?
.is_some();
if !legacy_recency_needs_repair {
return Ok(());
}
sqlx::query(
r#"
UPDATE _sqlx_migrations

View File

@@ -1,9 +1,15 @@
use std::borrow::Cow;
use std::time::Duration;
use sqlx::Connection;
use sqlx::Row;
use sqlx::SqliteConnection;
use sqlx::migrate::Migration;
use sqlx::migrate::Migrator;
use sqlx::sqlite::SqliteConnectOptions;
use sqlx::sqlite::SqliteJournalMode;
use sqlx::sqlite::SqlitePoolOptions;
use uuid::Uuid;
use super::STATE_MIGRATOR;
use super::repair_legacy_recency_migration_version;
@@ -196,3 +202,43 @@ async fn repairs_recency_migration_that_was_applied_as_version_38() {
.collect::<Vec<_>>();
assert_eq!(applied, expected);
}
#[tokio::test]
async fn repair_recency_migration_succeeds_while_another_connection_holds_writer_slot() {
let database_path = std::env::temp_dir().join(format!(
"codex-state-migrations-test-{}.sqlite",
Uuid::new_v4()
));
let options = SqliteConnectOptions::new()
.filename(&database_path)
.create_if_missing(true)
.journal_mode(SqliteJournalMode::Wal)
.busy_timeout(Duration::from_millis(100));
let pool = SqlitePoolOptions::new()
.max_connections(1)
.connect_with(options.clone())
.await
.expect("database should open");
STATE_MIGRATOR
.run(&pool)
.await
.expect("current migrations should apply");
let mut write_connection = SqliteConnection::connect_with(&options)
.await
.expect("write connection should open");
let write_transaction = write_connection
.begin_with("BEGIN IMMEDIATE")
.await
.expect("write transaction should acquire the writer slot");
let repair_result = repair_legacy_recency_migration_version(&pool, &STATE_MIGRATOR).await;
write_transaction
.rollback()
.await
.expect("write transaction should roll back");
drop(write_connection);
pool.close().await;
let _ = tokio::fs::remove_file(database_path).await;
repair_result.expect("current migration history should not need the writer slot");
}