Node.js函数中两个依赖数据库操作,第二个失败时如何回滚第一个?
如何撤销已成功的数据库变更(当后续操作失败时)
当然可以撤销第一个操作的变更,你需要用数据库事务实现「要么全部成功,要么全部回滚」的原子性操作。针对你使用的MongoDB,从4.0版本开始支持多文档事务,正好能解决这个场景的问题。
你的现有代码存在明显缺陷:两个数据库操作被拆分在独立的执行块中,第一个操作成功后直接提交了数据变更,后续第二个操作失败时,无法回滚已经完成的修改。
改造方案:用MongoDB事务包裹两个操作
核心思路是获取MongoDB的会话(session),将两个变更操作绑定到同一个会话中执行——只有当两个操作都成功时才提交事务,任意一步失败就回滚所有已做的变更。
改造后的代码示例:
export const postReset = async (req, res, next) => { const cardId = req.body.cardId; const userId = req.user?.id; if (!cardId || !userId) { return res.status(400).json({ success: false }); } // 获取MongoDB客户端会话 const session = await UsersModel.startSession(); session.startTransaction(); try { // 第一个变更操作(绑定事务会话) const cardRemoved = await UsersModel.updateOne( { _id: userId }, { $pull: { cards: { cardId: cardId } } }, { session, safe: true, multi: true } ); if (!cardRemoved.modifiedCount) { throw new Error("用户卡片移除失败"); } // 第二个变更操作(绑定同一个事务会话) const cardRemovedFromCardsCollection = await CardsModel.findOneAndDelete( { cardId: cardId, cardOwner: userId }, { session } ); if (!cardRemovedFromCardsCollection) { throw new Error("卡片集合中删除卡片失败"); } // 所有操作成功,提交事务 await session.commitTransaction(); return res.status(200).json({ success: true }); } catch (error) { // 任意操作失败,回滚事务 await session.abortTransaction(); return res.status(400).json({ success: false, message: error.message }); } finally { // 结束会话释放资源 session.endSession(); } };
关键要点说明
- 会话绑定:事务内的所有操作必须关联同一个会话,MongoDB通过会话追踪事务的执行状态。
- 原子性保障:事务提交前,所有数据变更都是临时状态;只有执行
commitTransaction后才会持久化,执行abortTransaction则会撤销所有已做的修改。 - 主动触发回滚:不仅要捕获数据库操作的异常,业务逻辑判断不通过时也要主动抛出错误,触发事务回滚。
- 环境要求:MongoDB事务需要运行在副本集或分片集群环境中,单节点实例不支持该功能。
内容的提问来源于stack exchange,提问作者BartD
相关产品推荐
相关产品推荐

