首页 > 解决方案 > 即使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 会返回“TypeError: Cannot read property '$session' of null”,并且该事务应该被中止,但它像 Publisher 行上方的代码一样被部分提交,将其保存在文件。我正在使用 mongodb 地图集

标签: javascriptnode.jsmongodbmongoosetransactions

解决方案


我对您的问题的理解是,当您的事务中引发错误时,事务不会中止?

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

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

现在,您可以使用 捕获在回调中引发的任何错误.catch,但是,如果该方法由于$sessioncannot exist on 而引发,则不会调用null您的方法。(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是异步的,则根据需要使用等待。


推荐阅读