Skip to content

Prisma Postgres pooler stalls a new client’s first query after ~15 minutes idle #30275

Description

@davidtheladder

Package and version

prisma@6.19.2 @prisma/client@6.19.2

What happened?

Prisma Postgres pooler (pooled.db.prisma.io, Vercel Marketplace) stalls a new client's first statement when it hands out a server connection that sat idle ~15 minutes. The query never returns. Prisma has no bound on that wait (socket_timeout does not cover prepare_typed), so the request hangs until the host kills it.

We hit this on a Vercel cron (GET /api/cron, every 15 minutes, maxDuration = 300). After switching production DB_URL from the direct host (db.prisma.io) to the pooled host (2026-09-08), every other cron tick timed out:

  • success ~1s → leave one server connection idle in the pooler
  • ~899s later the next client gets that connection
  • first statement (SELECT 1 / $executeRaw) never gets a reply
  • Vercel 504: "Task timed out after 300 seconds" (~40 × 504/day)
  • pooler then drops that server connection; the next tick succeeds; cycle repeats

During a hang, pg_stat_activity (via the direct host) showed our backend idle / ClientRead, pg_locks empty, no running query. The hang also happens on a brand-new serverless process with no prior Prisma connection, so it is not a stale client socket. connect_timeout never fires (TCP+TLS+auth already succeeded). Setting socket_timeout=30 did not help.

A second SELECT 1 issued while the first is still pending returns immediately on a new pooled connection.

What did you expect to happen?

The first query on a pooled connection should return or fail within connect_timeout / socket_timeout. The pooler should reset or discard a server connection that has been idle ~15 minutes instead of handing it to a new client in a half-dead state. socket_timeout should also cover the prepare path (prepare_typed), not only execute.

Minimal reproduction

Cannot share the production DB. Repro is timing + the Prisma Postgres pooler, not schema:

  1. Prisma Postgres via Vercel Marketplace. App uses the pooled URL (pooled.db.prisma.io) with connection_limit=5&pool_timeout=10. Do not add pgbouncer=true (Prisma Postgres already tracks prepared statements).

  2. Prisma ORM 6.19, Next.js on Vercel, a */15 * * * * cron whose first statement is:

    await prisma.$executeRawSELECT 1;
    // then any cheap reads

  3. Let a run succeed (opens a server connection, then the instance goes idle).

  4. Wait ~15 minutes with no other traffic on that pooled connection.

  5. Hit the cron again.

Expected: SELECT 1 returns in <1s.
Actual: it never returns; function 504s at 300s. Next tick after the kill succeeds.

Workaround that recovered every stalled tick: race the operation against 8s and re-issue the read on a new pooled connection. Writes are not retried.

Environment

  • Node: 24.x on Vercel (local 24.13.1)
  • OS: Vercel Fluid / iad1 (local macOS 15)
  • Package manager: npm
  • Database: Prisma Postgres (Vercel Marketplace), pooled host pooled.db.prisma.io, direct host db.prisma.io
  • Prisma ORM: 6.19.2 (library engine)
  • Framework: Next.js 16.1 on Vercel
  • Cron: GET /api/cron every 15 minutes

Additional context

Example recovered tick after our workaround (same stall, then re-issue):

  • 2026-09-13 12:00:33 UTC
  • deployment dpl_AMCMLbxjapx4phrsxBPLWv4tyEtj
  • log: {"evt":"prisma.stall","model":null,"operation":"$queryRaw","attempt":1,"reissue":true,"stallMs":8000}
  • then HTTP 200 in 8.6s (was 504 / 300s before the workaround)

Questions:

  1. What is the Prisma Postgres pooler’s idle / reset behaviour around ~900s?
  2. Can socket_timeout cover prepare_typed?
  3. Can the pooler reset a ~15-min-idle server connection instead of handing it out half-dead?

Happy to share more Vercel request IDs. Please do not ask for the connection string.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions