即使 mongodb 中的一项交易发生错误,交易也不会中止?

Transactions are not aborting even if there occurs an error in one of the transaction in mongodb?

router.put('/', async (req, res) => {
    try {
    const today = new Date();
    var month = (today.getMonth()) + 1;
    var year = today.getFullYear();
    var field = year + "-" + month;
    var category = req.body.category;
    var date = today.getDate();
    const session =  mongoose.startSession();
    const transactionOptions = {
        readPreference: 'primary',
        readConcern: { level: 'local' },
        writeConcern: { w: 'majority' }
    };
   
        const news = await News.findById(req.body.id);
        var newsDate = new Date(news.createdAt).getDate();
        var newsMonth = new Date(news.createdAt).getMonth() + 1;
        var newsYear = new Date(news.createdAt).getFullYear();
        // (await session).startTransaction();


        // (await Publisher.findOneAndUpdate({ registrationNumber: req.body.registrationNumber }, { $inc: { [`monthlyViews.${field}`]: 1, [`categoriesViews.${category}`]: 1 } })).$session(session);
        // if (newsDate == date && newsMonth == month && newsYear == year) {
        //     (await News.findOneAndUpdate({ _id: req.body.id }, { $inc: { [`views.${field}`]: 1, [`totalViews`]: 1, ['publishedDateViews']: 1 } })).$session(session);
        // } else {
        //     (await News.findOneAndUpdate({ _id: req.body.id }, { $inc: { [`views.${field}`]: 1, [`totalViews`]: 1 } })).$session(session);
        // }

        // (await session).commitTransaction();
        // res.status(200).send({ status: 1 });
        const transactionResult = (await session).withTransaction(async () => {
            
            var newsResult;
            if (newsDate == date && newsMonth == month && newsYear == year) {
                newsResult = (await News.findOneAndUpdate({ _id: req.body.id }, { $inc: { [`views.${field}`]: 1, [`totalViews`]: 1, ['publishedDateViews']: 1 } })).$session(session);
            } else {
                newsResult = (await News.findOneAndUpdate({ _id: req.body.id }, { $inc: { [`views.${field}`]: 1, [`totalViews`]: 1 } })).$session(session);
            }
            if (!newsResult) {
                return (await session).abortTransaction();

            }
            const pubresult = (await Publisher.findOneAndUpdate({ registrationNumber: req.body.registrationNumber }, { $inc: { [`monthlyViews.${field}`]: 1, [`categoriesViews.${category}`]: 1 } })).$session(session);
            if (!pubresult) {
                return (await session).abortTransaction();

            }



        }, transactionOptions).then(result => { res.status(200).send({ status: 1 }); }).catch(e => {
            console.log(e);
            res.status(400).send({ status: 0 });

        });
    } catch (error) {
        // (await session).abortTransaction();
        console.error(error);
        res.status(500).send({ status: 0, message: "Internal Server error" });
    } finally {
        (await session).endSession();
    }


});

每当其中一个事务失败时,它也不会中止事务并且事务得到部分提交。例如:当我使用邮递员发送错误的注册号码时,然后 Publisher.findoneandUpdate 一个 returns “TypeError: Cannot read 属性 '$session' of null” 并且交易应该被中止但它得到了部分像 Publisher 行上面的代码一样提交,将其保存在文档中。 我正在使用 mongodb 地图集

我对你的问题的理解是,当你的交易中发生错误时,交易并没有中止?

在检查您的代码时,您似乎将 async/await 与 promise 混在一起,这导致它的可读性降低,而且工作方式也略有不同。

当使用 async/await 时,任何 return 语句都会导致 promise 解析而不是拒绝,而当使用 throw 语句时,promise 将被拒绝而不是解析。

现在,您可以捕获使用 .catch 在回调中抛出的任何错误,但是,如果该方法因 null 上不存在 $session 而抛出,那么您的 (await session).abortTransaction() 未被调用。

您使用 try/catch/finally,但是,catchfinally 都不会执行,因为不会引发任何错误。您已经在回调中发现了任何错误。

相反,纯粹使用 async/await 看看是否有任何不同(请原谅自动格式化代码):


router.put('/', async (req, res) => {
  try {
    const today = new Date();
    var month = today.getMonth() + 1;
    var year = today.getFullYear();
    var field = year + '-' + month;
    var category = req.body.category;
    var date = today.getDate();
    const session = await mongoose.startSession(); // await here
    const transactionOptions = {
      readPreference: 'primary',
      readConcern: { level: 'local' },
      writeConcern: { w: 'majority' },
    };

    const news = await News.findById(req.body.id);
    var newsDate = new Date(news.createdAt).getDate();
    var newsMonth = new Date(news.createdAt).getMonth() + 1;
    var newsYear = new Date(news.createdAt).getFullYear();
    // (await session).startTransaction();

    // (await Publisher.findOneAndUpdate({ registrationNumber: req.body.registrationNumber }, { $inc: { [`monthlyViews.${field}`]: 1, [`categoriesViews.${category}`]: 1 } })).$session(session);
    // if (newsDate == date && newsMonth == month && newsYear == year) {
    //     (await News.findOneAndUpdate({ _id: req.body.id }, { $inc: { [`views.${field}`]: 1, [`totalViews`]: 1, ['publishedDateViews']: 1 } })).$session(session);
    // } else {
    //     (await News.findOneAndUpdate({ _id: req.body.id }, { $inc: { [`views.${field}`]: 1, [`totalViews`]: 1 } })).$session(session);
    // }

    // (await session).commitTransaction();
    // res.status(200).send({ status: 1 });
    await session.withTransaction(async () => {
      var newsResult;
      // if you haven't already, learn about strict equality
      if (newsDate == date && newsMonth == month && newsYear == year) {
        newsResult = (
          await News.findOneAndUpdate(
            { _id: req.body.id },
            {
              $inc: {
                [`views.${field}`]: 1,
                [`totalViews`]: 1,
                ['publishedDateViews']: 1,
              },
            }
          )
        ).$session(session);
      } else {
        newsResult = (
          await News.findOneAndUpdate(
            { _id: req.body.id },
            { $inc: { [`views.${field}`]: 1, [`totalViews`]: 1 } }
          )
        ).$session(session);
      }
      if (!newsResult) {
        // if not found, catch {} will catch it
        throw new Error('news result does not exist');
      }
      const pubresult = (
        await Publisher.findOneAndUpdate(
          { registrationNumber: req.body.registrationNumber },
          {
            $inc: {
              [`monthlyViews.${field}`]: 1,
              [`categoriesViews.${category}`]: 1,
            },
          }
        )
      ).$session(session);
      if (!pubresult) {
        throw new Error('publisher does not exist');
      }
    }, transactionOptions);

    res.status(200).send({ status: 1 });
  } catch (error) {
    session.abortTransaction(); // await if async

    if (
      error.message === 'publisher does not exist' ||
      error.message === 'news result does not exist'
    ) {
      res.status(404).send({ status: 0 }); // not found
      return;
    }
    // handle validation errors or whatever you used 400 for
    res.status(500).send({ status: 0, message: 'Internal Server error' });
  } finally {
    session.endSession();
  }
});

请注意:我还没有对此进行测试,但是,请尝试一下,看看您的问题是否已得到解决。如果 abortTransactionendSession 是异步的,则根据需要使用 await。