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

Node.js Patch请求数据更新滞后、返回旧数据问题排查

接口Patch操作异常问题

问题场景

向接口/inventorys/users/:inventoryId连续发送4次Patch请求,初始状态已手动清空inventory的users数组,预期实现:重复请求同一用户ID时,在添加/移除状态间切换,4次请求后users数组应为空。

请求详情

REQUEST1 => { users: 65ef2d09ef674c1e1a3632e5 }
REQUEST2 => { users: 65ef2d09ef674c1e1a3632e5 }
REQUEST3 => { users: 65ef2d11ef674c1e1a3632e9 }
REQUEST4 => { users: 65ef2d11ef674c1e1a3632e9 }

接口处理代码

router.patch("/inventorys/users/:inventoryId", auth, async (req, res) => {  
  try {
    //check if inventory exist and 
    const inventory = await Inventory.findOne({
      _id: req.params.inventoryId
    });

    if (!inventory) {
      return res.status(404).send({ error: "Inventory not found" });
    }

    // check if user exist in the data base.
    const newUserOrOldUser = await User.findOne({
      _id: req.body.users,
    });


    // Check if user already exist users array
    const userIndex = inventory.users.findIndex((userId) =>
      userId.equals(req.body.users)
    );

    // new user
    if (userIndex === -1) {
      await Inventory.updateOne(
        { _id: inventory._id },
        { $push: { users: req.body.users } }
      );
      console.log("add user", req.body.users, req.user._id);
    } else {
      await Inventory.updateOne(
        { _id: inventory._id },
        { $pull: { users: req.body.users } }
      );
      console.log("remove user", req.body.users);
    }
    return res.status(200).send(inventory);
  } catch (err) {
    if (err) {
      res.status(400).send({ error: err.message });
    }
  }
});

控制台输出

remove user 65ef2d09ef674c1e1a3632e5
add user 65ef2d09ef674c1e1a3632e5
add user 65ef2d11ef674c1e1a3632e9
remove user 65ef2d11ef674c1e1a3632e9

异常现象

  • 第一次请求日志显示移除用户,但返回的响应数据中用户已被添加;
  • 第二次请求日志显示添加用户,但返回的users数组为空;
  • 第三次请求日志显示添加用户65ef2d11ef674c1e1a3632e9,但返回的数组中存在65ef2d09ef674c1e1a3632e5;
  • 第四次请求日志显示移除用户65ef2d11ef674c1e1a3632e9,但实际该用户尚未被添加,执行的是添加操作;
  • 最终4次请求后,users数组中有2个用户,与预期为空不符。

问题原因

核心问题是数据读取与更新的不一致性:

  1. 代码先通过Inventory.findOne获取内存中的inventory对象,基于旧数据判断用户是否存在;
  2. 执行updateOne修改数据库后,未重新查询最新数据,直接返回初始的旧对象;
  3. 连续请求时,后续请求可能读取到数据库更新前的旧数据,导致判断逻辑完全错误,出现日志、实际操作、响应数据三者不匹配的情况;
  4. 变量newUserOrOldUser定义后未被使用,属于冗余代码。

修复方案

方案一:更新后重新查询最新数据返回

修改代码,在执行更新操作后,重新查询数据库获取最新的inventory对象再返回:

router.patch("/inventorys/users/:inventoryId", auth, async (req, res) => {  
  try {
    const inventory = await Inventory.findOne({
      _id: req.params.inventoryId
    });

    if (!inventory) {
      return res.status(404).send({ error: "Inventory not found" });
    }

    // 验证用户存在(可根据需求添加不存在时的错误返回)
    const user = await User.findOne({ _id: req.body.users });
    if (!user) {
      return res.status(404).send({ error: "User not found" });
    }

    const userId = req.body.users;
    const userIndex = inventory.users.findIndex(id => id.equals(userId));

    if (userIndex === -1) {
      await Inventory.updateOne(
        { _id: inventory._id },
        { $push: { users: userId } }
      );
      console.log("add user", userId, req.user._id);
    } else {
      await Inventory.updateOne(
        { _id: inventory._id },
        { $pull: { users: userId } }
      );
      console.log("remove user", userId);
    }

    // 重新查询最新数据返回
    const updatedInventory = await Inventory.findOne({ _id: inventory._id });
    return res.status(200).send(updatedInventory);
  } catch (err) {
    res.status(400).send({ error: err.message });
  }
});

方案二:使用原子操作(高效避免并发问题)

利用MongoDB的原子操作,直接在数据库层面完成判断与更新,仅需一次数据库交互:

router.patch("/inventorys/users/:inventoryId", auth, async (req, res) => {  
  try {
    const userId = req.body.users;

    // 验证用户存在
    const userExists = await User.exists({ _id: userId });
    if (!userExists) {
      return res.status(404).send({ error: "User not found" });
    }

    // 原子操作:判断用户是否存在,自动执行添加/移除
    const updatedInventory = await Inventory.findOneAndUpdate(
      { _id: req.params.inventoryId },
      [
        {
          $set: {
            users: {
              $cond: {
                if: { $in: [userId, "$users"] },
                then: { $pull: ["$users", userId] },
                else: { $addToSet: ["$users", userId] }
              }
            }
          }
        }
      ],
      { new: true, runValidators: true }
    );

    if (!updatedInventory) {
      return res.status(404).send({ error: "Inventory not found" });
    }

    // 打印对应操作日志
    const userWasAdded = updatedInventory.users.some(id => id.equals(userId));
    if (userWasAdded) {
      console.log("add user", userId, req.user._id);
    } else {
      console.log("remove user", userId);
    }

    return res.status(200).send(updatedInventory);
  } catch (err) {
    res.status(400).send({ error: err.message });
  }
});

内容的提问来源于stack exchange,提问作者Khant Min Si Thu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 22:08:19