安卓生鲜配送App Firestore事务写入偶发丢失数据问题咨询
Firestore 事务偶发部分写入失败问题原因及解决方案
根因分析
Firestore 事务本身具备原子性,正常服务端执行的事务不可能出现部分写入成功、部分失败的情况,你遇到的偶发问题均来自以下边缘场景:
- update 操作的前置限制:
transaction.update()要求目标文档必须已存在,如果 daylogs 对应的LogDocumentId是首次生成、文档尚未创建,update 操作会触发异常。如果你的代码没有捕获事务异常、仅判断成功回调,或者异常被重试逻辑覆盖,就可能出现预期外的表现。 - 安全规则偶发拦截:如果 daylogs 集合的安全规则存在动态校验逻辑(比如基于请求时间、请求字段内容、用户权限的动态判断),偶发触发规则拦截时,事务本应整体失败,但若未配置失败监听,会误以为操作成功。
- 本地离线缓存的回调差异:Firestore 默认开启离线持久化,写入操作会先写入本地缓存并立刻触发成功回调,后台再异步同步到服务端。如果同步时 daylogs 写入因冲突、规则拦截等原因失败,不会回溯触发之前的成功回调,就会出现「本地提示成功、但服务端 daylogs 无数据」的情况。
- 单文档并发冲突:如果 daylogs 按天/小时生成公共文档ID,大量事务同时修改同一个 daylogs 文档时,会触发高频冲突,事务重试过程中可能出现偶发的写入失效。
对应解决方案
- 替换写入方法:将 daylogs 的 update 操作改为支持文档不存在场景的 set 合并写入,避免因日志文档不存在导致的写入失败,代码示例:
transaction.set(logs, data, SetOptions.merge()) - 补全异常监听:给事务添加完整的失败、取消监听,记录异常信息排查根因,示例代码:
.addOnFailureListener(e -> { // 记录异常code、message,排查是权限问题、冲突问题还是参数问题 Log.e("FirestoreTransaction", "write failed", e); }) .addOnCanceledListener(() -> { Log.w("FirestoreTransaction", "write canceled"); });
- 避免单文档并发过高:调整 daylogs 的文档ID生成逻辑,比如在按天生成的ID后添加用户ID、随机后缀,或者将单条日志作为子文档存入当天的日志集合下,降低单文档的写入冲突概率。
- 确认服务端写入成功再提示:如果业务要求强一致性,可以在事务返回成功后,主动拉取三个文档的服务端快照确认全部写入完成,再给用户弹出成功提示:
// 示例:获取服务端快照确认 thisProduct.get(Source.SERVER).addOnSuccessListener(doc -> { // 同理校验stock、daylogs的服务端数据存在后再提示成功 });
- 无读取逻辑的场景可改用批量写入:如果你的事务不需要先读取文档数据再写入,纯写入场景下 batched writes 比事务更稳定,同样支持原子性,不会出现部分写入的情况。
内容的提问来源于stack exchange,提问作者Sushang Nirola
相关产品推荐
相关产品推荐

