NestJS中Mongoose从connection启动事务session报无已启动事务如何解决
报错原因
1. MongoError: No transaction started 100%报错原因
你通过connection.startSession()仅创建了会话实例,没有主动调用startTransaction()方法启动事务,直接使用会话执行操作时,MongoDB会判定当前没有正在运行的事务,直接抛出错误。
同时你使用的Mongoose 5.11.18版本存在已知的会话上下文绑定bug,创建的会话默认没有关联到当前Mongoose连接的事务上下文,即便启动事务也可能触发该报错。
2. 间歇性事务编号不匹配报错原因
你直接从原生MongoClient启动会话,绕开了Mongoose的会话管理逻辑,Mongoose内部维护的事务序号和原生会话的事务序号无法同步,当出现连接复用、会话复用、并发操作时,就会出现序号不匹配的问题。
此外如果会话在事务提交/回滚后没有调用endSession()销毁,重复使用同一个会话执行多次事务,也会触发该报错。
解决方案
步骤1:修正会话创建逻辑
确保创建会话后主动启动事务,优先使用Mongoose封装的会话创建方法:
import { Injectable } from '@nestjs/common'; import { InjectConnection } from '@nestjs/mongoose'; import { Connection, ClientSession } from 'mongoose'; @Injectable() export class YourService { constructor(@InjectConnection() private readonly connection: Connection) {} public async getTransactionSession(): Promise<ClientSession> { const session = await this.connection.startSession(); // 必须主动启动事务 session.startTransaction({ readConcern: { level: 'majority' }, writeConcern: { w: 'majority' }, }); return session; } }
步骤2:规范会话使用流程
每个事务使用独立会话,用完必须销毁,禁止跨事务/跨请求复用会话:
async executeTransactionDemo() { const session = await this.getTransactionSession(); try { // 所有参与事务的操作都必须传入session参数 await this.userModel.insertMany([{ username: 'test' }], { session }); await this.orderModel.insertMany([{ userId: 'xxx', amount: 100 }], { session }); // 提交事务 await session.commitTransaction(); } catch (e) { // 异常回滚 await session.abortTransaction(); throw e; } finally { // 必须结束会话,释放资源,避免复用导致序号冲突 session.endSession(); } }
步骤3:版本兼容修复
当前使用的依赖版本存在已知事务bug,建议升级到以下稳定版本:
"mongoose": "^5.13.20", "@nestjs/mongoose": "^7.2.4",
升级后可以解决大部分会话上下文绑定、事务序号不匹配的原生bug。
额外注意事项
- 确保你的MongoDB部署为副本集或分片集群,单机模式不支持事务
- 所有参与事务的操作都必须显式传入session参数,否则不会被纳入当前事务
内容的提问来源于stack exchange,提问作者SebastianG
相关产品推荐
相关产品推荐

