Skip to content

Commit 89483c0

Browse files
committed
list orphans test
1 parent 6b8d86a commit 89483c0

File tree

1 file changed

+152
-0
lines changed
  • nexus/db-queries/src/db/datastore

1 file changed

+152
-0
lines changed

nexus/db-queries/src/db/datastore/fm.rs

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,17 @@ impl DataStore {
197197
.map(|_| ())
198198
}
199199

200+
/// Lists all orphaned alternative sitreps for the provided
201+
/// [`fm::SitrepVersion`].
202+
///
203+
/// Orphaned sitreps at a given version are those which descend from the
204+
/// same parent sitrep as the current sitrep at that version, but which were
205+
/// not committed successfully to the sitrep history.
206+
///
207+
/// Note that this operation is only performed relative to a committed
208+
/// sitrep version. This is in order to prevent the returned list of sitreps
209+
/// from including sitreps which are in the process of being prepared, by
210+
/// only performing it on versions earlier than the current version.
200211
pub async fn fm_sitrep_list_orphaned(
201212
&self,
202213
opctx: &OpContext,
@@ -566,9 +577,11 @@ mod tests {
566577
use crate::db::explain::ExplainableAsync;
567578
use crate::db::pub_test_utils::TestDatabase;
568579
use chrono::Utc;
580+
use nexus_types::fm;
569581
use omicron_test_utils::dev;
570582
use omicron_uuid_kinds::CollectionUuid;
571583
use omicron_uuid_kinds::OmicronZoneUuid;
584+
use std::collections::BTreeSet;
572585

573586
#[tokio::test]
574587
async fn explain_insert_sitrep_version_query() {
@@ -834,4 +847,143 @@ mod tests {
834847
db.terminate().await;
835848
logctx.cleanup_successful();
836849
}
850+
851+
#[tokio::test]
852+
async fn test_sitrep_list_orphaned() {
853+
let logctx = dev::test_setup_log("test_sitrep_list_orphaned");
854+
let db = TestDatabase::new_with_datastore(&logctx.log).await;
855+
let (opctx, datastore) = (db.opctx(), db.datastore());
856+
857+
// First, insert an initial sitrep. This should succeed.
858+
let sitrep1 = fm::Sitrep {
859+
metadata: fm::SitrepMetadata {
860+
id: SitrepUuid::new_v4(),
861+
inv_collection_id: CollectionUuid::new_v4(),
862+
creator_id: OmicronZoneUuid::new_v4(),
863+
comment: "test sitrep v1".to_string(),
864+
time_created: Utc::now(),
865+
parent_sitrep_id: None,
866+
},
867+
};
868+
datastore
869+
.fm_sitrep_insert(&opctx, &sitrep1)
870+
.await
871+
.expect("inserting initial sitrep should succeed");
872+
873+
// Now, create some orphaned sitreps which also have no parent.
874+
let mut orphans_v1 = BTreeSet::new();
875+
for i in 1..5 {
876+
insert_orphan(&datastore, &opctx, &mut orphans_v1, None, 1, i)
877+
.await;
878+
}
879+
880+
// List orphans at the current version.
881+
let v1 = datastore
882+
.fm_current_sitrep_version(&opctx)
883+
.await
884+
.unwrap()
885+
.expect("should have a version");
886+
assert_eq!(dbg!(&v1).id, sitrep1.metadata.id);
887+
let listed_orphans = datastore
888+
.fm_sitrep_list_orphaned(&opctx, &v1)
889+
.await
890+
.expect("listing orphans should succeed")
891+
.iter()
892+
.map(|sitrep| sitrep.id)
893+
.collect::<BTreeSet<_>>();
894+
assert_eq!(dbg!(&listed_orphans), &orphans_v1);
895+
896+
// Next, create a new sitrep which descends from sitrep 0.
897+
let sitrep2 = fm::Sitrep {
898+
metadata: fm::SitrepMetadata {
899+
id: SitrepUuid::new_v4(),
900+
inv_collection_id: CollectionUuid::new_v4(),
901+
creator_id: OmicronZoneUuid::new_v4(),
902+
comment: "test sitrep v2".to_string(),
903+
time_created: Utc::now(),
904+
parent_sitrep_id: Some(sitrep1.metadata.id),
905+
},
906+
};
907+
datastore
908+
.fm_sitrep_insert(&opctx, &sitrep2)
909+
.await
910+
.expect("inserting child sitrep should succeed");
911+
912+
// Now, create some orphaned sitreps which also descend from sitreo 0.
913+
let mut orphans_v2 = BTreeSet::new();
914+
for i in 1..5 {
915+
insert_orphan(
916+
&datastore,
917+
&opctx,
918+
&mut orphans_v2,
919+
Some(sitrep1.metadata.id),
920+
2,
921+
i,
922+
)
923+
.await;
924+
}
925+
926+
// List orphans at the current version.
927+
let v2 = datastore
928+
.fm_current_sitrep_version(&opctx)
929+
.await
930+
.unwrap()
931+
.expect("should have a version");
932+
assert_eq!(dbg!(&v2).id, sitrep2.metadata.id);
933+
let listed_orphans_v2 = datastore
934+
.fm_sitrep_list_orphaned(&opctx, &v2)
935+
.await
936+
.expect("listing orphans at v2 should succeed")
937+
.iter()
938+
.map(|sitrep| sitrep.id)
939+
.collect::<BTreeSet<_>>();
940+
assert_eq!(dbg!(&listed_orphans_v2), &orphans_v2);
941+
// Orphans at the prior version should also be listable, and it should
942+
// not include the committed sitrep at that version.
943+
let listed_orphans_v1 = datastore
944+
.fm_sitrep_list_orphaned(&opctx, &v1)
945+
.await
946+
.expect("listing orphans at v1 should succeed")
947+
.iter()
948+
.map(|sitrep| sitrep.id)
949+
.collect::<BTreeSet<_>>();
950+
assert_eq!(dbg!(&listed_orphans_v1), &orphans_v1);
951+
952+
db.terminate().await;
953+
logctx.cleanup_successful();
954+
}
955+
956+
async fn insert_orphan(
957+
datastore: &DataStore,
958+
opctx: &OpContext,
959+
orphans: &mut BTreeSet<SitrepUuid>,
960+
parent_sitrep_id: Option<SitrepUuid>,
961+
v: usize,
962+
i: usize,
963+
) {
964+
let sitrep = fm::Sitrep {
965+
metadata: fm::SitrepMetadata {
966+
id: SitrepUuid::new_v4(),
967+
inv_collection_id: CollectionUuid::new_v4(),
968+
creator_id: OmicronZoneUuid::new_v4(),
969+
comment: format!("test sitrep v{i}; orphan {i}"),
970+
time_created: Utc::now(),
971+
parent_sitrep_id,
972+
},
973+
};
974+
match datastore.fm_sitrep_insert(&opctx, &sitrep).await {
975+
Ok(_) => {
976+
panic!("inserting sitrep v{v} orphan {i} should not succeed")
977+
}
978+
Err(InsertSitrepError::ParentNotCurrent(id)) => {
979+
orphans.insert(id);
980+
}
981+
Err(InsertSitrepError::Other(e)) => {
982+
panic!(
983+
"expected inserting sitrep v{v} orphan {i} to fail because \
984+
its parent is out of date, but saw an unexpected error: {e}"
985+
);
986+
}
987+
}
988+
}
837989
}

0 commit comments

Comments
 (0)