@@ -373,19 +373,32 @@ impl DataStore {
373
373
374
374
use db:: schema:: instance:: dsl as instance_dsl;
375
375
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 ( ) ;
377
385
378
- let mut results = instance_dsl:: instance
386
+ let query = instance_dsl:: instance
379
387
. filter ( instance_dsl:: id. eq ( authz_instance. id ( ) ) )
380
388
. filter ( instance_dsl:: time_deleted. is_null ( ) )
381
389
. left_join (
382
- vmm_dsl:: vmm. on ( ( vmm_dsl:: id
390
+ active_vmm. on ( active_vmm
391
+ . field ( vmm:: id)
383
392
. nullable ( )
384
393
. 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 ( ) ) ) ,
389
402
)
390
403
. left_join (
391
404
migration_dsl:: migration. on ( migration_dsl:: id
@@ -395,77 +408,33 @@ impl DataStore {
395
408
)
396
409
. select ( (
397
410
Instance :: as_select ( ) ,
398
- Option :: < Vmm > :: as_select ( ) ,
411
+ active_vmm. fields ( vmm_selection) . nullable ( ) ,
412
+ target_vmm. fields ( vmm_selection) . nullable ( ) ,
399
413
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 ?
412
425
)
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
+ } ) ?;
467
436
468
- Ok ( snapshot )
437
+ Ok ( InstanceSnapshot { instance , migration , active_vmm , target_vmm } )
469
438
}
470
439
471
440
// TODO-design It's tempting to return the updated state of the Instance
0 commit comments