首页 > 解决方案 > 错误:10 中止:对这些文档的争用过多。请再试一次

问题描述

这个错误是什么意思?

特别是,它们是什么意思:请重试

这是否意味着交易失败我必须手动重新运行交易?根据我从文档中了解到的情况,

事务读取在事务之外修改的文档。在这种情况下,事务会自动再次运行。事务被重试有限次。

如果有,在哪些文件上?该错误不指示它正在谈论的文档。我刚得到这个堆栈:

{ 错误:10 中止:对这些文档的争用过多。请再试一次。在 Object.exports.createStatusErrornode_modules\grpc\src\common.js:87:15) 在 ClientReadableStream._emitStatusIfDone \node_modules\grpc\src\client.js:235:26) 在 ClientReadableStream._receiveStatus \node_modules\grpc\src\client .js:213:8) 在 Object.onReceiveStatus \node_modules\grpc\src\client_interceptors.js:1256:15) 在 InterceptingListener._callNext node_modules\grpc\src\client_interceptors.js:564:42) 在 InterceptingListener.onReceiveStatus\node_modules \grpc\src\client_interceptors.js:614:8) 在 C:\Users\Tolotra Samuel\PhpstormProjects\CryptOcean\node_modules\grpc\src\client_interceptors.js:1019:24 代码:10,元数据:元数据 { _internal_repr: { } }, details: '对这些文件的争论太多了。请再试一次。

要重新创建此错误,只需按照文档中的说明在 db.runTransaction 方法上运行 for 循环

标签: javascriptnode.jsfirebasegoogle-cloud-firestore

解决方案


我们在使用 Firebase Firestore 数据库时遇到了同样的问题。即使是少于 30 件物品的小型柜台也无法计算遇到此问题的地方。

我们的解决方案不是分配计数器,而是增加事务的尝试次数并为这些重试添加延迟时间。

第一步是保存事务操作,因为 const witch 可以传递给另一个函数。

const taskCountTransaction = async transaction => {
  const taskDoc = await transaction.get(taskRef)

  if (taskDoc.exists) {
    let increment = 0
    if (change.after.exists && !change.before.exists) {
      increment = 1
    } else if (!change.after.exists && change.before.exists) {
      increment = -1
    }

    let newCount = (taskDoc.data()['itemsCount'] || 0) + increment
    return await transaction.update(taskRef, { itemsCount: newCount > 0 ? newCount : 0 })
  }

  return null
}

第二步是创建两个辅助函数。一个用于等待特定的时间,另一个用于运行事务并捕获错误。如果出现代码 10 的中止错误,我们只需再次运行事务以进行特定数量的重试。

const wait = ms => { return new Promise(resolve => setTimeout(resolve, ms))}


const runTransaction = async (taskCountTransaction, retry = 0) => {
  try {
    await fs.runTransaction(taskCountTransaction)
    return null
  } catch (e) {
    console.warn(e)
    if (e.code === 10) {
      console.log(`Transaction abort error! Runing it again after ${retry} retries.`)

      if (retry < 4) {
        await wait(1000)
        return runTransaction(taskCountTransaction, ++retry)
      }
    }
  }
}

现在我们已经拥有了我们需要的一切,我们可以调用我们的辅助函数,await我们的事务调用将运行比默认调用更长的时间,并且它会及时延迟。

await runTransaction(taskCountTransaction)

我喜欢这个解决方案的地方在于它并不意味着更多或更复杂的代码,并且大多数已经编写的代码可以保持原样。只有当计数器达到必须计算更多项目的程度时,它才会使用更多时间和资源。否则,时间和资源与您将拥有默认事务相同。

为了扩大大量项目,我们可以增加重试次数或等待时间。两者都在影响 Firebase 的成本。对于等待部分,我们还需要增加函数的超时时间。

免责声明:我没有用数千个或更多项目对这段代码进行压力测试。在我们的具体案例中,问题从 20 多个项目开始,我们需要多达 50 个项目来完成一项任务。我用 200 个项目对其进行了测试,并且问题没有再次出现。


推荐阅读