fix(speedtest): 覆盖感知并发编排,杜绝多入口子集测速静默漏测/错测#277
Merged
Sway-Chan merged 4 commits intoJul 9, 2026
Merged
Conversation
根因:SpeedTestService.currentTest 单飞按「是否存在」无条件复用,不看在飞测速是否覆盖 本次请求的 serverIds。注释假设「双入口都全量、同一 set」——被 per-group 本组测速(子集)/ 单节点⚡/子集先-全量后 打破: - 不同分组连续点(A 跑时切 tab 点 B):B 撞 A 的 currentTest → B 拿 A 的节点结果、B 自己节点从未测 - 本组/单节点 先 → 全部 后:全部撞子集 currentTest → 只测子集、其余节点静默漏测,UI 却报「完成」 修复:currentTest 旁记 currentTestIds(在飞节点集)。新请求 set S: - S ⊆ 在飞集 → 复用同一次(零重跑、避免双临时 sing-box 端口冲突)——全部+本组子集/同 set/多入口全量 - 否则 → 串行链在在飞测速之后跑、用自身 set 完整测到——不同分组/子集先-全量后 各自被测、永不并发双 sing-box 不采「打断旧测速」:无 AbortController,中途 teardown 临时 sing-box + 释放探针端口本身有 race,比串行更糟。 新增 speed-test-coalesce.test.ts(桩 doTestAllServers):覆盖复用/未覆盖串行/子集先全量后/归零 4 例。 gate tsc + eslint + jest 2895 绿。
问题:多入口/快速连点不同分组时,每次 handleSpeedTest 各自弹独立 loading→success toast,
堆叠出 N 个「开始测速」+ N 个「测速完成」。
修复(不走禁用全部按钮的捷径、保留排队多组能力):新增 speed-test-toast 聚合协调器——
引用计数 + 固定 toast id,首个开始弹 loading、仍有在跑显剩余组数、全部结束才弹一次
success/error;跨 useSpeedTest 实例共享(模块级单例)。
- use-speed-test handleSpeedTest 改用 beginAggSpeedTest/endAggSpeedTest
- 新增 i18n servers.speedTestingCount「测速中 · {{count}} 组」(5 locale,parity 1475)
- speed-test-toast.test(mock sonner):单组/多组并发/失败聚合/批次隔离/end 多于 begin 5 例
配合 dododook#277 后端串行链:后端保正确性(不漏测)、前端聚合保观感(不堆叠)。gate tsc+eslint+jest 2900 绿。
问题(用户反馈):快速连点不同分组时,排队组的按钮显当前在跑那组的数字(串台);
「共 N 组」在「本组A + 全部」场景语义不对等。
根因:进度是单条全局 IPC 流(无 test 标签),被多个 per-hook speedProgress 消费,后端串行
→ 所有按钮镜像当前在跑那次的数字。非 A/B 片面,任意 2+ 并发 handleSpeedTest 皆串。
修复:
- 进度收敛到聚合 toast 唯一权威:coordinator 订 per-node result 事件,以「已测唯一节点 /
全部请求节点并集」显进度。并集去重(本组A+全部 → 并集=全部;coalesce B⊆A → 并集=A;
节点二测 serverId 去重)天然处理所有入口组合,无「N 组」歧义。
- 按钮去掉 tested/total 数字 → 仅「测速中」二态(串台源即那串数字);三处消费点(本组/全部/首页)统一。
- 单节点⚡并入聚合(连点不堆终端 toast),行内 spinner 仍由 testingServerIds 驱动。
- i18n speedTestingCount(组)→ speedTestingNodes「测速中 · {{tested}}/{{total}} 节点」(5 locale,parity)。
- coordinator 用相对路径 import api(jest 无 @/ mapper,别名致单测不可 mock)。
speed-test-toast.test 重写(mock result 订阅):单次/并集去重/本组+全部/失败聚合/批次隔离 5 例。gate 2900 绿。
- .nd-flag 从右上(top:-12px)移右下(bottom:-12px):避让首行右侧 SpeedBadge 延迟徽标 (原右上重叠遮挡测速结果,用户反馈)。国旗按名称映射(getCountryCode 正则表,自建/订阅/组网通用)。 - getCountryCode 台湾/台北/新北/tw/taiwan → 返 'tw'(显 🇹🇼)而非 'cn'(用户指定)。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
根因
SpeedTestService.currentTest单飞按「是否存在」无条件复用,不看在飞测速是否覆盖本次请求的serverIds。注释假设「双入口都全量、同一 set」——被 per-group 本组测速(子集)/单节点⚡/子集先-全量后 打破。穷举(入口 × 分组):
修复(覆盖感知 coalesce + 串行链)
currentTest旁记currentTestIds(在飞节点集)。新请求 S:S ⊆ 在飞集→ 复用同一次(零重跑、避免双临时 sing-box 端口冲突)不采「打断旧测速」:无 AbortController,中途 teardown 临时 sing-box + 释放探针端口本身有 race,比串行更糟。
测试
新增
speed-test-coalesce.test.ts(桩doTestAllServers):覆盖复用 / 未覆盖串行(B 用自身 set 拿自身结果)/ 子集先-全量后(全量测全集)/ 归零 4 例。gate tsc + eslint + jest 2895 绿。真机待验:快速切 tab + 测速中点他组/全部。