chick 3a9eec5de2 fix(agent): 危险写限流改为滑动窗口,与中继侧同一算法
节点侧的限流器此前也是固定窗口(按自然分钟重置计数器),而注释声称的性质是
"任意 60 秒内至多 N 次危险写"——与中继侧那个缺陷完全相同,只是这一侧还没有
人测过它。注释同样以该性质为由选择了固定窗口,所以代码与自己的理由相反。

现在两端是同一个算法:保留被放行的写时间戳,窗口满时最早那笔离开窗口才放行。
这两个限流器从两侧约束同一个操作(节点自己、以及中继对设备的预算),一侧比
另一侧宽一倍是那种要等到事后复盘才会被发现的不一致,所以保持一致比任何一侧
单独正确更重要。

测试里有一处值得记下来:这个性质不能按"花完预算、过一分钟、再花一次"来测——
那样固定窗口也会通过,因为所有写都过期之后两种实现没有分歧。必须把预算花在
自然分钟边界**之前**,再断言边界之后一笔都放不进去。第一版就是这么写错的,
变异验证(把实现改回按自然分钟重置)没有失败,才发现测试本身没在测它。

内存代价同中继侧:每节点至多 N 个时间戳,而不是一个整数。

Go 全包、Node 112 项、Swift 101 项通过。
2026-09-17 01:46:32 +08:00
S
Description
Outbound-only node agent for SimAdmin nodes: long-polls a relay, verifies signed envelopes, and forwards to the local SimAdmin on loopback. Static Go binary, no third-party modules.
268 KiB
2026-09-18 12:39:58 +08:00
Languages
Go 96.8%
Shell 2.4%
Makefile 0.8%