如何防止Firebase实时数据库父节点被删除后意外创建子数据
Firebase实时数据库投票函数竞态问题修复方案
你遇到的是典型的读写竞态问题:先读取节点数据再执行更新的操作不是原子性的,两次操作的时间差内节点可能被其他操作删除,而Firebase的update方法默认会自动创建路径上所有缺失的父节点,这就是废弃节点被重复创建的根源。
修复思路
使用Firebase Realtime Database原生提供的transaction()原子操作方法,将节点存在性校验、票数累加、更新/删除操作绑定为不可拆分的原子操作,从根本上消除读写时间差带来的竞态问题。
修复后完整代码
exports.newVoteForDetected = functions.database.ref("Currentactivecontests/{userid}/Voteforstamp") .onUpdate((snapshot, context) => { const userid = context.params.userid; const contestRef = database.ref(`Currentactivecontests/${userid}`); // 原子事务:同时完成存在性校验+票数更新逻辑 return contestRef.transaction((currentContestData) => { // 节点已被删除,直接终止事务,不产生任何写入操作 if (currentContestData === null) { return; } // 累加票数 currentContestData.for = (currentContestData.for || 0) + 1; // 票数未达阈值,返回更新后的数据提交事务 if (currentContestData.for <= 3) { return currentContestData; } // 票数达标,先异步迁移数据到已完成节点,再返回null删除当前活跃节点 database.ref("Completed").child(userid).set(currentContestData); return null; }); });
方案说明
- 事务执行过程中不会被其他写入操作打断,不存在校验和更新的时间差
- 节点已被删除时事务直接终止,不会自动创建缺失的父节点
- 相比原有嵌套多次读库的逻辑,性能更优、代码结构更简洁
- 可额外在迁移数据前判断
Completed节点下是否已有对应userid的数据,保证迁移操作幂等,避免重复写入
内容的提问来源于stack exchange,提问作者Niko Bellic
相关产品推荐
相关产品推荐

