When the Vercel integration sets DATABASE_URL
for new preview environments, it does not include the connect_timeout
option in the URL.
This can result in connect errors when the database branch is idle and needs to spin up.
The errors are especially annoying when Vercel builds fail because we run prisma migrate deploy
as a build step, and then the whole thing fails if the migrate task gets a database connection error. This advice indicates that a connection timeout should solve the problem.
Hey @josh3736,
So the workaround is to pass the query params when creating a new Prisma Client instance.
If you’re not using connection pooling:
const globalForPrisma = global as unknown as { prisma: PrismaClient };
export const prisma =
globalForPrisma.prisma ||
new PrismaClient({
log: ['query'],
datasources: {
db: {
url: `${process.env.DATABASE_URL}?connect_timeout=10&pool_timeout=10`,
},
},
});
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;
If you’re using connection pooling:
const globalForPrisma = global as unknown as { prisma: PrismaClient };
export const prisma =
globalForPrisma.prisma ||
new PrismaClient({
log: ['query'],
datasources: {
db: {
url: `${process.env.DATABASE_URL}?pgbouncer=true&connect_timeout=10&pool_timeout=10`,
},
},
});
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma;
Let me know if you have any other questions
Thanks for the tip. I’m not in love with overriding the schema file from code, but the workaround should be fine.
I rewrote this to be a little more robust, and only set the timeout options if they aren’t already specified in the env var:
const dbUrl = url.parse(process.env.DATABASE_URL, true);
dbUrl.search = null; // must be null so url.format() uses the `query` object
// When neon creates a new DB branch, the DATABASE_URL it sets does not include any timeout options.
// However, the timeout options are necessary to avoid connection errors when the DB is idle and must cold-start.
// see https://community.neon.tech/t/add-connect-timeout-to-database-url-set-by-vercel-integration/864
if (!('connect_timeout' in dbUrl.query)) dbUrl.query.connect_timeout = '10';
if (!('pool_timeout' in dbUrl.query)) dbUrl.query.pool_timeout = '10';
const prisma = new PrismaClient({
datasources: {
db: {
url: url.format(dbUrl)
}
}
});
This way, any options you specify in DATABASE_URL
are honored (so you don’t need to handle connection pooling with separate code), and the timeout options are only set if they are missing.
That said, shouldn’t the integration include the timeout options in DATABASE_URL
by default when setting that var in Vercel? It seems a bit odd for the default to be a configuration likely to cause errors.
…although, actually, I’ve just realized this doesn’t fix the problem with prisma migrate deploy
.
This workaround solves errors in app code, but since the Prisma CLI creates its own connection using DATABASE_URL
directly, code like this wouldn’t have a chance to override the config var.
I guess I’ll need to write a helper that invokes prisma
(the CLI tool) with a pre-modified environment, which is super ugly.
The timeout options are Prisma-specific, and adding them by default might cause weird behaviors if you use a client other than Prisma.
We’re actively working on bringing down our cold start duration. So the timeout issue will be solved in the near future and you won’t need to set the query param
I’ve just realized this doesn’t fix the problem with prisma migrate deploy
.
Unfortunately it doesn’t
Ah, hadn’t considered that. Makes sense.
It would be nice to be able to configure the options, even if it was just the integration looking at the query string used for the prod (or whatever) env and copying it.
1 Like