【问题标题】:Batched Writes or Transaction in Cloud Functions? [duplicate]云函数中的批量写入或事务? [复制]
【发布时间】:2025-12-02 15:30:01
【问题描述】:

我有以下云函数,想知道我应该使用批量写入还是事务:

const firestore = admin.firestore()
// The following two queries potentially return hundreds of documents.
const queryA = firestore.collectionGroup('a').where('b', '==', 'c'),
  queryB = firestore.collection('b').where('b', '==', 'c')

const snapshotA = await queryA.get(), snapshotB = await queryB.get()
const batch = firestore.batch()
for (const documentSnapshot of snapshotA.docs.concat(snapshotB.docs)) {
  batch.update(documentSnapshot.ref, { 'b': 'd' })
}
return batch.commit()

我确实要求此操作永远不会失败,但是,我没有看到任何会失败的情况。

在这种情况下是否有任何理由使用事务
相反,这里有什么理由不使用事务吗?

【问题讨论】:

    标签: firebase google-cloud-firestore google-cloud-functions


    【解决方案1】:

    您只是写入到 Firestore(而不是读取),因此乍一看,没有必要使用事务,因为“当您想要更新字段的值时,事务很有用 基于其当前值或其他字段的值”(请参阅​​doc),您应该使用批量写入。

    但是,您应该注意“每个事务或每批写入最多可写入 500 个文档”(参见同一文档)。由于您提到您的查询“可能返回数百个文档”,您可能会在这里遇到问题,必须分几批编写。


    其他点:您说“我确实要求此操作永远不会失败,但是,我没有看到任何情况下这会失败”。您不能确定它永远会失败:如文档中所述,云函数“可能由于内部错误而过早退出”(有关更多详细信息,请参见下文)。例如,Cloud Function 平台和 Firestore 之间可能存在连接问题,并且您的批量写入失败(与您的代码无关)。

    要满足此要求(即“我确实要求此操作永远不会失败”),您应该利用 重试后台云功能的可能性以及 批处理of writes 以原子方式完成。对于后台 Cloud Functions 重试,请参阅此 doc,其中解释了:

    1. 为什么会发生这种情况(“在极少数情况下,函数可能会由于内部错误而提前退出,默认情况下该函数可能会或可能不会自动重试”),以及
    2. 如何处理这种情况(用两个词,启用重试并使您的后台 Cloud Functions 具有幂等性)。

    【讨论】:

    • 这帮助我消除了很多困惑。我可以获取我的文档快照数组并将其分成 500 个包,然后对所有这些包运行批量写入,返回 Promise.all
    • 不确定通过将Promise.all() 与批处理写入一起使用,您是否会保留写入的全局原子字符...。请参阅文档developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…,其中说“如果任何承诺拒绝, Promise.all 以被拒绝的 Promise 的值异步拒绝,无论其他 Promise 是否已解决。”。因此,您可能会遇到一种情况,即(在同一个 Cloud Function 中)一些批量写入已提交,而另一些则未提交,然后可能难以确保幂等性......
    • 操作是幂等的,这意味着结合失败重试应该没问题(假设所有批量写入最终都会一起成功)。另外,有没有别的办法?如果您知道替代方案,那就太好了,但是,我看不出有任何方法可以在 500 个字段转换的限制下确保原子行为。
    • 你说得对,它是幂等的! (您只需更新字段b),所以您应该可以接受这种方法!