#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
являются асинхронными, то используйте ожидание по мере необходимости.