如何使用 Mongoose Transactions 防止“给定的交易号 1 与任何正在进行的交易不匹配”?

2024-02-01

我正在使用 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()
  }
}

我希望我的代码添加到数据库中并以原子方式更新条目包,不存在不稳定的数据库状态。 这对于主要部分来说工作得很好,但我需要确保这个错误不再出现。


您应该使用session.withTransaction()执行事务的辅助函数,如中指出的猫鼬文档 https://mongoosejs.com/docs/transactions.html。这将负责事务的启动、提交和重试,以防事务失败。

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

解释:

MongoDB 中的多文档事务相对较新,在某些情况下可能有点不稳定,例如所描述的here https://jira.mongodb.org/browse/SERVER-36428。当然,Mongoose 中也有报道here https://github.com/Automattic/mongoose/issues/7502#。你的错误很可能是瞬态事务错误 https://stackoverflow.com/questions/52153538/what-is-a-transienttransactionerror-in-mongoose-or-mongodb由于一个写冲突事务提交时发生。

然而,这是 MongoDB 的一个已知且预期的问题,并且these https://github.com/Automattic/mongoose/issues/7502#issuecomment-470586927 解释他们决定这样做的原因。此外,他们声称用户应该处理写入冲突的情况,并在发生这种情况时重试事务。

因此,查看您的代码,Package.create(...)方法似乎是触发错误的原因,因为该方法正在执行save()对于数组中的每个文档(来自猫鼬文档 https://mongoosejs.com/docs/api.html#model_Model.create).

一个快速的解决方案可能是使用Package.insertMany(...)代替create(),自从Model.insertMany()“只向服务器发送一个操作,而不是为每个文档发送一个操作”(来自猫鼬文档 https://mongoosejs.com/docs/api.html#model_Model.insertMany).

然而,MongoDB提供了一个辅助函数session.withTransaction()这将负责启动和提交事务,并且如果出现任何错误,请重试, since 发布v3.2.1 https://github.com/mongodb/node-mongodb-native/releases/tag/v3.2.1。因此,这应该是你的首选方式以更安全的方式处理交易;当然,这就是可用于猫鼬 https://mongoosejs.com/docs/transactions.html通过 Node.js API。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用 Mongoose Transactions 防止“给定的交易号 1 与任何正在进行的交易不匹配”? 的相关文章

随机推荐