Skip to content

refactor: Migrate Machine API handlers to WithTx helper#475

Open
chet wants to merge 1 commit intoNVIDIA:mainfrom
chet:with-tx-machine
Open

refactor: Migrate Machine API handlers to WithTx helper#475
chet wants to merge 1 commit intoNVIDIA:mainfrom
chet:with-tx-machine

Conversation

@chet
Copy link
Copy Markdown
Contributor

@chet chet commented May 4, 2026

Description

This applies the new WithTx pattern from #462 to the machines handlers, including using the TerminateWorkflowOnTimeOut helper.

Keeping these PRs smaller and tightly scoped so they're:

  • In theory a little easier to read.
  • More tightly scoped/less "blast radius" per PR.
  • A little nicer on/for @coderabbitai. 😆

@coderabbitai also gave some feedback, which has also been addressed, where this:

  • Makes sure to use the contextual logger instead of the global log in the maintenance-mode workflow error path.
  • Passes tx to common.GetInfrastructureProviderForOrg so the read participates in the same transaction as the surrounding writes.

Signed-off-by: Chet Nichols III chetn@nvidia.com

Type of Change

  • Feature - New feature or functionality (feat:)
  • Fix - Bug fixes (fix:)
  • Chore - Modification or removal of existing functionality (chore:)
  • Refactor - Refactoring of existing functionality (refactor:)
  • Docs - Changes in documentation or OpenAPI schema (docs:)
  • CI - Changes in GitHub workflows. Requires additional scrutiny (ci:)
  • Version - Issuing a new release version (version:)

Services Affected

  • API - API models or endpoints updated
  • Workflow - Workflow service updated
  • DB - DB DAOs or migrations updated
  • Site Manager - Site Manager updated
  • Cert Manager - Cert Manager updated
  • Site Agent - Site Agent updated
  • RLA - RLA service updated
  • Powershelf Manager - Powershelf Manager updated
  • NVSwitch Manager - NVSwitch Manager updated

Related Issues (Optional)

Breaking Changes

  • This PR contains breaking changes

Testing

  • Unit tests added/updated
  • Integration tests added/updated
  • Manual testing performed
  • No testing required (docs, internal refactor, etc.)

Additional Notes

@chet chet requested a review from a team as a code owner May 4, 2026 04:26
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 4, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Refactors machine update and delete handlers to run DB mutations inside cdb.WithTx transaction closures: row/advisory locking, validation, instance-type association changes, maintenance/labels updates, capability/interface cleanup, synchronous Temporal site workflows invoked within transactions, and removal of unused database/sql import.

Changes

Transaction Refactor for Machine Handlers

Layer / File(s) Summary
Import Cleanup
api/pkg/api/handler/machine.go
Removed unused database/sql import after migrating DB logic to cdb.WithTx.
Row Locking & Reads
api/pkg/api/handler/machine.go
Machine is fetched with GetByID(..., forUpdate=true) inside transactions to lock the row and re-fetch relations before mutations.
Advisory Lock / Prechecks
api/pkg/api/handler/machine.go
Advisory locks and allocation-constraint checks for InstanceType reassignment are performed inside the transaction closure.
Core DB Mutations
api/pkg/api/handler/machine.go
InstanceType association delete/create, maintenance/initializing status and message updates, label updates, capability/interface cleanup, and machine deletion occur inside cdb.WithTx closures to ensure atomicity.
Temporal Workflow Integration
api/pkg/api/handler/machine.go
Synchronous Temporal site workflows (RemoveMachineInstanceTypeAssociation, AssociateMachinesWithInstanceType, SetMachineMaintenance, UpdateMachineMetadata) are invoked from within transactions; workflow timeouts are routed to deferred post-rollback termination callbacks.
Error Handling
api/pkg/api/handler/machine.go
Transaction closures return cutil.NewAPIError for validation/DB/Temporal failures; outer handler uses common.HandleTxError for transaction-level errors.
Tests / Documentation
(none changed)
No tests or docs updated in this diff.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant API_Handler
    participant DB as Database
    participant Temporal

    Client->>API_Handler: Update/Delete Machine request
    API_Handler->>DB: cdb.WithTx { GetByID(forUpdate), advisory locks, validations }
    DB-->>API_Handler: locked machine + relations
    API_Handler->>DB: apply DB mutations (assoc add/remove, status, labels, cleanup)
    API_Handler->>Temporal: run synchronous site workflow
    Temporal-->>API_Handler: workflow success / timeout/failure
    alt workflow success
        API_Handler->>DB: commit transaction (WithTx returns)
        DB-->>API_Handler: commit
        API_Handler->>Client: success response
    else workflow timeout/failure
        API_Handler->>Temporal: attempt TerminateWorkflowOnTimeOut (deferred post-rollback)
        API_Handler->>DB: WithTx returns error -> rollback
        API_Handler->>Client: error response
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description check ✅ Passed The description is directly related to the changeset, clearly articulating the refactoring scope, rationale, reviewer feedback incorporated, and specific implementation details.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title accurately and concisely describes the primary refactoring objective—migrating Machine API handlers to use the WithTx helper pattern—which aligns directly with the changeset's main scope.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

🔐 TruffleHog Secret Scan

No secrets or credentials found!

Your code has been scanned for 700+ types of secrets and credentials. All clear! 🎉

🔗 View scan details

🕐 Last updated: 2026-05-04 04:28:07 UTC | Commit: cda7168

@chet chet changed the title refactor: Migrate machine handler to WithTx refactor: Migrate machines handler to WithTx May 4, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
api/pkg/api/handler/machine.go (1)

1243-1261: ⚡ Quick win

Unify timeout termination handling with the shared helper to reduce drift.

Both maintenance-mode and labels branches duplicate timeout/terminate logic. Consider reusing the same deferred common.TerminateWorkflowOnTimeOut pattern already used in the InstanceType branch for consistency and easier maintenance.

♻️ Suggested consolidation pattern
+var timeoutResp func() error
 err = cdb.WithTx(ctx, umh.dbSession, func(mnTx *cdb.Tx) error {
   ...
   wferr = we.Get(wfCtx, nil)
   if wferr != nil {
     var timeoutErr *tp.TimeoutError
     if errors.As(wferr, &timeoutErr) || wferr == context.DeadlineExceeded || wfCtx.Err() != nil {
-      logger.Error().Err(wferr).Msg("failed to set/remove Machine maintenance mode, timeout occurred executing workflow on Site.")
-      newctx, newcancel := context.WithTimeout(context.Background(), cutil.WorkflowContextNewAfterTimeout)
-      defer newcancel()
-      serr := stc.TerminateWorkflow(newctx, wid, "", "timeout occurred executing set/remove maintenance mode Machine workflow")
-      if serr != nil {
-        return cutil.NewAPIError(http.StatusInternalServerError, fmt.Sprintf("Failed to terminate synchronous set/remove Machine maintenance mode workflow after timeout, Cloud and Site data may be de-synced: %s", serr), nil)
-      }
-      return cutil.NewAPIError(http.StatusInternalServerError, fmt.Sprintf("Failed to set/remove Machine maintenance mode, timeout occurred executing workflow on Site: %s", wferr), nil)
+      capturedErr := wferr
+      timeoutResp = func() error {
+        return common.TerminateWorkflowOnTimeOut(c, logger, stc, wid, capturedErr, "MachineMaintenance", "SetMachineMaintenance")
+      }
+      return cutil.NewAPIError(http.StatusInternalServerError, "workflow timeout", nil)
     }
     ...
   }
   return nil
 })
+if timeoutResp != nil {
+  return timeoutResp()
+}
 if err != nil {
   return common.HandleTxError(c, logger, err, "Failed to update Machine, DB transaction error")
 }

Also applies to: 1340-1358

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/pkg/api/handler/machine.go` around lines 1243 - 1261, The
timeout/terminate logic in machine.go duplicates the InstanceType branch;
replace the inline timeout handling (the errors.As check on wferr,
context.DeadlineExceeded/wfCtx.Err() and the context.WithTimeout +
stc.TerminateWorkflow call that returns API errors) with the shared helper
common.TerminateWorkflowOnTimeOut so both maintenance-mode and labels branches
reuse the same deferred termination pattern; specifically, remove the inline
block that references wferr, wfCtx, wid and stc.TerminateWorkflow and
invoke/defer common.TerminateWorkflowOnTimeOut with the same parameters/context
as used by the InstanceType branch, and make the same change at the other
duplicate site (the similar block around the 1340–1358 area) to ensure
consistent behavior and logging.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@api/pkg/api/handler/machine.go`:
- Around line 1635-1638: The handler currently logs the DB error but passes the
raw error (derr) into cutil.NewAPIError which can leak internal DB details to
clients via HandleTxError; change the NewAPIError call in the machine deletion
flow (the block that logs "error while retrieving StatusDetail for Machine" and
calls cutil.NewAPIError) to pass nil (or a sanitized message) as the error
payload while keeping the detailed derr only in
logger.Error().Err(derr).Msg(...), so clients receive a generic "Failed to
delete Machine" API error without internal DB details.

---

Nitpick comments:
In `@api/pkg/api/handler/machine.go`:
- Around line 1243-1261: The timeout/terminate logic in machine.go duplicates
the InstanceType branch; replace the inline timeout handling (the errors.As
check on wferr, context.DeadlineExceeded/wfCtx.Err() and the context.WithTimeout
+ stc.TerminateWorkflow call that returns API errors) with the shared helper
common.TerminateWorkflowOnTimeOut so both maintenance-mode and labels branches
reuse the same deferred termination pattern; specifically, remove the inline
block that references wferr, wfCtx, wid and stc.TerminateWorkflow and
invoke/defer common.TerminateWorkflowOnTimeOut with the same parameters/context
as used by the InstanceType branch, and make the same change at the other
duplicate site (the similar block around the 1340–1358 area) to ensure
consistent behavior and logging.
🪄 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: 5d079968-c2de-4e56-8d2a-7299e158c92c

📥 Commits

Reviewing files that changed from the base of the PR and between bce7503 and cda7168.

📒 Files selected for processing (1)
  • api/pkg/api/handler/machine.go

Comment thread api/pkg/api/handler/machine.go
@chet chet force-pushed the with-tx-machine branch from cda7168 to f98b9b2 Compare May 4, 2026 04:36
@chet chet changed the title refactor: Migrate machines handler to WithTx refactor: Migrate machines handlers to WithTx May 4, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 4, 2026

🔍 Container Scan Summary

Service Total Critical High Medium Low Other
nico-nsm 64 2 20 33 9 0
nico-psm 56 4 29 13 2 8
nico-rest-api 57 4 30 13 2 8
nico-rest-cert-manager 54 4 28 13 1 8
nico-rest-db 55 4 28 13 2 8
nico-rest-site-agent 54 4 28 13 1 8
nico-rest-site-manager 54 4 28 13 1 8
nico-rest-workflow 56 4 29 13 2 8
nico-rla 55 4 28 13 2 8
TOTAL 505 34 248 137 22 64

Per-CVE detail lives in the per-service grype-* artifacts (JSON + SARIF). Severity counts only — no CVE IDs published here.

@thossain-nv thossain-nv changed the title refactor: Migrate machines handlers to WithTx refactor: Migrate Machine API handlers to use WithTx transaction helper May 6, 2026
@chet chet force-pushed the with-tx-machine branch from f98b9b2 to 4096be5 Compare May 6, 2026 17:41
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
api/pkg/api/handler/machine.go (1)

1689-1692: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Typographical error in API error message.

The word "assocations" should be "associations" in the error message returned to clients.

📝 Suggested fix
 if derr != nil {
   logger.Error().Err(derr).Msg("error pulling instance details for Machine in DB")
-  return cutil.NewAPIError(http.StatusInternalServerError, "Failed to query Instance assocations for Machine", nil)
+  return cutil.NewAPIError(http.StatusInternalServerError, "Failed to query Instance associations for Machine", nil)
 }
🤖 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 `@api/pkg/api/handler/machine.go` around lines 1689 - 1692, Fix the
typographical error in the API error message returned by the cutil.NewAPIError
call: change the string "Failed to query Instance assocations for Machine" to
"Failed to query Instance associations for Machine" where the error handling
uses derr, logger.Error().Err(derr).Msg(...) and returns cutil.NewAPIError(...)
so clients receive the corrected spelling.
🧹 Nitpick comments (1)
api/pkg/api/handler/machine.go (1)

1034-1039: 💤 Low value

Workflow ID format inconsistency.

The workflow ID at line 1035 lacks the dash separator before the UUID suffix. Other workflow IDs in this file consistently use a trailing dash (e.g., "associate-machines-with-instance-type-", "site-set-maintenance-", "site-update-machine-metadata-").

🔧 Suggested fix
 workflowOptions := temporalClient.StartWorkflowOptions{
-  ID:                       "remove-machine-instance-type-association" + machine.InstanceTypeID.String(),
+  ID:                       "remove-machine-instance-type-association-" + machine.InstanceTypeID.String(),
   TaskQueue:                queue.SiteTaskQueue,
   WorkflowExecutionTimeout: cutil.WorkflowExecutionTimeout,
 }
🤖 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 `@api/pkg/api/handler/machine.go` around lines 1034 - 1039, The workflow ID
string in the temporalClient.StartWorkflowOptions (variable workflowOptions) is
missing the trailing dash before the machine.InstanceTypeID UUID; update the ID
construction for "remove-machine-instance-type-association" to include a "-"
separator before machine.InstanceTypeID.String() so it matches the other IDs
(e.g., "associate-machines-with-instance-type-"), ensuring the ID becomes
"remove-machine-instance-type-association-" + machine.InstanceTypeID.String().
🤖 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.

Duplicate comments:
In `@api/pkg/api/handler/machine.go`:
- Around line 1689-1692: Fix the typographical error in the API error message
returned by the cutil.NewAPIError call: change the string "Failed to query
Instance assocations for Machine" to "Failed to query Instance associations for
Machine" where the error handling uses derr, logger.Error().Err(derr).Msg(...)
and returns cutil.NewAPIError(...) so clients receive the corrected spelling.

---

Nitpick comments:
In `@api/pkg/api/handler/machine.go`:
- Around line 1034-1039: The workflow ID string in the
temporalClient.StartWorkflowOptions (variable workflowOptions) is missing the
trailing dash before the machine.InstanceTypeID UUID; update the ID construction
for "remove-machine-instance-type-association" to include a "-" separator before
machine.InstanceTypeID.String() so it matches the other IDs (e.g.,
"associate-machines-with-instance-type-"), ensuring the ID becomes
"remove-machine-instance-type-association-" + machine.InstanceTypeID.String().

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: f7bff02e-ad2e-4f5b-a43b-3d8b488be206

📥 Commits

Reviewing files that changed from the base of the PR and between f98b9b2 and b330622.

📒 Files selected for processing (1)
  • api/pkg/api/handler/machine.go

@chet chet force-pushed the with-tx-machine branch from b330622 to bf2c2a2 Compare May 6, 2026 19:55
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 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 `@api/pkg/api/handler/machine.go`:
- Around line 1253-1255: The timeout helper is being called with the wrong
workflow name; update the calls to common.TerminateWorkflowOnTimeOut so the
passed workflowName matches the actual starter functions (use
"SetMachineMaintenance" where you currently pass "SetMaintenanceMode", and
"UpdateMachineMetadata" where you currently pass "UpdateMetadata"). Locate the
timeoutResp closures that call common.TerminateWorkflowOnTimeOut (variables:
timeoutResp, c, logger, stc, wid, wferr) and replace the incorrect literal
workflow name argument with the correct starter name string so logs, termination
reasons, and responses reference the actual workflow started.
- Around line 1642-1649: The code currently blocks deletion by comparing
lastStatus.Message to the literal "Machine is missing on Site"; instead, change
the check to use a structured status field (e.g., a status code/enum or boolean)
rather than free-form text: locate the branch that checks IsMissingOnSite and
replace the string comparison of lastStatus.Message with a check against a
structured field on the status (for example lastStatus.Code == "MISSING_ON_SITE"
or lastStatus.IsMissingOnSite == true), update any status creation logic to set
that structured field if it doesn't exist, and log the structured field (not the
free text) when rejecting the delete; keep references to lastStatus.Message,
IsMissingOnSite and StatusDetail.Message when updating conditions so other
callers remain consistent.
- Around line 941-1016: The transaction currently only acquires an advisory lock
on the old InstanceType (see TryAcquireAdvisoryLock using emit.InstanceTypeID)
but not on the Machine, allowing concurrent PATCHes to race; fix by acquiring an
advisory lock for the Machine at the start of the cdb.WithTx callback (use
machine.ID as lock key via cdb.GetAdvisoryLockIDFromString(machine.ID.String())
or equivalent) before calling mitDAO.GetAll/mitDAO.DeleteByID/CreateFromParams
and mDAO.Update/Clear so the read-delete-create/update sequence on machine.ID is
serialized per machine; ensure the lock acquisition error is handled similarly
to the existing lock error handling.
🪄 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: a071236b-c0dd-4b61-b9ac-5027e1014247

📥 Commits

Reviewing files that changed from the base of the PR and between b330622 and bf2c2a2.

📒 Files selected for processing (1)
  • api/pkg/api/handler/machine.go

Comment thread api/pkg/api/handler/machine.go
Comment thread api/pkg/api/handler/machine.go
Comment thread api/pkg/api/handler/machine.go Outdated
@chet chet changed the title refactor: Migrate Machine API handlers to use WithTx transaction helper refactor: Migrate Machine API handlers to use WithTx May 6, 2026
@chet chet force-pushed the with-tx-machine branch from bf2c2a2 to 79e426f Compare May 6, 2026 22:25
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
api/pkg/api/handler/machine.go (1)

1582-1582: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Preserve contextual logger from SetupHandler.

The reassignment at line 1582 uses the global log.With() instead of extending the contextual logger returned by common.SetupHandler. This discards request-scoped context (trace IDs, org, user, etc.) that is valuable for correlating logs during incident investigation.

Suggested fix
-	logger = log.With().Str("Machine", mID).Logger()
+	logger = logger.With().Str("Machine", mID).Logger()
🤖 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 `@api/pkg/api/handler/machine.go` at line 1582, The current reassignment uses
the global log.With() and overwrites the request-scoped logger from
common.SetupHandler, dropping trace/org/user context; change the code that sets
logger (currently "logger = log.With().Str(\"Machine\", mID).Logger()") to
extend the existing request-scoped logger returned by SetupHandler (e.g., use
logger = logger.With().Str("Machine", mID).Logger()) so the new logger preserves
the original context like trace IDs, org, and user.
🤖 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.

Outside diff comments:
In `@api/pkg/api/handler/machine.go`:
- Line 1582: The current reassignment uses the global log.With() and overwrites
the request-scoped logger from common.SetupHandler, dropping trace/org/user
context; change the code that sets logger (currently "logger =
log.With().Str(\"Machine\", mID).Logger()") to extend the existing
request-scoped logger returned by SetupHandler (e.g., use logger =
logger.With().Str("Machine", mID).Logger()) so the new logger preserves the
original context like trace IDs, org, and user.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: a94a4ce4-7fc9-43a1-b8ca-62b665564cad

📥 Commits

Reviewing files that changed from the base of the PR and between bf2c2a2 and 79e426f.

📒 Files selected for processing (1)
  • api/pkg/api/handler/machine.go

@chet chet force-pushed the with-tx-machine branch 3 times, most recently from d15f8e4 to 375ecda Compare May 7, 2026 06:30
Apply `WithTx` (from NVIDIA#462) to all four API handlers in `machine`, including using the `TerminateWorkflowOnTimeOut` helper.

This also introduces a `MachineStatusMissing` enum to improve how we handle errors for missing machines (which came as a recommendation from @coderabbitai).

@coderabbitai also gave some feedback, which has also been addressed, where this:
- Makes sure to use the contextual `logger` instead of the global `log` in the `maintenance-mode` workflow error path.
- Passes `tx` to `common.GetInfrastructureProviderForOrg` so the read participates in the same transaction as the surrounding writes.

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
@chet chet force-pushed the with-tx-machine branch from 375ecda to d6c1f4a Compare May 7, 2026 16:53
@chet chet changed the title refactor: Migrate Machine API handlers to use WithTx refactor: Migrate Machine API handlers to WithTx helper May 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant