Demo, all content is generated
Question

Posted my app on a subreddit and got 'Max client connections reached' within 10 minutes

Solved · 723 views · asked by jonah_b · edited

Small launch, maybe 300 people at the same time. The site started throwing 500s and the Vercel logs show:

error: Max client connections reached

I'm on the Supabase free plan. I use Prisma in my Next.js route handlers, and new PrismaClient() is at the top of each route file. Is the free plan just not able to handle 300 users? I was hoping to not pay yet.

What I’ve tried

Upgraded Vercel to Pro thinking it was Vercel. Didn't help. Restarted the database.

Comment
Which connection string, 5432 or 6543? And is it the pooler host (pooler.supabase.com) or db.<ref>.supabase.co? deploydan · edited
pooler host, port 5432 jonah_b · edited
Bookmarking. I have exactly the new PrismaClient() per route thing. sophie_l · edited

3 answers

Marked as helpful by the asker
jonas_k · edited

Two problems stacking up.

  1. Port 5432 on the pooler host is session mode: each client holds a database connection for as long as it's connected. Serverless functions spin up many instances, each holds one or more, and you hit the pooler's client limit fast. Switch the runtime URL to transaction mode (port 6543) and add ?pgbouncer=true&connection_limit=1 for Prisma.
  2. new PrismaClient() in every route file means every route module has its own pool. Make one shared instance:
// lib/db.ts
import { PrismaClient } from '@prisma/client'
const g = globalThis as unknown as { prisma?: PrismaClient }
export const prisma = g.prisma ?? new PrismaClient()
if (process.env.NODE_ENV !== 'production') g.prisma = prisma

Keep directUrl on the session/direct connection for prisma migrate.

300 concurrent visitors is well within what a free project can serve once connections are pooled. The limit you hit was config, not size.

Comment
Did both. Ran a quick load test with 400 virtual users, no errors. Downgrading Vercel again lol jonah_b · edited
deploydan · edited

Also check if you're making queries that don't need to be there. Our landing page was hitting the DB on every request for a count that changed once a day. export const revalidate = 3600 on that page removed a big chunk of load.

Comment
The stats card on my homepage, exactly that. Cached now. jonah_b · edited
arjun_codes · edited

For next time: Reports > Database in the dashboard shows pooler client connections over time. Keep it open during a load test and you see the ceiling coming before your users do.

Comment