Javascript “如何修复”;“争论太多”;关于由批写入触发的onCreate

Javascript “如何修复”;“争论太多”;关于由批写入触发的onCreate,javascript,google-cloud-firestore,google-cloud-functions,Javascript,Google Cloud Firestore,Google Cloud Functions,我的学校目标是教室和学生。学生是每个家庭的子集合。在创建一所学校时,我分几批写了几百个家庭 在创建每个学生时,我想做几件事: 更新学生家庭中包含学生名字的字符串 学生包含一个教室id,我希望用学生的一些数据更新教室 学校保留了单独的学生名单,以便查找,我想把学生添加到那里 代码看起来像这样 exports.didCreateStudent = functions.firestore.document(studentPath).onCreate((snap, context) => {

我的学校目标是教室和学生。学生是每个家庭的子集合。在创建一所学校时,我分几批写了几百个家庭

在创建每个学生时,我想做几件事:

  • 更新学生家庭中包含学生名字的字符串
  • 学生包含一个教室id,我希望用学生的一些数据更新教室
  • 学校保留了单独的学生名单,以便查找,我想把学生添加到那里
代码看起来像这样

exports.didCreateStudent = functions.firestore.document(studentPath).onCreate((snap, context) => {
  return Promise.all([
    updateFamilyNames(snap.ref.parent.parent),
    updateClassroomStudent(snap, null, snap.data().classroom),
    updateStudentCollection(null, snap)
  ])
})
  return transaction.get(classroomDocRef).then(snapshot => {
    const student = Object.assign({}, _.pick(studentSnapshot.data(), 'firstName', 'lastName', 'grade'), { ref: studentSnapshot.ref })
    const addition = { students: { [studentSnapshot.id]: student } }
    return transaction.set(snapshot.ref, addition, { merge: true })
  })
这些函数中的每一个都执行一个事务。第一个得到学生的家人并写下孩子的名字,第二个在学生的教室里做同样的事情,等等。(我在触发器上执行这些操作也是为了获得更新和删除时的行为)

例如,
updateClassroomStudent
的突出部分如下所示

exports.didCreateStudent = functions.firestore.document(studentPath).onCreate((snap, context) => {
  return Promise.all([
    updateFamilyNames(snap.ref.parent.parent),
    updateClassroomStudent(snap, null, snap.data().classroom),
    updateStudentCollection(null, snap)
  ])
})
  return transaction.get(classroomDocRef).then(snapshot => {
    const student = Object.assign({}, _.pick(studentSnapshot.data(), 'firstName', 'lastName', 'grade'), { ref: studentSnapshot.ref })
    const addition = { students: { [studentSnapshot.id]: student } }
    return transaction.set(snapshot.ref, addition, { merge: true })
  })
这对于小型学校(10个家庭,每个家庭大约有2名学生)来说效果很好,但是对于大型学校(100个家庭),我开始看到
Error:10中止:对这些文档的争论太多了。请重试。

我可以通过跳过
updateClassroomStudent
updateStudentCollection
来避免错误,但如果我允许其中一个(或两个)运行,则不能


不使用事务会有帮助吗?当使用写入批处理创建几百个文档(如学生的家庭)时,
onCreate
触发器是否会按顺序运行?如果是这样,也许我可以跳过事务并节省一些开销,也许只是在批之间暂停。

云函数触发器没有保证的执行顺序,也不一定是串行执行的。如果您的代码启动了数百个触发函数的事件,那么该函数很可能会并行运行数百个。这会在
snapshot.ref
处为文档创建大量争用

我还建议阅读本文,其中包含相同的错误,使用不同的用例:

我所说的“争用”是指通过事务对文档进行大量并发访问。由于所有写入操作,事务处理程序重试次数过多。该行为在以下文件中说明:

在并发编辑的情况下,Cloud Firestore将再次运行整个事务。例如,如果一个事务读取文档,而另一个客户端修改了其中任何一个文档,则Cloud Firestore将重试该事务。此功能确保事务在最新和一致的数据上运行

后来:

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

在没有看到所有代码的情况下,很难推荐一个行动方案,但我认为关键在于尝试通过在单个事务中应用文档的所有更新来改变方案的工作方式,而不是允许大量事务在多个更新中发生冲突


另一个更具可扩展性的选项是将学生列表存储为子集合中的单个文档,而不是单个文档中的列表字段。

谢谢。您是否推荐您引用的线程上投票最多的答案?我在发帖前读到了这篇文章,对调整重试的想法感到不满,并等待它起作用(我认为这很脆弱)。要澄清的是,锁定发生在文档而不是集合中吗?我只建议您将该问题作为附加阅读,以供考虑。解决许多问题的方法不止一种。争论最多的文档是“教室”,30岁左右的学生可以一次尝试输入一些关于自己的参考信息(这是我提供的代码)。只有当一所学校的所有家庭同时建立时,这种情况才会大量发生。我知道我可以通过每个教室的事务来实现这一点,但我也希望稍后通过触发器处理一次性的学生插入。我希望我能为一个大的写操作禁用触发器。我不能,对吧?没错。正如我在最后一句中所说的,实现这一点的可扩展方法是为每个学生提供单独的文档。再次感谢。我会接受这个关于执行命令的好建议。我现在理解这个问题与触发器无关,是由一个小的代码更改引起的。修复它让我对如何继续感到困惑。我已经在这里详细说明了这个问题,非常感谢你的建议。