@@ -422,32 +422,35 @@ impl DataStore {
422
422
} )
423
423
}
424
424
425
- // Ensures that the database schema matches "desired_version".
426
- //
427
- // - Updating the schema makes the database incompatible with older
428
- // versions of Nexus, which are not running "desired_version".
429
- // - This is a one-way operation that cannot be undone.
430
- // - The caller is responsible for ensuring that the new version is valid,
431
- // and that all running Nexus instances can understand the new schema
432
- // version.
433
- //
434
- // TODO: This function assumes that all concurrently executing Nexus
435
- // instances on the rack are operating on the same version of software.
436
- // If that assumption is broken, nothing would stop a "new deployment"
437
- // from making a change that invalidates the queries used by an "old
438
- // deployment".
439
- pub async fn ensure_schema (
425
+ /// Ensures that the database schema matches `desired_version`.
426
+ ///
427
+ /// - `validated_action`: A [ValidatedDatastoreSetupAction], indicating that
428
+ /// [Self::check_schema_and_access] has already been called.
429
+ /// - `all_versions`: A description of all schema versions between
430
+ /// "whatever is in the DB" and `desired_version`, instructing
431
+ /// how to perform an update.
432
+ pub async fn update_schema (
440
433
& self ,
441
- log : & Logger ,
442
- desired_version : Version ,
434
+ validated_action : ValidatedDatastoreSetupAction ,
443
435
all_versions : Option < & AllSchemaVersions > ,
444
436
) -> Result < ( ) , anyhow:: Error > {
437
+ let action = validated_action. action ( ) ;
438
+
439
+ match action {
440
+ DatastoreSetupAction :: Ready => {
441
+ bail ! ( "No schema update is necessary" )
442
+ }
443
+ DatastoreSetupAction :: Update => ( ) ,
444
+ _ => bail ! ( "Not ready for schema update" ) ,
445
+ }
446
+
447
+ let desired_version = validated_action. desired_version ( ) . clone ( ) ;
445
448
let ( found_version, found_target_version) = self
446
449
. database_schema_version ( )
447
450
. await
448
451
. context ( "Cannot read database schema version" ) ?;
449
452
450
- let log = log. new ( o ! (
453
+ let log = self . log . new ( o ! (
451
454
"found_version" => found_version. to_string( ) ,
452
455
"desired_version" => desired_version. to_string( ) ,
453
456
) ) ;
@@ -1145,15 +1148,34 @@ mod test {
1145
1148
// Confirms that calling the internal "ensure_schema" function can succeed
1146
1149
// when the database is already at that version.
1147
1150
#[ tokio:: test]
1148
- async fn ensure_schema_is_current_version ( ) {
1149
- let logctx = dev:: test_setup_log ( "ensure_schema_is_current_version " ) ;
1151
+ async fn check_schema_is_current_version ( ) {
1152
+ let logctx = dev:: test_setup_log ( "check_schema_is_current_version " ) ;
1150
1153
let db = TestDatabase :: new_with_raw_datastore ( & logctx. log ) . await ;
1151
1154
let datastore = db. datastore ( ) ;
1152
1155
1153
- datastore
1154
- . ensure_schema ( & logctx. log , SCHEMA_VERSION , None )
1156
+ let checked_action = datastore
1157
+ . check_schema_and_access (
1158
+ IdentityCheckPolicy :: DontCare ,
1159
+ SCHEMA_VERSION ,
1160
+ )
1155
1161
. await
1156
- . expect ( "Failed to ensure schema" ) ;
1162
+ . expect ( "Failed to check schema and access" ) ;
1163
+
1164
+ assert ! (
1165
+ matches!( checked_action. action( ) , DatastoreSetupAction :: Ready ) ,
1166
+ "Unexpected action: {:?}" ,
1167
+ checked_action. action( ) ,
1168
+ ) ;
1169
+ assert_eq ! (
1170
+ checked_action. desired_version( ) ,
1171
+ & SCHEMA_VERSION ,
1172
+ "Unexpected desired version: {}" ,
1173
+ checked_action. desired_version( )
1174
+ ) ;
1175
+
1176
+ datastore. update_schema ( checked_action, None ) . await . expect_err (
1177
+ "Should not be able to update schema that's already up-to-date" ,
1178
+ ) ;
1157
1179
1158
1180
db. terminate ( ) . await ;
1159
1181
logctx. cleanup_successful ( ) ;
@@ -1256,8 +1278,13 @@ mod test {
1256
1278
let log = log. clone ( ) ;
1257
1279
let pool = pool. clone ( ) ;
1258
1280
tokio:: task:: spawn ( async move {
1259
- let datastore =
1260
- DataStore :: new ( & log, pool, Some ( & all_versions) ) . await ?;
1281
+ let datastore = DataStore :: new (
1282
+ & log,
1283
+ pool,
1284
+ Some ( & all_versions) ,
1285
+ IdentityCheckPolicy :: DontCare ,
1286
+ )
1287
+ . await ?;
1261
1288
1262
1289
// This is the crux of this test: confirm that, as each
1263
1290
// migration completes, it's not possible to see any artifacts
@@ -1384,9 +1411,23 @@ mod test {
1384
1411
1385
1412
// Manually construct the datastore to avoid the backoff timeout.
1386
1413
// We want to trigger errors, but have no need to wait.
1414
+
1387
1415
let datastore = DataStore :: new_unchecked ( log. clone ( ) , pool. clone ( ) ) ;
1416
+ let checked_action = datastore
1417
+ . check_schema_and_access (
1418
+ IdentityCheckPolicy :: DontCare ,
1419
+ SCHEMA_VERSION ,
1420
+ )
1421
+ . await
1422
+ . expect ( "Failed to check schema and access" ) ;
1423
+
1424
+ // This needs to be in a loop because we constructed a schema change
1425
+ // that will intentionally fail sometimes when doing this work.
1426
+ //
1427
+ // This isn't a normal behavior! But we're trying to test the
1428
+ // intermediate steps of a schema change here.
1388
1429
while let Err ( e) = datastore
1389
- . ensure_schema ( & log , SCHEMA_VERSION , Some ( & all_versions) )
1430
+ . update_schema ( checked_action . clone ( ) , Some ( & all_versions) )
1390
1431
. await
1391
1432
{
1392
1433
warn ! ( log, "Failed to ensure schema" ; "err" => %e) ;
0 commit comments