NodeJS + Prisma执行长任务频繁断连?(ConnectorError)
解决Prisma+PlanetScale大插入超时及后台任务执行问题
一、让服务器后台执行任务,无需前端等待
直接在Express接口中先返回响应,再异步执行任务,避免前端因长时间等待导致连接断开:
app.post('/', async (req, res) => { // 立即返回202状态码,告知任务已接收 res.status(202).json({ message: '任务已启动,将在后台执行' }); // 后台异步执行插入逻辑,不阻塞响应返回 (async () => { try { const { items } = req.body; // 转换数据格式 let prismaItems = await Promise.all(items.map(async (item) => { let bla = await Promise.all(item.bla.map(async innerItem => { // ...你的内部数据转换逻辑 })); return { /* 构造sentence对应的数据结构 */ }; })); // 执行插入(后续需优化长事务问题) await prisma.megaItem.create({ data: { title: "My title", sentences: { create: [...prismaItems] } } }); console.log('插入任务完成'); } catch (err) { console.error('插入任务失败:', err); } })(); });
如果需要更可靠的任务管理(支持失败重试、状态监控),推荐使用任务队列(如BullMQ):
- 前端请求时将任务加入队列,立即返回响应
- 单独启动Worker进程后台处理队列任务
示例代码:
// 队列定义(queue.js) const { Queue } = require('bullmq'); const insertQueue = new Queue('megaInsert'); // 接口处理逻辑 app.post('/', async (req, res) => { await insertQueue.add('batchInsert', { items: req.body.items }); res.status(202).json({ message: '任务已加入队列,后台处理中' }); }); // Worker进程(worker.js) const { Worker } = require('bullmq'); const prisma = require('./prisma'); const worker = new Worker('megaInsert', async (job) => { const { items } = job.data; // 数据转换逻辑 let prismaItems = await Promise.all(items.map(async (item) => { // ...转换代码 })); // 执行分批插入(见下文优化方案) const megaItem = await prisma.megaItem.create({ data: { title: "My title" } }); const batchSize = 50; for (let i = 0; i < prismaItems.length; i += batchSize) { const batch = prismaItems.slice(i, i + batchSize); await prisma.sentence.createMany({ data: batch.map(s => ({ megaItemId: megaItem.id, ...s })) }); } }); worker.on('completed', (job) => console.log(`任务 ${job.id} 完成`)); worker.on('failed', (job, err) => console.error(`任务 ${job.id} 失败: ${err}`));
二、解决PlanetScale事务超时问题
你的报错是PlanetScale的tx killer机制触发的——它会自动终止运行超过10秒的事务。当前嵌套create的方式会把所有插入操作放在一个事务中,数据量大时必然超时,优化方案如下:
1. 拆分事务,分批插入
先创建megaItem主记录,再用createMany分批插入关联的sentences子记录,每个批次的事务时间短,不会触发超时:
// 先创建主记录 const megaItem = await prisma.megaItem.create({ data: { title: "My title" } }); // 分批次插入子记录 const batchSize = 50; // 根据数据大小调整,建议50-100条/批 const total = prismaItems.length; for (let i = 0; i < total; i += batchSize) { const batch = prismaItems.slice(i, i + batchSize); await prisma.sentence.createMany({ data: batch.map(sentence => ({ megaItemId: megaItem.id, // 填充sentence的其他字段 ...sentence })) }); }
2. 禁用Prisma自动事务(不推荐)
如果必须使用嵌套创建,可尝试禁用Prisma的自动事务管理,但会失去数据一致性保障:
await prisma.megaItem.create({ data: { /* ... */ }, transaction: { enabled: false } });
关键注意事项
- 独立NodeJS服务器需用PM2等进程管理工具托管,避免进程意外退出中断任务
- 批量插入的
batchSize需根据单条数据大小测试调整,找到最优值 - 任务队列方案更适合长时间运行的任务,支持重试、监控和持久化
内容的提问来源于stack exchange,提问作者antonwilhelm
相关产品推荐
相关产品推荐

