即使 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,但是,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 && 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();
}
});
请注意:我还没有对此进行测试,但是,请尝试一下,看看您的问题是否已得到解决。如果 abortTransaction
和 endSession
是异步的,则根据需要使用 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 = 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,但是,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 && 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();
}
});
请注意:我还没有对此进行测试,但是,请尝试一下,看看您的问题是否已得到解决。如果 abortTransaction
和 endSession
是异步的,则根据需要使用 await。