MongoDB事务未回滚及重复执行问题排查(附Mongoose代码)
Mongoose事务实现问题排查
遇到的问题
- 事务中任一操作失败时,未正确执行回滚;
- 事务回滚后,1分钟后自动启动新事务重复执行相同操作,触发错误:
[Symbol(errorLabels)]: Set(1) { 'TransientTransactionError' }
原始代码
var mongo = require("mongoose") session10 = await mongo.startSession try { await session10.withTransaction(async () => { console.log("transaction started"); await readonlymodel.findOneAndUpdate({}, { $set: { readonlyMode: true, yearEndClosure: true } }, { session:session10 }); var archivalActivity = { activity_id: 4, activityName: 'Current year Data Back', activity_Status: 'In Progress', err_occured: 'None', remarks: 'None' } await year_end_closure_model.findOneAndUpdate({ year: MCpayload.year, month: MCpayload.month, 'activities.activity_id': 3 }, { $set: { 'activities.$.activity_Status': 'Completed', }, }, { session:session10 }); await year_end_closure_model.findOneAndUpdate({ year: MCpayload.year, month: MCpayload.month }, { $push: { activities: archivalActivity } }, {session: session10 }) const dataCurrentYear = await model.find({}, { _id: 0 }, {session: session10 }) const etBackupData = await archival_yearEnd.insertMany(etdataCurrentYear, {session: session10 }); console.log(dataCurrentYear.length, etBackupData.length, "inserted Succesfully") const etproofCurrentYear = await proofmodel.find({}, { _id: 0 }, { session: session10 }); const proofBackupData = await proof_archival_yearEnd.insertMany(proofCurrentYear, { session:session10 }); console.log(proofCurrentYear.length, proofBackupData.length, "inserted Succesfully") const proofCurrentYear = await commonproofmodel.find({}, { _id: 0 }, {session: session10 }); const proofBackupData = await common_proof_archival_yearEnd.insertMany(proofCurrentYear, { session:session10 }); console.log(cmnproofCurrentYear.length, cmnproofBackupData.length, "inserted Succesfully") const etcaseCurrentYear = await etcasemodel.find({}, { _id: 0 }, {session: session10 }); const etcaseBackupData = await caseStudy_proof_archival_yearEnd.insertMany(etcaseCurrentYear, {session: session10 }); console.log(etcaseCurrentYear.length, etcaseBackupData.length, "inserted Succesfully") const SummaryCurrentYear = await summary_model.find({}, { _id: 0 }, {session: session10 }); const SummaryBackupData = await Summary_archive_yearEnd.insertMany(SummaryCurrentYear, {session: session10 }); console.log(SummaryCurrentYear.length, SummaryBackupData.length, "inserted Succesfully") const DetailsCurrentYear = await data_model.find({}, { _id: 0 }, { session:session10 }); const DetailsBackupData = await Detail_archive_yearEnd.insertMany(DetailsCurrentYear, {session: session10 }); console.log(DetailsCurrentYear.length, DetailsBackupData.length, "inserted Succesfully") const ExpCurrentYear = await umt_exception_model.find({}, { _id: 0 }, {session:session10 }); const ExpBackupData = await UmtExceptional_archive_yearEnd.insertMany(ExpCurrentYear, {session: session10 }); console.log(ExpCurrentYear.length, ExpBackupData.length, "inserted Succesfully") more operations to do// return true }catch(err){ console.log(err) }finally{ session10.endSession() }
错误分析与修正点
1. 会话启动错误
session10 = await mongo.startSession 缺少调用括号,正确写法应为 session10 = await mongo.startSession()。原代码获取的是函数引用而非会话实例,导致事务无法正确初始化。
2. 事务异常处理逻辑错误
withTransaction 的回调内部添加了catch块,会吃掉操作异常,导致Mongoose无法感知错误触发回滚。正确做法是移除内部catch,让Mongoose的事务机制自动处理异常与回滚;若必须内部捕获,需重新抛出异常(throw err)。
3. 变量名错误
代码中存在多处变量名不一致问题,比如 insertMany(etdataCurrentYear) 中的etdataCurrentYear应为dataCurrentYear,这类错误会直接导致操作失败,若被内部catch吃掉则事务无法回滚。
4. 会话关闭时机错误
session10.endSession() 放在了withTransaction的内部finally块,会提前终止会话,干扰事务流程。应将会话关闭逻辑移到外层finally块,确保事务完成后再关闭会话。
5. TransientTransactionError 触发原因
该错误源于事务重试机制:Mongoose默认会自动重试事务,加上原代码会话管理错误、批处理任务未正确处理失败状态,导致事务失败后重复执行。可通过withTransaction的第二个参数配置重试策略,同时确保批处理任务在事务失败后不再重复触发。
修正后的代码示例
const mongoose = require("mongoose"); async function runYearEndTransaction(MCpayload) { let session; try { // 正确启动会话 session = await mongoose.startSession(); // 移除内部catch,让Mongoose处理事务异常 const transactionResult = await session.withTransaction(async () => { console.log("transaction started"); await readonlymodel.findOneAndUpdate({}, { $set: { readonlyMode: true, yearEndClosure: true } }, { session }); const archivalActivity = { activity_id: 4, activityName: 'Current year Data Back', activity_Status: 'In Progress', err_occured: 'None', remarks: 'None' }; await year_end_closure_model.findOneAndUpdate( { year: MCpayload.year, month: MCpayload.month, 'activities.activity_id': 3 }, { $set: { 'activities.$.activity_Status': 'Completed' } }, { session } ); await year_end_closure_model.findOneAndUpdate( { year: MCpayload.year, month: MCpayload.month }, { $push: { activities: archivalActivity } }, { session } ); // 修正变量名错误 const dataCurrentYear = await model.find({}, { _id: 0 }, { session }); const etBackupData = await archival_yearEnd.insertMany(dataCurrentYear, { session }); console.log(dataCurrentYear.length, etBackupData.length, "inserted Succesfully"); const etproofCurrentYear = await proofmodel.find({}, { _id: 0 }, { session }); const proofBackupData = await proof_archival_yearEnd.insertMany(etproofCurrentYear, { session }); console.log(etproofCurrentYear.length, proofBackupData.length, "inserted Succesfully"); const cmnproofCurrentYear = await commonproofmodel.find({}, { _id: 0 }, { session }); const cmnProofBackupData = await common_proof_archival_yearEnd.insertMany(cmnproofCurrentYear, { session }); console.log(cmnproofCurrentYear.length, cmnProofBackupData.length, "inserted Succesfully"); // 其他操作同理修正变量名 const etcaseCurrentYear = await etcasemodel.find({}, { _id: 0 }, { session }); const etcaseBackupData = await caseStudy_proof_archival_yearEnd.insertMany(etcaseCurrentYear, { session }); console.log(etcaseCurrentYear.length, etcaseBackupData.length, "inserted Succesfully"); const SummaryCurrentYear = await summary_model.find({}, { _id: 0 }, { session }); const SummaryBackupData = await Summary_archive_yearEnd.insertMany(SummaryCurrentYear, { session }); console.log(SummaryCurrentYear.length, SummaryBackupData.length, "inserted Succesfully"); const DetailsCurrentYear = await data_model.find({}, { _id: 0 }, { session }); const DetailsBackupData = await Detail_archive_yearEnd.insertMany(DetailsCurrentYear, { session }); console.log(DetailsCurrentYear.length, DetailsBackupData.length, "inserted Succesfully"); const ExpCurrentYear = await umt_exception_model.find({}, { _id: 0 }, { session }); const ExpBackupData = await UmtExceptional_archive_yearEnd.insertMany(ExpCurrentYear, { session }); console.log(ExpCurrentYear.length, ExpBackupData.length, "inserted Succesfully"); // 更多操作... return true; }); if (!transactionResult) { console.log("事务未提交,已回滚"); } } catch (err) { console.error("事务执行出错:", err); } finally { // 确保会话正确关闭 if (session) { await session.endSession(); } } }
内容的提问来源于stack exchange,提问作者匿名用户
相关产品推荐
相关产品推荐

