Skip to content

Commit 9a199c3

Browse files
icecrasher321claude
andcommitted
docs(deploy): name the tripwire as the mechanism, not pool starvation
The previous commit attributed the 500 to pool starvation under concurrency. That was wrong. `packages/db/tx-tripwire.ts` marks the async context for the duration of a transaction callback and reports any query issued on the global pool inside it — throwing outside production, warning in production. So the failure was deterministic in dev, not load-dependent, which is why it reported against the authz lookup rather than anything the caller wrote. Saturation deadlock is what the tripwire exists to PREVENT, not what happened. Recording the real mechanism, since the wrong one would send the next reader looking for a concurrency bug. Also drops a comment claiming a transaction connection "cannot serve concurrent statements". `loadWorkflowDeploymentSnapshot` issues two tx-handle reads under `Promise.all` and has always worked, so the claim is false; the sequential reads stay because they are clearer, not because concurrency is unsafe. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent f3c669a commit 9a199c3

2 files changed

Lines changed: 14 additions & 15 deletions

File tree

apps/sim/lib/workflows/deployment-status.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,15 @@ export async function checkNeedsRedeployment(workflowId: string): Promise<boolea
2525
await tx.execute(sql`SET TRANSACTION ISOLATION LEVEL REPEATABLE READ`)
2626
/*
2727
* `workspaceId` is selected here, in this transaction, rather than left for
28-
* `materializeDeploymentState` to look up. It resolves an absent one through
29-
* `getActiveWorkflowContext`, which runs on the global pool — a second
30-
* connection checkout while this transaction already holds one. Under any
31-
* concurrency (this endpoint is polled, and refetches on window focus) that
32-
* starves the pool and fails the nested read, surfacing as a 500 on
33-
* `/api/workflows/[id]/deploy`. A transaction must not await a checkout.
28+
* `materializeDeploymentState` to look up: resolving an absent one goes
29+
* through `getActiveWorkflowContext`, which queries the global pool, and
30+
* this callback already holds a pooled connection.
31+
*
32+
* `packages/db/tx-tripwire.ts` detects exactly that and throws outside
33+
* production, so it did not degrade quietly — it 500'd every
34+
* `/api/workflows/[id]/deploy` in dev, reported against the authz lookup
35+
* rather than anything this function wrote. Hoisting the read into the
36+
* transaction is the tripwire's own first remedy.
3437
*/
3538
const [active] = await tx
3639
.select({
@@ -52,10 +55,6 @@ export async function checkNeedsRedeployment(workflowId: string): Promise<boolea
5255
/* The inner join guarantees a workspace row; a null id means unusable data. */
5356
if (!active?.state || !active.workspaceId) return false
5457

55-
/*
56-
* Sequential, not `Promise.all`: both reads share the transaction's single
57-
* connection, which cannot serve concurrent statements.
58-
*/
5958
const currentState = await loadWorkflowDeploymentSnapshot(workflowId, tx)
6059
if (!currentState) return false
6160

apps/sim/lib/workflows/persistence/utils.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -175,11 +175,11 @@ interface DeploymentStateRow {
175175
*/
176176
/**
177177
* `workspaceId` is required rather than resolved here on purpose. Resolving it
178-
* means `getActiveWorkflowContext`, which runs on the global pooland this
179-
* function is called from inside a REPEATABLE READ transaction by
180-
* `checkNeedsRedeployment`, where a second connection checkout while holding one
181-
* starves the pool under concurrency and fails the nested read. Taking it as an
182-
* argument makes that impossible instead of merely avoided.
178+
* means `getActiveWorkflowContext`, which queries the global pool, and
179+
* `checkNeedsRedeployment` calls this from inside a REPEATABLE READ transaction
180+
* that already holds a pooled connection — the nested checkout
181+
* `packages/db/tx-tripwire.ts` exists to catch. Taking the id as an argument
182+
* makes the violation unrepresentable rather than merely avoided.
183183
*/
184184
export async function materializeDeploymentState(
185185
workflowId: string,

0 commit comments

Comments
 (0)