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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 16:35:12