fix(billing): prevent deadlock with timeout#4949
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview Inside the transaction, sets a 3s Reviewed by Cursor Bugbot for commit 7084891. Configure here. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 7084891. Configure here.
Greptile SummaryThis PR fixes a pool-starvation deadlock in
Confidence Score: 3/5The deadlock fix is sound, but every top-up flush (the majority of calls after the first) now performs a subscription lookup that is discarded unused, adding extra DB load on a high-traffic billing endpoint. Moving billing context resolution out of the transaction correctly eliminates the pool-starvation deadlock and the lock changes are technically correct. However, the unconditional pre-transaction apps/sim/lib/billing/core/usage-log.ts — the unconditional billing context resolution before the transaction. Important Files Changed
Sequence DiagramsequenceDiagram
participant Caller
participant recordCumulativeUsage
participant resolveBillingContext
participant DB_Pool
participant Transaction
Caller->>recordCumulativeUsage: "{userId, eventKey, cost, ...}"
note over recordCumulativeUsage: BEFORE (old): billingContext resolved inside tx
note over recordCumulativeUsage: AFTER (new): billingContext resolved BEFORE tx
recordCumulativeUsage->>resolveBillingContext: resolveBillingContext(userId)
resolveBillingContext->>DB_Pool: getHighestPrioritySubscription(userId)
DB_Pool-->>resolveBillingContext: subscription
resolveBillingContext-->>recordCumulativeUsage: "{billingEntity, billingPeriod}"
recordCumulativeUsage->>DB_Pool: db.transaction()
DB_Pool->>Transaction: acquire connection
Transaction->>Transaction: "SET lock_timeout = '3000ms' (tx-local)"
Transaction->>Transaction: pg_advisory_xact_lock(hashtextextended(eventKey, 0))
Transaction->>Transaction: SELECT existing row
alt existing row found (UPDATE path)
Transaction->>Transaction: UPDATE cost to newTotal
note over Transaction: billingContext resolved above — unused here
else first flush (INSERT path)
Transaction->>Transaction: INSERT with pre-resolved billingEntity/billingPeriod
end
Transaction-->>DB_Pool: release connection + advisory lock
DB_Pool-->>recordCumulativeUsage: result
recordCumulativeUsage-->>Caller: "{billed, delta, total}"
Reviews (1): Last reviewed commit: "fix(billing): prevent deadlock with time..." | Re-trigger Greptile |

Summary
Fix pool-starvation deadlock in /api/billing/update-cost by resolving billing context before the per-event-key advisory-lock transaction, and bound the lock wait with a 3s lock_timeout (on a 64-bit hashtextextended key) so stuck flushes fail fast for the Go retry/dead-letter path instead of pinning pooled connections.
Type of Change
Testing
Tested manually
Checklist