mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-21 11:25:03 +00:00
ChannelMonitorQuotaFetcher.Fetch 顶部的 cachedSnapshot 与随后的 flight.DoChan 之间存在一个窗口:goroutine A 判定缓存未命中之后、真正入队之前,goroutine B 的 flight 可能已经跑完、写好缓存,并且它的 singleflight key 也已被摘掉。A 于是不会 并入 B 的那次飞行,而是另起一个新 flight,对同一账号再打一次上游——正是 singleflight 想要消除的重复查询。 多个监控可以关联同一账号,interval 最小 15s,国产配额服务自身无缓存, 这条路径的全部意义就是防止打爆上游配额端点(见文件头注释)。 修复:把 flight 执行体提为具名方法 fetchShared,开头重查一次缓存。 这次重查必然命中,所以合并是确定的而非尽力而为:storeSnapshot 发生在执行体 返回之前,singleflight 删 key 发生在返回之后,因此「能新起一次飞行」必然蕴含 「上一次的快照已经可见」。 该窗口也是主线 CI 上 TestQuotaFetcher_ConcurrentFetchesShareSingleFlight 间歇性失败(expected: 1, actual: 2)的成因,本地 -count=900 复现 3 次(约 0.33%), 带修复后 -count=1700 零失败。 新增两条确定性用例:既有并发用例只能以约 1/300 的概率撞上该窗口,故直接钉住 窗口本身——fetchShared 就是 flight 的执行体,直接调用它等价于「已越过顶层缓存 判定、拿到属于自己的那次飞行」这一状态。另一条守住重查不得退化成无条件短路: 缓存过期后必须照常回源。