Skip to content

Commit 8cd2861

Browse files
committed
Make DataStore::instance_fetch_all less ugly
This commit rewrites the `DataStore::instance_fetch_all` query added in #5888 to perform two separate left joins with the `vmm` table, so that the result set only contains a single row. This is a bit more aesthetically pleasing than having to iterate over a multi-row result set, but should still return the same values. Thanks to @jgallagher and @luqmana for figuring out how to do the Diesel alias!
1 parent 1b89771 commit 8cd2861

File tree

1 file changed

+44
-75
lines changed

1 file changed

+44
-75
lines changed

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

Lines changed: 44 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -373,19 +373,32 @@ impl DataStore {
373373

374374
use db::schema::instance::dsl as instance_dsl;
375375
use db::schema::migration::dsl as migration_dsl;
376-
use db::schema::vmm::dsl as vmm_dsl;
376+
use db::schema::vmm;
377+
378+
// Create a Diesel alias to allow us to LEFT JOIN the `instance` table
379+
// with the `vmm` table twice; once on the `active_propolis_id` and once
380+
// on the `target_propolis_id`.
381+
let (active_vmm, target_vmm) =
382+
diesel::alias!(vmm as active_vmm, vmm as target_vmm);
383+
let vmm_selection =
384+
<Vmm as Selectable<diesel::pg::Pg>>::construct_selection();
377385

378-
let mut results = instance_dsl::instance
386+
let query = instance_dsl::instance
379387
.filter(instance_dsl::id.eq(authz_instance.id()))
380388
.filter(instance_dsl::time_deleted.is_null())
381389
.left_join(
382-
vmm_dsl::vmm.on((vmm_dsl::id
390+
active_vmm.on(active_vmm
391+
.field(vmm::id)
383392
.nullable()
384393
.eq(instance_dsl::active_propolis_id)
385-
.or(vmm_dsl::id
386-
.nullable()
387-
.eq(instance_dsl::target_propolis_id)))
388-
.and(vmm_dsl::time_deleted.is_null())),
394+
.and(active_vmm.field(vmm::time_deleted).is_null())),
395+
)
396+
.left_join(
397+
target_vmm.on(target_vmm
398+
.field(vmm::id)
399+
.nullable()
400+
.eq(instance_dsl::target_propolis_id)
401+
.and(target_vmm.field(vmm::time_deleted).is_null())),
389402
)
390403
.left_join(
391404
migration_dsl::migration.on(migration_dsl::id
@@ -395,77 +408,33 @@ impl DataStore {
395408
)
396409
.select((
397410
Instance::as_select(),
398-
Option::<Vmm>::as_select(),
411+
active_vmm.fields(vmm_selection).nullable(),
412+
target_vmm.fields(vmm_selection).nullable(),
399413
Option::<Migration>::as_select(),
400-
))
401-
.load_async::<(Instance, Option<Vmm>, Option<Migration>)>(
402-
&*self.pool_connection_authorized(opctx).await?,
403-
)
404-
.await
405-
.map_err(|e| {
406-
public_error_from_diesel(
407-
e,
408-
ErrorHandler::NotFoundByLookup(
409-
ResourceType::Instance,
410-
LookupType::ById(authz_instance.id()),
411-
),
414+
));
415+
416+
let (instance, active_vmm, target_vmm, migration) =
417+
query
418+
.first_async::<(
419+
Instance,
420+
Option<Vmm>,
421+
Option<Vmm>,
422+
Option<Migration>,
423+
)>(
424+
&*self.pool_connection_authorized(opctx).await?
412425
)
413-
})?
414-
.into_iter();
415-
416-
// Because we join with the `vmm` table on VMMs whose ID is the active
417-
// *or* target VMM ID in the instance record, this query may return a
418-
// result set with multiple rows. If it does, we have to iterate over
419-
// them and determine which VMM result is the active VMM and which is the
420-
// target. The instance and migration records should be identical, so we
421-
// only need the first ones.
422-
//
423-
// Yes, this code is a bit unfortunate, but Diesel doesn't like the idea
424-
// of joining with the same table twice on different columns...so, this,
425-
// at least, works.
426-
let (instance, vmm, migration) = results.next().ok_or_else(|| {
427-
LookupType::ById(authz_instance.id())
428-
.into_not_found(ResourceType::Instance)
429-
})?;
430-
let mut snapshot = InstanceSnapshot {
431-
instance,
432-
migration,
433-
active_vmm: None,
434-
target_vmm: None,
435-
};
436-
437-
impl InstanceSnapshot {
438-
fn add_vmm(&mut self, vmm: Vmm) {
439-
match vmm.id {
440-
id if self.instance.runtime_state.propolis_id == Some(id) => {
441-
self.active_vmm = Some(vmm)
442-
}
443-
id if self.instance.runtime_state.dst_propolis_id == Some(id) => {
444-
self.target_vmm = Some(vmm)
445-
}
446-
_ => debug_assert!(
447-
false,
448-
"tried to add a VMM to an instance snapshot that was neither \
449-
the active nor target VMM...was this VMM actually returned by \
450-
the instance snapshot query?",
451-
),
452-
}
453-
}
454-
}
455-
if let Some(vmm) = vmm {
456-
snapshot.add_vmm(vmm);
457-
}
458-
if let Some((_, Some(vmm), _)) = results.next() {
459-
snapshot.add_vmm(vmm);
460-
}
461-
462-
debug_assert!(
463-
results.next().is_none(),
464-
"instance snapshot query should not return more than two rows, as \
465-
as an instance has 0-1 active and 0-1 target VMMs"
466-
);
426+
.await
427+
.map_err(|e| {
428+
public_error_from_diesel(
429+
e,
430+
ErrorHandler::NotFoundByLookup(
431+
ResourceType::Instance,
432+
LookupType::ById(authz_instance.id()),
433+
),
434+
)
435+
})?;
467436

468-
Ok(snapshot)
437+
Ok(InstanceSnapshot { instance, migration, active_vmm, target_vmm })
469438
}
470439

471440
// TODO-design It's tempting to return the updated state of the Instance

0 commit comments

Comments
 (0)