【问题标题】:Handling concurrent request that finds and update the same resource in Node Js & Mongo DB?处理在 Node Js 和 Mongo DB 中查找和更新相同资源的并发请求?
【发布时间】:2021-06-30 17:21:26
【问题描述】:

我在节点中有一个函数,该函数在单击结帐按钮后运行。它检查购物车中物品的可用性,如果物品可用,它将从库存中扣除。

我目前正在测试两个用户同时单击结帐按钮。两个用户在他们的购物车中都有完全相同的内容(每个 10 个苹果),总共有 20 个苹果,但库存中只有 10 个苹果。

如果购物车中没有商品,它应该向用户返回一个错误,但两个订单都在进行中。

注意:如果点击之间有 1 秒的延迟,则此方法有效。

我能做些什么来防止这种情况发生?

  // Check if items in inventory
  const availability = await checkInventory(store, cart, seller);

  if (!availability.success) {
    return res.status(400).json({
      success: false,
      type: 'unavailable',
      errors: availability.errors,
    });
  }

  // Deduct Inventory
  const inventory = await deductInventory(store, seller, cart);

  if (!inventory) {
    return next(new ErrorResponse('Server Error', 500));
  }

检查库存

exports.checkInventory = asyncHandler(async (store, cart, seller) => {
  let isAvailable = true;
  const unavailableProducts = [];

  const inventory = await Inventory.find({
    $and: [
      {
        store: store,
        user: seller,
      },
    ],
  });

  const products = inventory[0].products;

  cart.forEach((item) => {
    const product = products.find(
      (product) => product._id.toString() === item.productId
    );

    if (!item.hasvariation) {
      if (product.stock < item.qty) {
        isAvailable = false;
        unavailableProducts.push(
          `${item.title} is not available, only ${product.stock} left available`
        );
      }
    }

    if (item.hasvariation) {
      const variation = product.variations.find(
        (variation) => variation._id.toString() === item.variationId
      );

      const option = variation.options.find(
        (option) => option._id.toString() === item.optionId
      );

      if (option.stock < item.qty) {
        isAvailable = false;
        unavailableProducts.push(
          `${item.title} is not available, only ${product.stock} left available`
        );
      }
    }
  });

  return {
    success: isAvailable,
    errors: unavailableProducts,
  };
});

扣除库存

exports.deductInventory = asyncHandler(async (store, seller, cart) => {
  const inventory = await Inventory.findOne({
    $and: [
      {
        store: store,
        user: seller,
      },
    ],
  });

  const products = inventory.products;

  cart.forEach((item) => {
    const product = products.find(
      (product) => product._id.toString() === item.productId
    );
    if (!item.hasvariation) {
      product.stock = product.stock - item.qty;
    }

    if (item.hasvariation) {
      const variation = product.variations.find(
        (variation) => variation._id.toString() === item.variationId
      );

      const option = variation.options.find(
        (option) => option._id.toString() === item.optionId
      );

      option.stock = option.stock - item.qty;
    }
  });

  const saveInventory = await Inventory.findOneAndUpdate(
    {
      $and: [
        {
          store: store,
          user: seller,
        },
      ],
    },
    {
      $set: { products: products },
    },
    { new: true, runValidator: true }
  );

  if (!saveInventory) {
    return {
      success: false,
      errors: ['Server Error'],
    };
  }

  return {
    success: true,
  };
});

【问题讨论】:

    标签: node.js mongodb


    【解决方案1】:

    问题在于 2 个结帐调用(几乎)同时运行,并且您的例程不是线程保存的。两个调用都读取内存中库存数据的副本。因此,这两个调用都得到一个 products.stock=10 并根据您检查的本地信息,通过计算函数中的新数量(stock-qty)来设置产品计数器,并使用更新查询将其设置为固定值(所以两个调用都将 products.stock 更新为 0)。导致您的并发问题。

    你应该做的是让 mongodb 为你处理并发。 有几种方法可以处理并发,但您可以例如使用 $inc 直接在 mongo 中减少库存量。这样数据库中的库存量永远不会出错。

    结果 = 等待更新({stock: {$ge: 10}}, {$inc: {stock: -10}})

    由于我在查询中添加了一个过滤器,因此订单金额不能低于 0,而且您现在可以检查更新调用的结果以查看更新是否修改了任何文档。如果没有 (result.nModified==0),您就知道库存太少,您可以向用户报告。

    https://docs.mongodb.com/manual/reference/operator/update/inc/ https://docs.mongodb.com/manual/reference/method/db.collection.update/#std-label-writeresults-update

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-08-12
      • 1970-01-01
      • 1970-01-01
      • 2017-11-13
      • 2020-01-27
      • 2012-09-23
      • 1970-01-01
      相关资源
      最近更新 更多