@@ -443,32 +443,35 @@ impl DataStore {
443
443
} )
444
444
}
445
445
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 (
461
454
& self ,
462
- log : & Logger ,
463
- desired_version : Version ,
455
+ validated_action : ValidatedDatastoreSetupAction ,
464
456
all_versions : Option < & AllSchemaVersions > ,
465
457
) -> 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 ( ) ;
466
469
let ( found_version, found_target_version) = self
467
470
. database_schema_version ( )
468
471
. await
469
472
. context ( "Cannot read database schema version" ) ?;
470
473
471
- let log = log. new ( o ! (
474
+ let log = self . log . new ( o ! (
472
475
"found_version" => found_version. to_string( ) ,
473
476
"desired_version" => desired_version. to_string( ) ,
474
477
) ) ;
@@ -1166,15 +1169,34 @@ mod test {
1166
1169
// Confirms that calling the internal "ensure_schema" function can succeed
1167
1170
// when the database is already at that version.
1168
1171
#[ 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 " ) ;
1171
1174
let db = TestDatabase :: new_with_raw_datastore ( & logctx. log ) . await ;
1172
1175
let datastore = db. datastore ( ) ;
1173
1176
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
+ )
1176
1182
. 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
+ ) ;
1178
1200
1179
1201
db. terminate ( ) . await ;
1180
1202
logctx. cleanup_successful ( ) ;
@@ -1277,8 +1299,13 @@ mod test {
1277
1299
let log = log. clone ( ) ;
1278
1300
let pool = pool. clone ( ) ;
1279
1301
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 ?;
1282
1309
1283
1310
// This is the crux of this test: confirm that, as each
1284
1311
// migration completes, it's not possible to see any artifacts
@@ -1405,9 +1432,23 @@ mod test {
1405
1432
1406
1433
// Manually construct the datastore to avoid the backoff timeout.
1407
1434
// We want to trigger errors, but have no need to wait.
1435
+
1408
1436
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.
1409
1450
while let Err ( e) = datastore
1410
- . ensure_schema ( & log , SCHEMA_VERSION , Some ( & all_versions) )
1451
+ . update_schema ( checked_action . clone ( ) , Some ( & all_versions) )
1411
1452
. await
1412
1453
{
1413
1454
warn ! ( log, "Failed to ensure schema" ; "err" => %e) ;
0 commit comments