Skip to content

[payments][P0 CRITICAL] Production checkout cannot take a payment: the DPDP consent check runs on the global Prisma client inside the checkout transaction, which deadlocks under PG_POOL_MAX=1 #1436

Description

@teetangh

Found 2026-09-04 by the finance-train verification on the Netlify deploy preview of #1422 and confirmed pre-existing on dev (and therefore on production, which runs the same PG_POOL_MAX=1).

Symptom. POST /api/checkout for a plain Razorpay consultation or subscription answers HTTP 500 timeout exceeded when trying to connect every time, while sibling database routes on the same deploy answer in 1 to 5 seconds. No partial rows are written.

Cause. validateSlotAvailability (lib/payments/operations/checkout.ts ~L857) calls checkConsent, and checkConsent reads on the GLOBAL Prisma client (lib/compliance/dpdp.ts:194). validateSlotAvailability only ever runs inside a transaction (calculateAmountAndValidate at ~L500, revalidateInsideLock at ~L1414, handleConsultationCheckout at ~L2861). With a pool of one connection (lib/prisma.ts:52,65) the open transaction holds the only client, the nested read waits for a connection only that transaction can release, and pg gives up at the 3 s connect timeout. A second instance sits at ~L1528 (org-sponsored consent inside revalidateInsideLock). Both arrived with commit 20fef46 (Wave 7 consent cascade, #1255). The train's local E2E passed only because a dev server runs poolMax=10.

Fix. PR #1435: checkConsent accepts an optional client defaulting to the global one, and both in-transaction sites pass tx. The gate still fails closed; the pool is not widened. A jest pin models the single-connection pool: the global client throws the production error text whenever it is touched while $transaction is open (red on the old code, green after).

Also on the checkout path (not fatal, same family). Three fire-and-forget global-client lookups run from inside the Serializable transaction and lose their Novu notification silently under poolMax=1: checkout.ts ~L3190 (program cap hit), ~L3287 (80% cap bell), lib/payments/billing/overage-settlement.ts ~L245 (overage due). They should move to the post-commit pattern invoice-rollup.ts uses.

Owner actions. Merge #1435 ahead of the train and cut a dev → prod release; production has not been able to take a consultation payment since #1255 merged.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MgadJydWEKkdhhzY58yiL7

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingcriticalCritical priority itemsfinancePayments, refunds, earnings, payouts, invoicing, ledgerlaunch: pre-mvpGates launch — money, data, or a failure we would not detectpriority: criticalproductionProduction deployment and readiness

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions