Mongoose事务未按预期工作:测试回滚失败问题排查
Mongoose事务未回滚,Account文档被持久化的原因及解决方法
问题场景
我编写了一段创建Mongoose事务的代码,测试时在事务完成前主动抛出错误,预期会触发事务回滚,但Account文档始终被持久化,未执行回滚操作。相关代码如下:
注册接口代码
export const signUp = catchAsync(async (req: Request, res: Response, next: NextFunction) => { const createdAt = req.body.createdAt ? new Date(req.body.createdAt) : new Date() const session = await mongoose.startSession() try { session.startTransaction() const account: IAccount = await Account.create<ICreateAccountInput>( [ { createdAt, accountName: req.body.accountName, accountOwnerName: req.body.accountOwnerName, accountOwnerEmail: req.body?.accountOwnerEmail, contactName: req.body?.contactName || undefined, contactNumber: req.body.contactNumber || undefined, contactEmail: req.body.contactEmail || undefined, }, ], { session }, )[0] // throw error to test transaction cancellation throw new Error('Test error') const accountAdmin: IUser = await User.create<ICreateUserInput>( [ { createdAt, accountId: account._id, username: req.body.accountOwnerName, email: req.body.accountOwnerEmail, role: UserRoles.AccountAdmin, password: req.body.password, passwordConfirm: req.body.passwordConfirm, }, ], { session }, )[0] await session.commitTransaction() createSendToken(accountAdmin, 201, res, account) } catch (e) { session.abortTransaction() throw e } finally { session.endSession() } })
catchAsync工具函数
export const catchAsync = fn => { return (req, res, next) => { fn(req, res, next).catch(next) } }
核心原因及解决方案
1. MongoDB环境不支持多文档事务
Mongoose的多文档事务依赖MongoDB官方事务功能,该功能仅在副本集(Replica Set)或分片集群(Sharded Cluster)环境中可用。如果使用的是单节点MongoDB实例,事务逻辑不会生效,所有写入操作会直接持久化,不会触发回滚。
解决方法:
- 本地开发搭建单节点副本集:
- 启动MongoDB时添加副本集参数:
mongod --replSet rs0 --port 27017 - 连接MongoDB后执行初始化命令:
rs.initiate()
- 启动MongoDB时添加副本集参数:
- 修改Mongoose连接字符串指向副本集:
mongoose.connect('mongodb://localhost:27017/your-db-name?replicaSet=rs0')
2. 事务回滚逻辑的异步处理问题
当前代码中session.abortTransaction()未使用await,而abortTransaction是异步方法,会导致回滚操作未完成就抛出错误,可能出现事务状态异常。
解决方法:
在catch块中添加await等待回滚完成,同时捕获回滚本身的错误:
catch (e) { try { await session.abortTransaction(); } catch (abortErr) { console.error('事务回滚失败:', abortErr); } throw e; }
3. 验证会话关联的正确性
可以在Account.create后添加日志,确认文档是否正确关联了事务会话:
const account: IAccount = await Account.create<ICreateAccountInput>(/* ... */)[0]; console.log(account.$session()); // 应返回当前创建的session对象,若为null则说明会话未关联成功
内容的提问来源于stack exchange,提问作者Jo Momma
相关产品推荐
相关产品推荐

