Mongoose中session.abortTransaction无法回滚事务问题求助
Mongoose事务调用abortTransaction()后未回滚操作的问题排查与解决
我刚看了你的代码,问题出在Mongoose事务的session传递方式上——你调用create方法时的参数格式错误,导致这些数据库操作根本没加入到事务上下文里,所以abortTransaction()自然不会回滚它们。
问题根源
Mongoose的Model.create()方法签名是create(docs, options),第二个参数是配置对象(用来传递session、timestamps等选项),而不是你现在写的第三个参数。你当前的写法create([...], null, session)会把null当成配置选项,session被当成了回调函数(但你用的是async/await,回调不会触发),这就导致这些创建操作完全脱离了事务session,变成了独立的非事务操作,所以即使调用abortTransaction(),这些操作也不会被回滚。
同样,你的findOne和findById调用也存在类似的参数传递问题,虽然查询操作本身不会修改数据,但为了保证事务上下文的一致性,也应该用正确的方式传递session。
修正后的代码
下面是调整了session传递方式的核心代码片段:
export const signUp = async (req, res) => { const { email, password, fullname } = req.body; // Init session const session = await mongoose.startSession(); // Begin transaction session.startTransaction(); try { // Check if user exist - 修正session传递方式 const dbUser = await UserModel.findOne({ email }, { session }); if (dbUser) return res.status(409).json({ message: "User already exists" }); // Create tenant - 修正create的参数 const tenantResult = await TenantModel.create([{ createdAt: new Date() }], { session }); // Get admin role - 修正session传递方式 const roleResult = await RoleModel.findById('626db17b8645717d657fc4c8', { session }); //Hash password const hashedPassword = await bcrypt.hash(password, 12); // Store user in db - 修正create的参数 const userResult = await UserModel.create([ { email: email, password: hashedPassword, fullname: fullname, tenant: tenantResult[0], role: roleResult } ], { session }); throw new Error("Error message"); if (userResult){ // Create currency - 修正create的参数 await CurrencyModel.create([ { name: 'Dollar', createdAt: new Date(), symbol: '$', isoCode: 'USD', default: true, userRole: 'setting-list', createdBy: userResult[0], } ], { session }); } // Generate token using jwt, secret, and user data const token = jwt.sign( { email: userResult[0].email, id: userResult[0]._id }, secret, { expiresIn: "24h" } ); // Send welcome email to registered user await sendWelcomeEmail(userResult[0].email, userResult[0].fullname, `Welcome ${userResult[0].fullname} - light-speed.dev`, 'welcome-email.html'); //Commit transaction await session.commitTransaction(); return res.status(201).json({ userResult, token }); } catch (error) { // Abort transaction if one of these request throw error await session.abortTransaction(); console.log(error); return res.status(400).json({ message: 'Something went wrong!' }); } finally { // close session session.endSession(); } };
额外注意事项
- 确保所有和事务相关的数据库操作都明确传递了
{ session }选项,包括查询、创建、更新、删除等操作,否则该操作会脱离事务上下文。 - 在事务中尽量统一使用async/await,避免混用回调函数,否则可能导致session状态不一致。
- 确认你的Mongoose版本支持事务功能(需要MongoDB 4.0+,Mongoose 5.2+),并且集合是分片集群或者副本集(单节点MongoDB默认不支持事务)。
内容的提问来源于stack exchange,提问作者max
相关产品推荐
相关产品推荐

