Skip to content

fix: crud operations on var sets trigger reconciliations#912

Merged
adityachoudhari26 merged 2 commits intomainfrom
var-set-changes-trigger-reconcile
Apr 6, 2026
Merged

fix: crud operations on var sets trigger reconciliations#912
adityachoudhari26 merged 2 commits intomainfrom
var-set-changes-trigger-reconcile

Conversation

@adityachoudhari26
Copy link
Copy Markdown
Member

@adityachoudhari26 adityachoudhari26 commented Apr 6, 2026

Resolves #911

Summary by CodeRabbit

  • Bug Fixes
    • Variable set create/update/delete now reliably trigger synchronization of related release targets.
    • Synchronization is initiated after changes are fully persisted and before the API responds, ensuring external systems see up-to-date versions.

Copilot AI review requested due to automatic review settings April 6, 2026 20:24
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 6, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 82f2ee93-480e-45f4-9dc8-b2cb767d6df6

📥 Commits

Reviewing files that changed from the base of the PR and between 4d74b7c and 348fbc0.

📒 Files selected for processing (1)
  • apps/api/src/routes/v1/workspaces/variable-sets.ts
✅ Files skipped from review due to trivial changes (1)
  • apps/api/src/routes/v1/workspaces/variable-sets.ts

📝 Walkthrough

Walkthrough

Added calls to enqueueAllReleaseTargetsDesiredVersion(db, workspaceId) after variable-set mutations in the variable sets route: the create handler enqueues after the DB transaction commits, the update handler enqueues after confirming the variable set exists (post-transaction), and the delete handler enqueues after confirming deletion succeeded, prior to returning responses.

Changes

Cohort / File(s) Summary
Variable Sets Route Handlers
apps/api/src/routes/v1/workspaces/variable-sets.ts
Inserted reconciliation enqueue calls (enqueueAllReleaseTargetsDesiredVersion(db, workspaceId)) following create, update, and delete variable-set operations. Enqueue timing adjusted to occur after transaction commit/confirmation of mutation success.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 I nudge the queue when sets are changed,
Hopping through commits to keep things arranged.
A little enqueue, a gentle reconcile,
Workspaces hum and release targets smile. ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: adding reconciliation triggers to CRUD operations on variable sets.
Linked Issues check ✅ Passed The changes implement the required fix for issue #911 by enqueuing reconciliations in create, update, and delete handlers for variable sets.
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing the reconciliation trigger requirement; no unrelated modifications are present.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch var-set-changes-trigger-reconcile

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR ensures that variable set CRUD operations in the API trigger reconciliation by enqueueing desired-version work for all release targets in the workspace.

Changes:

  • Add enqueueAllReleaseTargetsDesiredVersion calls after variable set create and delete.
  • Add enqueueAllReleaseTargetsDesiredVersion call during variable set update.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +166 to +170
}
}

enqueueAllReleaseTargetsDesiredVersion(tx, workspaceId);

Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

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

enqueueAllReleaseTargetsDesiredVersion is async, but this call is not awaited and is passed the transaction handle (tx). Because the transaction callback can resolve before the reconciler finishes its first awaited query, the reconciler may try to use a closed transaction, causing intermittent failures/unhandled rejections. Move this enqueue call outside the transaction (using db), or await it inside the transaction if it must be part of the same atomic unit.

Copilot uses AI. Check for mistakes.
return vs;
});

enqueueAllReleaseTargetsDesiredVersion(db, workspaceId);
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

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

enqueueAllReleaseTargetsDesiredVersion returns a Promise, but it's invoked without await/error handling. If enqueueing fails (DB error, deadlock, etc.), this becomes an unhandled rejection and the reconciler work may silently not be scheduled. If this is intended to be fire-and-forget, consider explicitly discarding the promise and handling errors (e.g., void ... .catch(...)); otherwise await it so failures surface predictably.

Suggested change
enqueueAllReleaseTargetsDesiredVersion(db, workspaceId);
void enqueueAllReleaseTargetsDesiredVersion(db, workspaceId).catch((error) => {
console.error(
"Failed to enqueue release targets desired version reconciliation",
{ workspaceId, error },
);
});

Copilot uses AI. Check for mistakes.
Comment on lines 192 to 198
.then(takeFirstOrNull);

if (deleted == null) throw new NotFoundError("Variable set not found");

enqueueAllReleaseTargetsDesiredVersion(db, workspaceId);

res.status(202).json(deleted);
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

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

enqueueAllReleaseTargetsDesiredVersion is async but is called without await/error handling. If enqueueing fails, this can result in an unhandled rejection and reconciliations not being scheduled. Either await it (and decide what response behavior you want on failure) or explicitly fire-and-forget with error capture (e.g., void ... .catch(...)).

Copilot uses AI. Check for mistakes.
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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/api/src/routes/v1/workspaces/variable-sets.ts`:
- Line 169: The enqueueAllReleaseTargetsDesiredVersion call is being invoked
with the transaction handle (tx) inside the transaction callback which can close
before the fire-and-forget enqueue completes; move the call out of the
transaction and invoke enqueueAllReleaseTargetsDesiredVersion(db, workspaceId)
after the transaction completes (i.e., after the commit/transaction block ends)
so it uses the global db handle like the create/delete paths, and chain a
.catch(err => logger.warn(...)) to log any enqueue failures following the
existing fire-and-forget pattern.
- Line 91: The three calls to enqueueAllReleaseTargetsDesiredVersion are
currently fire-and-forget and can produce unhandled rejections; import logger
from "@ctrlplane/logger" and wrap each call as an explicit fire-and-forget: void
enqueueAllReleaseTargetsDesiredVersion(...).catch(err =>
logger.warn("enqueueAllReleaseTargetsDesiredVersion failed", { err, workspaceId
})) for the calls at the top-level (lines ~91 and ~196), and for the call inside
the db.transaction callback (around line ~169 / inside db.transaction(tx => {
... })), either await the enqueue before returning from the transaction or wrap
it the same way with void ... .catch to ensure errors are logged if the queue
write fails after the transaction commits.
🪄 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: Pro

Run ID: 073765bb-6a25-4595-be8d-8370cb3e120f

📥 Commits

Reviewing files that changed from the base of the PR and between 34dc2f3 and 4d74b7c.

📒 Files selected for processing (1)
  • apps/api/src/routes/v1/workspaces/variable-sets.ts

return vs;
});

enqueueAllReleaseTargetsDesiredVersion(db, workspaceId);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n apps/api/src/routes/v1/workspaces/variable-sets.ts | head -220

Repository: ctrlplanedev/ctrlplane

Length of output: 7287


🏁 Script executed:

rg -A 5 -B 5 "logger\." apps/api/src/routes/v1/workspaces/variable-sets.ts

Repository: ctrlplanedev/ctrlplane

Length of output: 48


🏁 Script executed:

rg "logger" apps/api/src/routes/ -t ts --max-count 5 | head -20

Repository: ctrlplanedev/ctrlplane

Length of output: 753


🏁 Script executed:

grep -r "import.*logger" apps/api/src/routes/tfe/ --include="*.ts" | head -5

Repository: ctrlplanedev/ctrlplane

Length of output: 230


🏁 Script executed:

sed -n '110,120p' apps/api/src/routes/tfe/run_notification.ts

Repository: ctrlplanedev/ctrlplane

Length of output: 314


🏁 Script executed:

sed -n '1,30p' apps/api/src/routes/tfe/run_notification.ts

Repository: ctrlplanedev/ctrlplane

Length of output: 1281


🏁 Script executed:

sed -n '100,125p' apps/api/src/routes/tfe/run_notification.ts

Repository: ctrlplanedev/ctrlplane

Length of output: 573


🏁 Script executed:

rg "void enqueueAllReleaseTargetsDesiredVersion" apps/api/src/routes/ -A 1

Repository: ctrlplanedev/ctrlplane

Length of output: 48


🏁 Script executed:

rg "void.*catch" apps/api/src/routes/ -B 1 -A 1 | head -30

Repository: ctrlplanedev/ctrlplane

Length of output: 48


🏁 Script executed:

rg "enqueueAllReleaseTargetsDesiredVersion" apps/api/src/routes/v1/workspaces/variable-sets.ts -B 2 -A 2

Repository: ctrlplanedev/ctrlplane

Length of output: 626


🏁 Script executed:

sed -n '138,176p' apps/api/src/routes/v1/workspaces/variable-sets.ts

Repository: ctrlplanedev/ctrlplane

Length of output: 1054


🏁 Script executed:

rg -B 5 -A 5 "if.*workspaceId.*null" apps/api/src/routes/tfe/run_notification.ts

Repository: ctrlplanedev/ctrlplane

Length of output: 314


Add explicit fire-and-forget handling with error logging for all three enqueue calls.

The floating promises at lines 91, 169, and 196 can cause unhandled rejections if the queue write fails after the response is sent. These should use the pattern void enqueueAllReleaseTargetsDesiredVersion(...).catch(err => logger.warn(...)).

Line 169 requires special attention: this call sits inside the db.transaction() callback and uses tx, so the enqueue operation may complete (or fail) after the transaction itself commits. Either await it or wrap it with the explicit void/catch pattern.

You'll also need to import logger: import { logger } from "@ctrlplane/logger";

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

In `@apps/api/src/routes/v1/workspaces/variable-sets.ts` at line 91, The three
calls to enqueueAllReleaseTargetsDesiredVersion are currently fire-and-forget
and can produce unhandled rejections; import logger from "@ctrlplane/logger" and
wrap each call as an explicit fire-and-forget: void
enqueueAllReleaseTargetsDesiredVersion(...).catch(err =>
logger.warn("enqueueAllReleaseTargetsDesiredVersion failed", { err, workspaceId
})) for the calls at the top-level (lines ~91 and ~196), and for the call inside
the db.transaction callback (around line ~169 / inside db.transaction(tx => {
... })), either await the enqueue before returning from the transaction or wrap
it the same way with void ... .catch to ensure errors are logged if the queue
write fails after the transaction commits.

@adityachoudhari26 adityachoudhari26 merged commit a1b1835 into main Apr 6, 2026
8 checks passed
@adityachoudhari26 adityachoudhari26 deleted the var-set-changes-trigger-reconcile branch April 6, 2026 21:17
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.

bug: global variable set CRUD operations do not enqueue release target reconciliations

2 participants