fix(api-core): scope transaction lint exemptions#3872
Conversation
Summary by CodeRabbit
WalkthroughThe change narrows transaction lint exemptions, documents intentional cross-await transaction usage, centralizes host UEFI credential resolution for machine deletion, and adds explicit Astra transaction commit error mapping. ChangesTransaction and credential updates
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@crates/api-core/src/handlers/machine.rs`:
- Around line 38-53: Refactor resolve_host_uefi_clear_credentials and the
underlying host_uefi_clear_credentials flow so the UEFI device version is read
in a short transaction, that transaction is committed before any credential
lookup, and Vault credential resolution occurs outside the transaction. Remove
the local txn_held_across_await allowance and preserve the existing
Option<Credentials> behavior and error handling.
- Around line 46-55: Update the transaction handling in the api.txn_begin
success branch so txn.commit().await is checked before returning credentials. Do
not discard commit failures; return None (or the existing failure result) when
the commit fails, and only return credentials.ok() after a successful commit.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 84740f61-11dc-4437-84a5-3abbefbf0b3e
📒 Files selected for processing (7)
crates/api-core/src/handlers/astra.rscrates/api-core/src/handlers/machine.rscrates/api-core/src/handlers/uefi.rscrates/api-core/src/instance/mod.rscrates/api-core/src/lib.rscrates/api-core/src/run.rscrates/api-core/src/secrets/re_wrap.rs
| // TODO(#2991, @spydaNVIDIA): avoid holding this transaction while the | ||
| // credential reader may perform a remote Vault request. Keep the allowance on | ||
| // this small helper rather than the full force-delete handler. | ||
| #[allow(txn_held_across_await)] | ||
| async fn resolve_host_uefi_clear_credentials( | ||
| api: &Api, | ||
| bmc_mac_address: mac_address::MacAddress, | ||
| ) -> Option<Credentials> { | ||
| match api.txn_begin().await { | ||
| Ok(mut txn) => { | ||
| let credentials = crate::handlers::uefi::host_uefi_clear_credentials( | ||
| &mut txn, | ||
| api.redfish_pool.credential_reader(), | ||
| bmc_mac_address, | ||
| ) | ||
| .await; |
There was a problem hiding this comment.
🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift
Do not hold the database transaction across the Vault request.
host_uefi_clear_credentials awaits credential resolution while holding &mut txn; its implementation in crates/api-core/src/handlers/uefi.rs:132-141 performs the remote lookup after the database read. This can retain database connections and locks during slow external I/O, reducing pool capacity under concurrent force deletes.
Resolve the UEFI device version in a short transaction, commit it, then perform the credential lookup outside the transaction and remove the local allowance.
As per coding guidelines, transactions must not be held across awaits; as per path instructions, transaction lifetime and resource behavior should be reviewed explicitly.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@crates/api-core/src/handlers/machine.rs` around lines 38 - 53, Refactor
resolve_host_uefi_clear_credentials and the underlying
host_uefi_clear_credentials flow so the UEFI device version is read in a short
transaction, that transaction is committed before any credential lookup, and
Vault credential resolution occurs outside the transaction. Remove the local
txn_held_across_await allowance and preserve the existing Option<Credentials>
behavior and error handling.
Sources: Coding guidelines, Path instructions
| match api.txn_begin().await { | ||
| Ok(mut txn) => { | ||
| let credentials = crate::handlers::uefi::host_uefi_clear_credentials( | ||
| &mut txn, | ||
| api.redfish_pool.credential_reader(), | ||
| bmc_mac_address, | ||
| ) | ||
| .await; | ||
| let _ = txn.commit().await; | ||
| credentials.ok() |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Honor transaction commit failures before using the credentials.
let _ = txn.commit().await allows credentials.ok() to return Some even when the transaction failed to commit. This changes the previous best-effort behavior and can issue the device-side clear using credentials resolved from an unsuccessful transaction.
- let _ = txn.commit().await;
- credentials.ok()
+ if txn.commit().await.is_err() {
+ return None;
+ }
+ credentials.ok()As per coding guidelines, transaction results must not be discarded with let _ = ...; handle failures explicitly.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| match api.txn_begin().await { | |
| Ok(mut txn) => { | |
| let credentials = crate::handlers::uefi::host_uefi_clear_credentials( | |
| &mut txn, | |
| api.redfish_pool.credential_reader(), | |
| bmc_mac_address, | |
| ) | |
| .await; | |
| let _ = txn.commit().await; | |
| credentials.ok() | |
| match api.txn_begin().await { | |
| Ok(mut txn) => { | |
| let credentials = crate::handlers::uefi::host_uefi_clear_credentials( | |
| &mut txn, | |
| api.redfish_pool.credential_reader(), | |
| bmc_mac_address, | |
| ) | |
| .await; | |
| if txn.commit().await.is_err() { | |
| return None; | |
| } | |
| credentials.ok() |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@crates/api-core/src/handlers/machine.rs` around lines 46 - 55, Update the
transaction handling in the api.txn_begin success branch so txn.commit().await
is checked before returning credentials. Do not discard commit failures; return
None (or the existing failure result) when the commit fails, and only return
credentials.ok() after a successful commit.
Source: Coding guidelines
Signed-off-by: Dmitry Porokh <dporokh@nvidia.com>
1a2fa42 to
38c72d7
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@crates/api-core/src/handlers/uefi.rs`:
- Around line 129-131: Remove transaction-across-await behavior from the UEFI
credential flows: in crates/api-core/src/handlers/uefi.rs lines 129-131, commit
the device-version lookup before resolving clear credentials; in
crates/api-core/src/handlers/uefi.rs lines 113-115, separate set-flow database
work from credential retrieval; and in crates/api-core/src/handlers/machine.rs
lines 38-41, remove the helper-level allowance after moving Vault lookup outside
the transaction.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 13094f32-2c56-42aa-8d2f-1e9a603133e1
📒 Files selected for processing (7)
crates/api-core/src/handlers/astra.rscrates/api-core/src/handlers/machine.rscrates/api-core/src/handlers/uefi.rscrates/api-core/src/instance/mod.rscrates/api-core/src/lib.rscrates/api-core/src/run.rscrates/api-core/src/secrets/re_wrap.rs
🚧 Files skipped from review as they are similar to previous changes (3)
- crates/api-core/src/instance/mod.rs
- crates/api-core/src/secrets/re_wrap.rs
- crates/api-core/src/lib.rs
| // TODO(#2991, @spydaNVIDIA): do not hold the database connection while the | ||
| // credential reader may perform a remote Vault request. | ||
| #[allow(txn_held_across_await)] |
There was a problem hiding this comment.
🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift
Remove the UEFI transaction-across-Vault awaits.
All three paths retain database connections during remote credential resolution, so Vault latency can consume API database-pool capacity.
crates/api-core/src/handlers/uefi.rs#L129-L131: commit the device-version lookup before resolving clear credentials.crates/api-core/src/handlers/uefi.rs#L113-L115: separate set-flow database work from credential retrieval.crates/api-core/src/handlers/machine.rs#L38-L41: remove the helper-level allowance after moving Vault lookup outside the transaction.
📍 Affects 2 files
crates/api-core/src/handlers/uefi.rs#L129-L131(this comment)crates/api-core/src/handlers/uefi.rs#L113-L115crates/api-core/src/handlers/machine.rs#L38-L41
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@crates/api-core/src/handlers/uefi.rs` around lines 129 - 131, Remove
transaction-across-await behavior from the UEFI credential flows: in
crates/api-core/src/handlers/uefi.rs lines 129-131, commit the device-version
lookup before resolving clear credentials; in
crates/api-core/src/handlers/uefi.rs lines 113-115, separate set-flow database
work from credential retrieval; and in crates/api-core/src/handlers/machine.rs
lines 38-41, remove the helper-level allowance after moving Vault lookup outside
the transaction.
Sources: Coding guidelines, Path instructions
Vault import and secret re-wrap each need to hold a lock for their whole run -- one importer at a time, one re-wrap at a time -- while they await Vault/KMS and open a transaction per batch. Both do it the same way: take a Postgres *session* advisory lock on a connection they `detach()` from the pool, and let dropping that connection release the lock. No transaction is ever open on it. It's the right design, but NVIDIA#3872 (which restored `txn_held_across_await` on `api-core`) can't tell a bare `PgConnection` holding only a session lock from a real transaction, so both had been parked behind `#[allow(txn_held_across_await)]`. So, this gives the pattern a name. `db::secrets::SessionLock` -- living next to the lock primitives it wraps -- owns the detached connection and releases on drop, with `acquire` (waits) and `try_acquire` (returns `None` when another session holds it). It's `#[must_use]`, so you can't accidentally drop the lock the instant you take it. The value held across the awaits is now a `SessionLock`, a lock holder rather than a connection, so the lint is satisfied for the true reason: nothing here is a transaction. Primary callouts are: - `import_vault_secrets_once` and `re_wrap_stale` each collapse to a one-line `SessionLock::{acquire,try_acquire}`, and both `#[allow(txn_held_across_await)]` go away. - The "why we detach a dedicated connection" explanation moves to the guard's doc, instead of being re-explained at each call site. No behavior change -- the guard does exactly what the hand-rolled `acquire().detach()` + lock did, releasing on drop the same way (including on an early `?` return). The existing `re_wrap_stale` tests exercise the new `try_acquire` path. This supports NVIDIA#4002 Follow-up to NVIDIA#3872. Signed-off-by: Chet Nichols III <chetn@nvidia.com>
Restore custom transaction-lint coverage for production api-core code. The API crate split in #2054 unintentionally made the test-support exemption crate-wide, while carbide-lints runs with --all-features; as a result, enabling test-support suppressed transaction warnings in production modules.
Changes:
The UEFI and force-delete behavior is intentionally preserved. The prefix and secrets allowances document cases where the custom lint cannot distinguish database-only savepoint work or detached session-lock connections from transactions.
Related issues
#2054
#2904
#2991
#3456
#2665
Type of Change
Breaking Changes
Testing
Additional Notes