[bgp-config] move lookups from datastore#10777
Conversation
5c68bbc to
29f94be
Compare
29f94be to
7fe1f19
Compare
|
This is relatively minimal change that moves the lookups from the the datastore while keeping the guarantees around announce set existing. I've adapted the The only possible concern I have is if we shouldn't do the proper lookup mechanics when initializing the rack in nexus/src/app/rack.rs for whatever reason. If fine, an argument could be made to call the app layer |
|
Looks good! A couple small suggestions from bot review:
diff --git a/nexus/db-queries/src/db/datastore/switch_port.rs b/nexus/db-queries/src/db/datastore/switch_port.rs
index 74e4e3de1a..d22d355d66 100644
--- a/nexus/db-queries/src/db/datastore/switch_port.rs
+++ b/nexus/db-queries/src/db/datastore/switch_port.rs
@@ -2666,19 +2666,14 @@
NameOrId::Name(name) => {
let (.., authz_bgp_config) =
LookupPath::new(&opctx, datastore)
- .bgp_config_name_owned(name.clone().into())
+ .bgp_config_name_owned(name.into())
.lookup_for(nexus_auth::authz::Action::Read)
.await
.expect("lookup bgp config");
- let db_bgp_config = datastore
- .bgp_config_get(opctx, authz_bgp_config.id())
- .await
- .expect("bgp config should be present in db");
-
assert_eq!(
- db_bgp_config.identity.name,
- nexus_db_model::Name(name)
+ db_peer.bgp_config_id(),
+ authz_bgp_config.id()
);
}
}
diff --git a/nexus/src/app/bgp.rs b/nexus/src/app/bgp.rs
index 59e7b7fe69..b9c8a80e2c 100644
--- a/nexus/src/app/bgp.rs
+++ b/nexus/src/app/bgp.rs
@@ -46,12 +46,12 @@
) -> LookupResult<BgpConfig> {
opctx.authorize(authz::Action::Read, &authz::FLEET).await?;
- let (.., authz_bgp_config, _) = self
- .bgp_config_lookup(opctx, name_or_id.clone())?
+ let (.., db_bgp_config) = self
+ .bgp_config_lookup(opctx, name_or_id)?
.fetch_for(authz::Action::Read)
.await?;
- self.db_datastore.bgp_config_get(opctx, authz_bgp_config.id()).await
+ Ok(db_bgp_config)
}
pub async fn bgp_config_list(TOCTOU riskI was curious about the TOCTOU risk of moving the lookups out of the transaction. Here is a 🤖 summary of why it's not an issue here (after the above fix).
One more fixAfter putting together the above table I found one more suggestion: 404 instead of 500ing when a BGP config gets deleted in the middle of an update. diff --git a/nexus/db-queries/src/db/datastore/bgp.rs b/nexus/db-queries/src/db/datastore/bgp.rs
index 7287b4f81b..dcc7887da8 100644
--- a/nexus/db-queries/src/db/datastore/bgp.rs
+++ b/nexus/db-queries/src/db/datastore/bgp.rs
@@ -256,7 +256,10 @@
err
} else {
error!(opctx.log, "{msg}"; "error" => ?e);
- public_error_from_diesel(e, ErrorHandler::Server)
+ public_error_from_diesel(
+ e,
+ ErrorHandler::NotFoundByResource(authz_bgp_config),
+ )
}
})
}
@@ -1122,7 +1125,7 @@
// Update the BGP config
datastore
- .bgp_config_update(&opctx, &authz_bgp_config, update)
+ .bgp_config_update(&opctx, &authz_bgp_config, update.clone())
.await
.expect("update bgp config");
@@ -1140,6 +1143,22 @@
assert_eq!(bgp_config.bgp_announce_set_id, new_announce_id);
assert_eq!(bgp_config.max_paths.0, new_max_paths.as_u8());
+ // Simulate deletion after the app layer has resolved the config but
+ // before the datastore update begins.
+ datastore
+ .bgp_config_delete(&opctx, &authz_bgp_config)
+ .await
+ .expect("delete bgp config");
+
+ let err = datastore
+ .bgp_config_update(&opctx, &authz_bgp_config, update)
+ .await
+ .expect_err("updating a deleted BGP config should fail");
+ assert!(
+ matches!(err, Error::ObjectNotFound { .. }),
+ "unexpected error: {err:?}"
+ );
+
db.terminate().await;
logctx.cleanup_successful();
} |
👍
I didn't do this initially because there is an explicit filter there for |
|
Yup, the lookups do the soft delete check: omicron/nexus/db-macros/src/lookup.rs Lines 834 to 838 in 5e2a6a6 omicron/nexus/db-lookup/src/lookup.rs Lines 894 to 900 in 5e2a6a6 |
jgallagher
left a comment
There was a problem hiding this comment.
LGTM.
The only possible concern I have is if we shouldn't do the proper lookup mechanics when initializing the rack in nexus/src/app/rack.rs for whatever reason. If fine, an argument could be made to call the app layer bgp_config_create directly from rack_initialize, since it's already in app and I don't think we wan to have different business logic for it.
AFAIK authz / lookups all work fine during rack initialization. It looks like every other call in that method goes directly to the datastore. I hope this isn't true but it easily could be: some app-level methods might expect rack initialization to have already happened in some way (unwrapping an option? assuming some db table is populated? whatever), so maybe it's safer to only call datastore methods if we're worried about that?
| }, | ||
| }?; | ||
|
|
||
| let (.., authz_bgp_announce_set) = self |
There was a problem hiding this comment.
Can we get the announce set ID from the return value of the create above (line 416) instead of having to look it up here? I guess we'd have to look it up inside the ObjectAlreadyExists error branch since that doesn't return an ID :(
There was a problem hiding this comment.
Yep. But I'm inclined to use the app version given your comment.
bgp-configlookups out of datastore and into the app layer.