You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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实例,事务逻辑不会生效,所有写入操作会直接持久化,不会触发回滚。

解决方法:

  • 本地开发搭建单节点副本集:
    1. 启动MongoDB时添加副本集参数:mongod --replSet rs0 --port 27017
    2. 连接MongoDB后执行初始化命令:rs.initiate()
  • 修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 06:20:27