Firebase客户端在JS时钟同步中如何删除非自身创建的文档
问题描述
我正通过每秒向Firestore写入数据的方式同步两个JavaScript客户端的时钟,随后让第二个远程“从”客户端订阅该文档。
目前该方案运行正常,我可通过如下创建文档引用的方式实时读取文档变更:
const useCloudTimer = (isMaster, opponentCCID, userCCID) => { const dispatch = useDispatch(); const { localRemainingTimeMS, clockTimeOut } = useSelector((state) => state.clock); const timerRef = useRef(null); useEffect(() => { const db = firebase.firestore(); timerRef.current = db.collection('timers').doc(`${isMaster ? userCCID + opponentCCID : opponentCCID + userCCID }`); dispatch(clock(CLOCK, { cloudTimerDbRef: timerRef.current })); }, []); const pushTimer = async (duration) => { try { await timerRef.current.set({ duration }); } catch (error) { logger.error(error, 'cloud timer'); } }; useEffect(() => { if (isMaster) pushTimer(localRemainingTimeMS); }, [localRemainingTimeMS]); const getTimer = async () => { try { const unsubscribeRemoteTimer = await timerRef.current.onSnapshot((doc) => { if (!clockTimeOut && doc.exists) { const duration = Number(doc.data().duration); dispatch(clock(CLOCK, { remoteRemainingTimeMS: duration })); } }); if (clockTimeOut) unsubscribeRemoteTimer().then((arg) => console.log('unsubscribeRemoteTimer', arg)); } catch (error) { logger.error(error, 'getTimer'); } }; useEffect(() => { if (!isMaster) getTimer(); }, []); }; export default useCloudTimer;
目前存在的问题是需要删除该文档时,若由非文档创建者发起删除操作,会出现同名文档先被短暂创建、随后再被删除的情况。该现象发生瞬间可观察到出现了两个ID反向的同名文档:
- 文档处于写入状态时显示为绿色,删除状态时显示为红色。
我的文档引用会存储在redux中,需要时通过状态存储调用,删除逻辑代码如下:
export const deleteCloudTimer = async (timerRef) => { if (timerRef) { try { await timerRef.delete(); } catch (error) { logger.error(error, 'Error removing document: '); } } };
我的Firebase客户端应用要如何才能删除非自身创建的文档?
解决方案
1. 先修复文档ID拼接逻辑
字符串拼接不满足交换律,你当前的ID生成逻辑isMaster ? userCCID + opponentCCID : opponentCCID + userCCID会因为两个端的主从身份不同,生成两个完全不同的文档ID,这就是你看到两个“同名文档”的核心原因。
替换为排序后拼接的逻辑,保证两个端生成的文档ID完全一致:
// 生成双方通用的统一文档ID const getTimerDocId = (userCCID, opponentCCID) => { return [userCCID, opponentCCID].sort().join('_'); } // 替换原来的doc入参 timerRef.current = db.collection('timers').doc(getTimerDocId(userCCID, opponentCCID));
2. 配置Firestore安全规则放开删除权限
Firestore默认的安全规则仅允许文档创建者执行删除操作,非创建者的删除请求会被直接拦截,需要修改timers集合的规则,允许两个关联用户对文档执行全部操作:
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /timers/{docId} { // 校验当前登录用户属于该文档对应的两个用户之一,即拥有读写、删除权限 allow read, write, delete: if request.auth != null && (docId.split('_')[0] == request.auth.uid || docId.split('_')[1] == request.auth.uid); } } }
3. 补充防重复写入逻辑
主端当前是无判断直接每秒set写入,若从端触发删除后主端还未收到停止指令,会再次把已删除的文档写入恢复。修改pushTimer逻辑,写入前先校验文档是否存在,避免已删除的文档被重新创建:
const pushTimer = async (duration) => { try { const docSnap = await timerRef.current.get(); // 仅当文档存在时才继续写入 if (docSnap.exists) { await timerRef.current.set({ duration }); } } catch (error) { logger.error(error, 'cloud timer'); } };
内容的提问来源于stack exchange,提问作者Mr. Robot
相关产品推荐
相关产品推荐

