Транзакции не прерываются, даже если в одной из транзакций в mongodb возникает ошибка?

#javascript #node.js #mongodb #mongoose #transactions

Вопрос:

 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 amp;amp; newsMonth == month amp;amp; 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 newsResu<
            if (newsDate == date amp;amp; newsMonth == month amp;amp; 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();
    }


});
 

Всякий раз, когда одна из транзакций завершается неудачно, она также не прерывает транзакцию, и транзакция была частично зафиксирована. например: когда я отправляю неправильный регистрационный номер с помощью postman, Publisher.findOneAndUpdate возвращает «Ошибка типа: Не удается прочитать свойство» $session «null», и транзакция должна быть прервана, но она была частично зафиксирована, как код над строкой Publisher, сохраните ее в документе.
я использую атлас mongodb

Ответ №1:

Насколько я понимаю, ваша проблема заключается в том, что когда в вашей транзакции возникает ошибка, транзакция не прерывается?

При просмотре вашего кода вы, похоже, смешиваете асинхронность/ожидание с обещаниями, что приводит к тому, что он становится менее читаемым, а также все работает немного по-другому.

При использовании async/await любое return утверждение приведет к тому, что обещание будет разрешено, а не отклонено, тогда как при использовании throw оператора обещание будет отклонено, а не разрешено.

Теперь вы ловите любую ошибку, которая возникает внутри вашего обратного вызова , используя .catch , однако, если метод выдает, потому $session что не может существовать null , то ваш (await session).abortTransaction() не вызывается.

Однако вы используете try/catch/finally, и то, и catch другое, и finally не выполняются, так как ошибка не возникает. Вы уже обнаружили какую-либо ошибку в своем обратном вызове.

Вместо этого используйте 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 amp;amp; newsMonth == month amp;amp; 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 newsResu<
      // if you haven't already, learn about strict equality
      if (newsDate == date amp;amp; newsMonth == month amp;amp; 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();
  }
});
 

Пожалуйста, обратите внимание: я не тестировал это, однако попробуйте и посмотрите, исправлена ли ваша проблема. Если abortTransaction и endSession являются асинхронными, то используйте ожидание по мере необходимости.