当我使用承诺数组( Promise.all )时,Mongoose 事务不会回滚

问题描述 投票:0回答:1

我这里有一个奇怪的情况 我正在尝试在数组中插入一些写入查询(它们都属于同一事务),并使用 Promise.all(arr) 对整个事务执行/回滚 问题是,如果我的代码抛出错误并且我在调用 Promise.all() 之前中止事务,则承诺数组中要完成的操作将被提交!但是,如果我的代码在调用 Promise.all() 后抛出错误,文档将按预期恢复到原始状态。 即使事务仅仅因为我在调用 Promise.all() 之前抛出异常而中止,如何避免提交对文档的更改。

这是代码片段

module.exports.updateVariantStock = async (req, res, next) => {
  const session = await mongoose.startSession();
  try {

    session.startTransaction();

    const opts = { session };

    const { newStock } = req.body;

    const { variant } = req.mydata;

    const promises = [];

    variant.stock = Number(variant.stock) + Number(newStock);

    const variantPromise = variant.save(opts);
    promises.push(variantPromise);

    const stockLogElement = new VariantStockLogElement({
     // Data to insert
    });

    const newStockLogContainerPromise = stockLogElement.save(opts);

    promises.push(newStockLogContainerPromise);

    throw new Error("A test error"); // If error is thrown here, changes are commited
    await Promise.all(promises);
    throw new Error("A test error"); // If error is thrown here, changes are rolled-back

    await session.commitTransaction();
  
    return res.status(200).json({ success: "Stock is updated." });
  } catch (err) {
      await session.abortTransaction();
    next(err);
  } finally {
    session.endSession();
  }
};

请注意,如果我在抛出此错误之前运行任何数据库查询(与事务无关),则更改将回滚。但我收到这个错误

UnhandledPromiseRejectionWarning: MongoError: Transaction with { txnNumber: 1 } has been aborted.

我真的不明白这里发生了什么。

node.js mongodb express mongoose transactions
1个回答
0
投票

在 MongoDB 中使用 Promise.all 进行事务处理时,了解 Promise 的结算顺序非常重要。在代码中,当您在调用 Promise.all(promises) 之前抛出错误时,数组中的 Promise 可能已经开始执行,并且 MongoDB 可能会将这些更改视为事务外部的单独操作。但是,如果在调用 Promise.all(promises) 后抛出错误,则操作将捆绑到同一事务中。为了确保在调用 Promise.all 之前不会执行数组中的 Promise,您可以将 throw new Error("A test error") 行移到 Promise.all(promises) 之后。你可以考虑做这样的事情,

// Execute promises within the transaction
const promiseResult = await Promise.all(promises);

// Check if any promise rejected
const hasRejectedPromise = promiseResult.some(p => p instanceof Error || p instanceof mongoose.Error);

if (hasRejectedPromise) {
      throw new Error("A test error"); // If error is thrown here, changes are rolled-back
}

await session.commitTransaction();

但由于某种原因,您的目标是在单个 Promise 抛出错误时立即中止事务,您可以通过删除 Promise.all 并使用 try...catch 块单独处理每个 Promise 来实现此目的。如果任何 Promise 抛出错误,您可以立即中止事务并处理错误。考虑这样的事情,

    variant.stock = Number(variant.stock) + Number(newStock);
    const variantPromise = variant.save(opts);

    try {
      await variantPromise; // Handle the promise individually
    } catch (error) {
      // If an error occurs, immediately abort the transaction
      await session.abortTransaction();
      throw error;
    }

    const stockLogElement = new VariantStockLogElement({
      // Data to insert
    });

    const newStockLogContainerPromise = stockLogElement.save(opts);

    try {
      await newStockLogContainerPromise; // Handle the promise individually
    } catch (error) {
      // If an error occurs, immediately abort the transaction
      await session.abortTransaction();
      throw error;
    }

    await session.commitTransaction();
    // .... Continue with the rest of your code ...
© www.soinside.com 2019 - 2024. All rights reserved.