Firebase云函数模拟器中删除不存在文档意外触发创建触发器的异常问题
我最近在Firebase模拟器环境里遇到了一个特别诡异的问题,折腾了好一会儿才摸清楚原因,先把我的场景、问题和解决方案整理出来,给遇到同样坑的朋友参考:
我的场景与问题详情
我基于Firebase Functions v2写了两个云函数,都是用onDocumentCreated触发器:
- deleteUserData:监听
deleteUser/{userId}路径的文档创建事件,触发后尝试删除userData/{userId}下的对应文档 - checker:仅监听
userData/{userId}路径的文档创建事件,触发时打印日志做验证
函数代码
import { onDocumentCreated } from "firebase-functions/v2/firestore"; import * as firebase_admin from "firebase-admin"; firebase_admin.initializeApp(); // 触发后尝试删除userData对应文档 export const deleteUserData = onDocumentCreated( { document: "deleteUser/{userId}" }, async (event) => { const db = firebase_admin.firestore(); const userId = event.params.userId; // 补充原代码遗漏的参数获取 const tokenDocRef = db.collection("userData").doc(userId); tokenDocRef.delete(); } ); // 仅监听userData文档创建的验证函数 export const checker = onDocumentCreated( { document: "userData/{userId}" }, async (event) => { console.log('checker函数被触发了!'); } );
诡异的异常现象
当我在deleteUser/{userId}下创建文档时,deleteUserData会正常触发,但如果userData/{userId}对应的文档根本不存在,checker函数居然会被意外触发!
从模拟器的执行日志里能明确看到这个问题:
Beginning execution of "asia-south1-deleteUserData" Finished "asia-south1-deleteUserData" in 5.352708ms Beginning execution of "asia-south1-checker" // 这完全不应该发生! Finished "asia-south1-checker" in 5.352708ms
我当然知道可以先查询文档是否存在再执行删除,但我更困惑的是:删除一个不存在的文档,为什么会触发该路径的创建触发器? 毕竟我根本没在userData里创建任何文档。
问题根源分析
翻了Firebase官方文档、GitHub Issues和社区讨论后,终于搞清楚了:
这是Firebase本地模拟器针对Functions v2的一个已知bug——当你调用delete()删除一个不存在的文档时,模拟器的事件路由逻辑出现了误判,错误地生成了一个userData/{userId}的“伪创建事件”,从而触发了checker函数的onDocumentCreated触发器。
⚠️ 重要说明:这个问题仅存在于本地模拟器环境,Firestore生产服务对“删除不存在的文档”的处理是完全正常的:直接返回成功,但不会生成任何变更事件(包括创建、更新、删除),不会触发任何无关触发器。
可行的解决方案
1. 临时修复:先检查文档存在性再删除
这是最直接的解决方式,既适配模拟器,也让生产环境的代码更严谨:
export const deleteUserData = onDocumentCreated( { document: "deleteUser/{userId}" }, async (event) => { const db = firebase_admin.firestore(); const userId = event.params.userId; const tokenDocRef = db.collection("userData").doc(userId); // 先查询文档是否存在 const docSnapshot = await tokenDocRef.get(); if (!docSnapshot.exists) { console.log(`userData/${userId}不存在,跳过删除操作`); return; } // 仅当文档存在时执行删除 await tokenDocRef.delete(); } );
这样就不会触发模拟器的误判逻辑,从根源上避免了跨触发问题。
2. 彻底解决:升级Firebase CLI与模拟器
这个bug在较新版本的Firebase模拟器中已经被修复了,你可以通过升级Firebase CLI来解决:
# 全局升级Firebase CLI npm install -g firebase-tools
升级完成后,重启Firebase模拟器,这个跨触发问题就会消失。
总结
这个问题本质是Firebase本地模拟器对Functions v2的空删除操作的事件路由误判,生产环境不会出现该问题。如果赶进度可以先加文档存在性检查,长期来看升级到最新版模拟器是更彻底的解决方案。
内容来源于stack exchange

