Firestore使用事务与查询批量删除关联文档的实现正确性咨询
问题解答
一、当前实现是否正确
你当前的实现存在设计缺陷,不符合Firebase Firestore事务的最佳实践:
- 你将7个关联集合的查询逻辑写在了
runTransaction的回调函数内部,而非事务外部。Firestore事务的回调存在重试机制(当出现写冲突、文档变更等情况时会自动重试最多5次),这会导致关联查询被重复执行,产生不必要的性能消耗,也和你最初“先在事务外查询待删文档再传入事务”的设计思路不符。 - 事务内调用
t.getAll()读取待删文档、再执行批量删除的逻辑是符合规范的:Firestore事务会校验所有读取的文档在事务执行周期内有没有被其他操作修改,一旦发生变更会自动重试,保证删除操作的原子性,不会出现部分文档删除、部分保留的异常状态。
修正方案:将关联集合的查询逻辑移到事务外部,拿到所有待删文档的引用后再传入事务执行删除,修正后的代码如下:
"use strict"; const functions = require("firebase-functions"); const admin = require("firebase-admin"); exports.deleteOrderAndInformation = functions.https.onCall( async (data, context) => { const { admin: adminToken, pa } = context.auth.token; if (!adminToken && !pa) throw new functions.https.HttpsError( "permission-denied", "Insufficent permissions to delete order" ); try { const db = admin.firestore(); const { orderId } = data; const orderRef = db.doc(`filmOrders/${orderId}`); // 将关联集合查询移到事务外部执行 const batchCols = [ "extrusionBatches", "printingBatches", "laminationBatches", "slittingBatches", "conversionBatches", "filmBatches", "filmEvents" ]; const queriesPromises = batchCols.map(col => { return db .collection(col) .where("orderId", "==", orderId) .get(); }); const queriesResolved = await Promise.all(queriesPromises); const docRefs = queriesResolved.map(qSnapshot => { return qSnapshot.docs.map(doc => doc.ref); }); const docRefsFlat = docRefs.flat(); // 执行事务 await db.runTransaction(async t => { const tDocs = await t.getAll(...docRefsFlat, orderRef); const tDelete = tDocs.map(doc => { return t.delete(doc.ref); }); await Promise.all(tDelete); }); } catch (error) { throw new functions.https.HttpsError("cancelled", error.toString()); } } );
二、事务运行期间新增的关联文档是否会被同步删除
不会被同步删除,原因如下:
- 关联文档的查询操作是在事务执行前完成的,查询结果仅包含查询发起那一刻已经存在的关联文档。事务运行过程中新增的关联文档不会被包含在提前获取的待删文档引用列表中,因此不会被事务删除。
如果需要避免这类遗漏,可以搭配以下兜底方案:
- 新增
filmOrders集合的onDelete云函数触发器,当filmOrders文档被删除时,自动再次查询所有关联集合删除对应的文档,兜底处理事务执行期间新增的关联数据 - 事务执行完成后,额外做一次全关联集合的查询校验,如果存在漏删的文档执行二次删除
内容的提问来源于stack exchange,提问作者SRR
相关产品推荐
相关产品推荐

