如何防止“给定的交易号 1 与任何正在进行的交易不匹配”与猫鼬交易?

Exo*_*mus 8 transactions backend mongoose mongodb node.js

我正在使用 Mongoose 访问我的数据库。我需要使用事务来进行原子插入更新。我的交易在 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()
  }
}
Run Code Online (Sandbox Code Playgroud)

我希望我的代码以原子方式添加到数据库中并更新入口包,没有不稳定的数据库状态。这对于主要部分非常有效,但我需要确保不再显示此错误。

gas*_*sbi 8

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

const session = await mongoose.startSession();
await session.withTransaction(async () => {
    // Your transaction methods
});
Run Code Online (Sandbox Code Playgroud)

解释:

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

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

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

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

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