@@ -443,32 +443,35 @@ impl DataStore {
443443 } )
444444 }
445445
446- // Ensures that the database schema matches "desired_version".
447- //
448- // - Updating the schema makes the database incompatible with older
449- // versions of Nexus, which are not running "desired_version".
450- // - This is a one-way operation that cannot be undone.
451- // - The caller is responsible for ensuring that the new version is valid,
452- // and that all running Nexus instances can understand the new schema
453- // version.
454- //
455- // TODO: This function assumes that all concurrently executing Nexus
456- // instances on the rack are operating on the same version of software.
457- // If that assumption is broken, nothing would stop a "new deployment"
458- // from making a change that invalidates the queries used by an "old
459- // deployment".
460- pub async fn ensure_schema (
446+ /// Ensures that the database schema matches `desired_version`.
447+ ///
448+ /// - `validated_action`: A [ValidatedDatastoreSetupAction], indicating that
449+ /// [Self::check_schema_and_access] has already been called.
450+ /// - `all_versions`: A description of all schema versions between
451+ /// "whatever is in the DB" and `desired_version`, instructing
452+ /// how to perform an update.
453+ pub async fn update_schema (
461454 & self ,
462- log : & Logger ,
463- desired_version : Version ,
455+ validated_action : ValidatedDatastoreSetupAction ,
464456 all_versions : Option < & AllSchemaVersions > ,
465457 ) -> Result < ( ) , anyhow:: Error > {
458+ let action = validated_action. action ( ) ;
459+
460+ match action {
461+ DatastoreSetupAction :: Ready => {
462+ bail ! ( "No schema update is necessary" )
463+ }
464+ DatastoreSetupAction :: Update => ( ) ,
465+ _ => bail ! ( "Not ready for schema update" ) ,
466+ }
467+
468+ let desired_version = validated_action. desired_version ( ) . clone ( ) ;
466469 let ( found_version, found_target_version) = self
467470 . database_schema_version ( )
468471 . await
469472 . context ( "Cannot read database schema version" ) ?;
470473
471- let log = log. new ( o ! (
474+ let log = self . log . new ( o ! (
472475 "found_version" => found_version. to_string( ) ,
473476 "desired_version" => desired_version. to_string( ) ,
474477 ) ) ;
@@ -1166,15 +1169,34 @@ mod test {
11661169 // Confirms that calling the internal "ensure_schema" function can succeed
11671170 // when the database is already at that version.
11681171 #[ tokio:: test]
1169- async fn ensure_schema_is_current_version ( ) {
1170- let logctx = dev:: test_setup_log ( "ensure_schema_is_current_version " ) ;
1172+ async fn check_schema_is_current_version ( ) {
1173+ let logctx = dev:: test_setup_log ( "check_schema_is_current_version " ) ;
11711174 let db = TestDatabase :: new_with_raw_datastore ( & logctx. log ) . await ;
11721175 let datastore = db. datastore ( ) ;
11731176
1174- datastore
1175- . ensure_schema ( & logctx. log , SCHEMA_VERSION , None )
1177+ let checked_action = datastore
1178+ . check_schema_and_access (
1179+ IdentityCheckPolicy :: DontCare ,
1180+ SCHEMA_VERSION ,
1181+ )
11761182 . await
1177- . expect ( "Failed to ensure schema" ) ;
1183+ . expect ( "Failed to check schema and access" ) ;
1184+
1185+ assert ! (
1186+ matches!( checked_action. action( ) , DatastoreSetupAction :: Ready ) ,
1187+ "Unexpected action: {:?}" ,
1188+ checked_action. action( ) ,
1189+ ) ;
1190+ assert_eq ! (
1191+ checked_action. desired_version( ) ,
1192+ & SCHEMA_VERSION ,
1193+ "Unexpected desired version: {}" ,
1194+ checked_action. desired_version( )
1195+ ) ;
1196+
1197+ datastore. update_schema ( checked_action, None ) . await . expect_err (
1198+ "Should not be able to update schema that's already up-to-date" ,
1199+ ) ;
11781200
11791201 db. terminate ( ) . await ;
11801202 logctx. cleanup_successful ( ) ;
@@ -1277,8 +1299,13 @@ mod test {
12771299 let log = log. clone ( ) ;
12781300 let pool = pool. clone ( ) ;
12791301 tokio:: task:: spawn ( async move {
1280- let datastore =
1281- DataStore :: new ( & log, pool, Some ( & all_versions) ) . await ?;
1302+ let datastore = DataStore :: new (
1303+ & log,
1304+ pool,
1305+ Some ( & all_versions) ,
1306+ IdentityCheckPolicy :: DontCare ,
1307+ )
1308+ . await ?;
12821309
12831310 // This is the crux of this test: confirm that, as each
12841311 // migration completes, it's not possible to see any artifacts
@@ -1405,9 +1432,23 @@ mod test {
14051432
14061433 // Manually construct the datastore to avoid the backoff timeout.
14071434 // We want to trigger errors, but have no need to wait.
1435+
14081436 let datastore = DataStore :: new_unchecked ( log. clone ( ) , pool. clone ( ) ) ;
1437+ let checked_action = datastore
1438+ . check_schema_and_access (
1439+ IdentityCheckPolicy :: DontCare ,
1440+ SCHEMA_VERSION ,
1441+ )
1442+ . await
1443+ . expect ( "Failed to check schema and access" ) ;
1444+
1445+ // This needs to be in a loop because we constructed a schema change
1446+ // that will intentionally fail sometimes when doing this work.
1447+ //
1448+ // This isn't a normal behavior! But we're trying to test the
1449+ // intermediate steps of a schema change here.
14091450 while let Err ( e) = datastore
1410- . ensure_schema ( & log , SCHEMA_VERSION , Some ( & all_versions) )
1451+ . update_schema ( checked_action . clone ( ) , Some ( & all_versions) )
14111452 . await
14121453 {
14131454 warn ! ( log, "Failed to ensure schema" ; "err" => %e) ;
0 commit comments