如何防止猫鼬交易"Given transaction number 1 does not match any in-progress transactions"?



我正在使用猫鼬访问我的数据库。我需要使用事务进行原子插入更新。 95%的时间我的交易工作正常,但5%的时间显示错误:

"给定事务编号 1 与任何正在进行的事务不匹配">

重现此错误非常困难,所以我真的很想了解它来自哪里以摆脱它。 我找不到关于这种行为的非常明确的解释。

我尝试在各种函数上使用 async/await 关键字。我不知道手术是没有及时完成还是太早完成。

这是我使用的代码:

export const createMany = async function (req, res, next) {
if (!isIterable(req.body)) {
res.status(400).send('Wrong format of body')
return
}
if (req.body.length === 0) {
res.status(400).send('The body is well formed (an array) but empty')
return
}
const session = await mongoose.startSession()
session.startTransaction()
try {
const packageBundle = await Package.create(req.body, { session })
const options = []
for (const key in packageBundle) {
if (Object.prototype.hasOwnProperty.call(packageBundle, key)) {
options.push({
updateOne: {
filter: { _id: packageBundle[key].id },
update: {
$set: {
custom_id_string: 'CAB' + packageBundle[key].custom_id.toLocaleString('en-US', {
minimumIntegerDigits: 14,
useGrouping: false
})
},
upsert: true
}
}
})
}
}
await Package.bulkWrite(
options,
{ session }
)
for (const key in packageBundle) {
if (Object.prototype.hasOwnProperty.call(packageBundle, key)) {
packageBundle[key].custom_id_string = 'CAB' + packageBundle[key].custom_id.toLocaleString('en-US', {
minimumIntegerDigits: 14,
useGrouping: false
})
}
}
res.status(201).json(packageBundle)
await session.commitTransaction()
} catch (error) {
res.status(500).end()
await session.abortTransaction()
throw error
} finally {
session.endSession()
}
}

我希望我的代码添加到数据库中并以原子方式更新条目包,没有不稳定的数据库状态。 这对于主要部分来说是完美的,但我需要确保这个错误不再显示。

您应该使用session.withTransaction()帮助程序函数来执行事务,如猫鼬文档中所述。这将负责启动、提交和重试事务,以防交易失败。

const session = await mongoose.startSession();
await session.withTransaction(async () => {
// Your transaction methods
});
<小时 />

说明:

MongoDB中的多文档事务相对较新,在某些情况下可能有点不稳定,例如此处所述。当然,这里的猫鼬也有报道。您的错误很可能是瞬态事务错误,这是由于提交事务时发生写入冲突所致。

但是,这是MongoDB的已知和预期问题,这些评论解释了他们为什么决定这样做的原因。此外,他们声称用户应该处理写入冲突的情况,并在发生这种情况时重试事务。

因此,查看您的代码,Package.create(...)方法似乎是触发错误的原因,因为此方法正在为数组中的每个文档执行save()(来自猫鼬文档(。

一个快速的解决方案可能是使用Package.insertMany(...)而不是create(),因为Model.insertMany()"只向服务器发送一个操作,而不是每个文档一个"(来自猫鼬文档(。

但是,MongoDB提供了一个辅助函数session.withTransaction(),它将负责启动和提交事务,并在出现任何错误时重试它,自版本v3.2.1以来。因此,这应该是您以更安全的方式处理交易的首选方式;当然,这可以通过 Node.js API 在 Mongoose 中使用。

接受的答案很棒。就我而言,我在一个会话中串行运行多个事务。我仍然时不时地面临这个问题。我写了一个小助手来解决这个问题。

文件 1:

// do some work here
await session.withTransaction(() => {});
// ensure the earlier transaction is completed
await ensureTransactionCompletion(session);
// do some more work here
await session.withTransaction(() => {});

实用工具文件:

async ensureTransactionCompletion(session: ClientSession, maxRetryCount: number = 50) {
// When we are trying to split our operations into multiple transactions
// Sometimes we are getting an error that the earlier transaction is still in progress
// To avoid that, we ensure the earlier transaction has finished
let count = 0;
while (session.inTransaction()) {
if (count >= maxRetryCount) {
break;
}
// Adding a delay so that the transaction get be committed
await new Promise(r => setTimeout(r, 100));
count++;
}
}

相关内容

最新更新