flutter - 当 Internet 出现故障时,ServerValue.increment 无法正常工作
问题描述
添加ServerValue.increment()
(Add increment() for atomic field value increment #2437)是一个好消息,因为它允许在 Firebase RTDB 中自动增加字段值。
我有一个保存库存的应用程序,这个功能很关键,因为它允许更新库存,而不管用户是否有时离线。但是,我开始注意到有时该函数会执行两次,这完全以错误的方式错误地列出了库存。
为了隔离问题,我决定进行以下测试,这表明ServerValue.Increment()
当连接从联机到脱机时工作错误:
从
for loop function
1 到 200:for (var i = 1; i <= 200; i++) { testBloc.incrementTest(i); print('Pos: $i'); }
该函数
incrementTest(i)
必须增加两个变量:(position
从 1 中的 1 计数到 200)和sum
(加 1 + 2 + 3, ..., + 200 应该得到 20,100)Future<bool> incrementTest(int value) async { try { db.child('test/position') .set(ServerValue.increment(1)); db.child('test/sum') .set(ServerValue.increment(value)); } catch (e) { print(e); } return true; }
请注意, db 指的是 Firebase 实例 ( FirebaseDatabase.instance.reference()
)
随之而来的是测试:
测试 1:100% 在线。通过
该函数正常工作,将两个变量传递给正确的结果(在 Firebase 控制台中):
职位:200
总和:20100
测试 2:100% 离线。通过
为此,我在飞行模式下使用了物理设备,然后执行了for loop function
,当函数完成执行后,我停用了飞行模式并在 firebase 控制台中检查了结果,结果令人满意:
职位:200
总和:20100
测试 3:开始在线,然后转到离线。失败的
这是Internet 连接断开时的典型操作场景。更糟糕的是,当连接断断续续时,您正在地铁上旅行,或者您处于需要离线持久性的低覆盖率站点。为了模拟它,我所做的是for loop function
在在线模式下运行,在它完成之前,我将物理设备置于飞行模式。后来我上网完成了测试,并在 Firebase 控制台上查看了结果。在所有情况下,获得的结果都是不正确的。以下是一些结果:
如您所见,增量错误地重复了 10、18 和 9 次。
我怎样才能避免这种行为?
有没有其他方法可以在 Firebase 中自动增加一个可以在线/离线正常工作的数字?
解决方案
火力基地在这里
这是增量行为中一个有趣的边缘情况。客户端和服务器之间都无法确定增量是否已执行,因此最终会在重新连接时从客户端重试。据我所知,这个问题只会发生在增量操作中,因为除了事务之外,所有其他写操作都是幂等的,但那些在离线时不起作用。
可以确保每个增量只发生一次,但这需要一些工作:
- 首先,添加一个 nonce 以唯一标识此操作的写入操作。您可以为此使用按键,但任何其他 UUID 也可以正常工作。将此与您的原始
set()
调用组合成一个多路径update
调用,将 nonce 写入顶级节点,并将服务器端时间戳作为其值。 - 现在在顶级位置的安全规则中,仅在没有现有数据时才允许写入。这可以确保您看到的辅助写入被拒绝,并且由于在整个多路径更新中检查安全规则,错误的增量也将被拒绝。
- 您可能希望根据其中的时间戳值定期使用 nonce 键清理节点。这对性能无关紧要(因为您在清理期间从不在这里搜索),但可能有助于控制随机数的存储成本。
我还没有将这种方法用于这个特定的用例,但已经为其他人使用过。如果您包含客户端重试,则上述内容实质上构建了您自己的多路径事务机制,这是我过去需要的。但是由于您在这里不需要它,所以没有它会更简单。
推荐阅读
- node.js - 节点 | 解压缩并只读前 n 行文件
- r - 在 R 中重复 1000 次的正态性检验:Shapiro Wilk、Jarque Bera、Lilliefors
- vue.js - Vue.js getElementById() targets wrong instance of component
- javascript - 我可以使用 AWS lambda 作为 WebSocket(AWS IoT 堆栈)的客户端吗?
- python - Keras CNN val_accuracy、loss、accuracy
- python - Python内存管理的困惑
- regex - 正则表达式:浮点数与非数字符号的完全匹配
- nginx - unknown directive "state" in nginx 1.17.7
- database - DB21015E The Command Line Processor backend process request queue or input queue was not created within the timeout period
- go - time.Location() returns as "Local" even if /etc/localtime is correct