使用Promise.all并发执行超12个Prisma事务时报错排查
Prisma事务并发调用超时:中间件误用主客户端导致的问题
问题场景
我有一个使用Prisma $transaction的API函数,用Vitest做并发调用测试时,当并发数超过12次就会触发超时错误,12次以内则正常。
函数代码
const verifyEmail = (data) => { return prisma.$transaction(async tx => { // 读取和更新操作 }) }
测试代码
test("run verifyEmail multiple times", async () => { await Promise.all(myCredentials.map(data => verifyEmail(data))) })
报错信息
Transaction API error: Unable to start a transaction in the given time.
调整超时时间和maxWait参数无法解决问题,排查后怀疑是Prisma中间件内的查询误用了主Prisma客户端而非事务上下文的客户端。
问题原因
Prisma的事务依赖专属的连接上下文(即$transaction回调中的tx实例),如果中间件里直接使用全局的prisma客户端执行查询,会绕过事务上下文,创建新的独立数据库连接。当并发请求数超过数据库连接池的默认上限(通常10-15)时,连接池耗尽,新的事务无法获取连接,最终触发超时错误。
解决方案
修改Prisma中间件,确保所有数据库操作都使用当前上下文的客户端(事务场景下自动复用tx实例),而非全局的prisma客户端:
错误的中间件示例
// 错误:直接使用全局prisma客户端,事务中会创建新连接 prisma.$use(async (params, next) => { await prisma.log.create({ data: { action: params.action } }) return next(params) })
修正后的中间件示例
// 正确:使用当前上下文的客户端,事务内自动使用tx实例 prisma.$use(async (params, next) => { // params.prisma 会指向当前操作的客户端(事务场景下是tx) const client = params.prisma || prisma await client.log.create({ data: { action: params.action } }) return next(params) })
额外注意事项
- 检查所有中间件中的数据库操作,确保统一使用
params.prisma获取客户端实例,避免硬编码全局prisma。 - 若需要更高并发,可以适当调整数据库连接池配置(如PostgreSQL的
max_connections、Prisma的connection_limit),但优先解决中间件的客户端误用问题,这才是根本原因。
内容的提问来源于stack exchange,提问作者Arash Jahan Bakhshan
相关产品推荐
相关产品推荐

