跟踪:#191
问题
三个相对独立的 follow-up,单个 PR 都不大,但都属于"长期债",合并到一个 tracking issue 里便于分配。
A. 域名前缀 trie 替换 HashMap/HashSet
现状(src-tauri/crates/mhost-dns/src/adblock.rs:105,2026-09-15 更新行号):规则以 HashMap<String, IpAddr> / HashSet<String> 存储,每个域名 String 24 字节指针 + len + capacity,100k 规则 ≈ 50 MB 堆。注意 #205 已把单源硬顶提到 500k 规则、响应上限 64MB,大列表源的内存压力比本文写作时更现实。
Arc::swap 时旧 snapshot 再驻留一会儿,高峰期内存压力明显。
建议:换成 reversed-domain trie(按 label 反序建树,com → example → ads → tracker)。同 100k 规则压到 5–10 MB,lookup 从 O(走链 × 哈希) → O(域名 label 数)。
验收:
- 替换
RulesSnapshot 内部表示
- 公开 API(
rebuild / check / rule_count / *_count)不变
walk_parents 逻辑可以保留(用 trie 的 descend 替代 HashMap lookup),或者改成 trie 自身支持 "find longest suffix prefix match"
- benchmark:
cargo bench 100k 规则下 lookup p99 < 1µs
- 现有所有
AdBlockEngine 单测不动
工作量:3–5 天。
B. ad-block 命中率 / 刷新耗时指标
现状:AdBlockEngine 只暴露 rule_count / *_count,没有命中率、没有刷新成功率。
建议:
AdBlockEngine 加 AtomicU64:hits_zero_addr / hits_nxdomain / hits_whitelist / misses
- 每个 source 加
last_refresh_duration_ms / last_refresh_success_at / last_refresh_failed_at
- 新增 IPC
get_ad_block_stats() -> AdBlockStats
AdBlock.tsx 加一个 "Stats" 折叠面板:今日命中次数、最后一次刷新耗时、源失败率
验收:
check 返回 Some 时 hits_* 累加
check 返回 None 且 master switch on 时 misses 累加(不要把 whitelist 命中算 miss)
- 周期刷新任务在 fetch 前后记时间戳、写回 source
- 单测:100 次查询混合 whitelist + block + miss,断言计数准确
工作量:1 天。
C. 禁用源 cache 文件清理
现状:用户禁用一个 source 后,adblock-cache/{id}.txt 仍占空间。purge_source(src-tauri/crates/mhost-storage/src/adblock.rs:243,2026-09-15 更新行号)只在源被删除时清 cache,禁用不清。
2026-09-15 补充:现在已有 sweep_orphan_caches(同文件 ~L204)清扫不在 state.sources 中的孤儿 cache 文件(覆盖已删除的源),但禁用源仍在 state.sources 里、cache 不受影响——本项仍按原计划有效,实现时勿重复造清扫。
建议:
- 二选一:
- A. 禁用即清:source 切到 disabled 时调
delete_cache,下次 enable 时自动重 fetch
- B. 禁用不清:但
add_ad_block_source_impl 列表超过 N 条时 LRU 清理最久未刷新成功的 cache
- 优先 A(行为更可预测,但需要确保切回 enabled 时主动 refresh)
验收:
- 选 A:
set_ad_block_source_enabled(false, _) 末尾 delete_cache;切回 enabled 时 set_ad_block_source_enabled(true, _) 末尾调一次 fetch_and_cache_source
- 选 B:在
mhost_storage 里加 prune_idle_caches(root, keep_n: usize),周期任务跑前调一次
工作量:0.5 天。
优先
P3。这三项都属于"等真出问题再做也不迟"的优化。但 trie 那项可以在跑大列表源(>200k 规则)用户反馈内存时立刻启动。
涉及文件
src-tauri/crates/mhost-dns/src/adblock.rs(trie + counters)
src-tauri/crates/mhost-storage/src/adblock.rs(cache 清理)
src-tauri/src/commands/adblock.rs(stats IPC)
src-tauri/src/commands/dns.rs(refresh 耗时记录)
src/lib/tauri.ts + src/stores/profiles/actions.ts + src/pages/AdBlock.tsx(前端 stats 面板)
跟踪:#191
问题
三个相对独立的 follow-up,单个 PR 都不大,但都属于"长期债",合并到一个 tracking issue 里便于分配。
A. 域名前缀 trie 替换 HashMap/HashSet
现状(
src-tauri/crates/mhost-dns/src/adblock.rs:105,2026-09-15 更新行号):规则以HashMap<String, IpAddr>/HashSet<String>存储,每个域名String24 字节指针 + len + capacity,100k 规则 ≈ 50 MB 堆。注意 #205 已把单源硬顶提到 500k 规则、响应上限 64MB,大列表源的内存压力比本文写作时更现实。Arc::swap时旧 snapshot 再驻留一会儿,高峰期内存压力明显。建议:换成 reversed-domain trie(按 label 反序建树,
com → example → ads → tracker)。同 100k 规则压到 5–10 MB,lookup 从 O(走链 × 哈希) → O(域名 label 数)。验收:
RulesSnapshot内部表示rebuild/check/rule_count/*_count)不变walk_parents逻辑可以保留(用 trie 的descend替代 HashMap lookup),或者改成 trie 自身支持 "find longest suffix prefix match"cargo bench100k 规则下 lookup p99 < 1µsAdBlockEngine单测不动工作量:3–5 天。
B. ad-block 命中率 / 刷新耗时指标
现状:
AdBlockEngine只暴露rule_count/*_count,没有命中率、没有刷新成功率。建议:
AdBlockEngine加AtomicU64:hits_zero_addr/hits_nxdomain/hits_whitelist/misseslast_refresh_duration_ms/last_refresh_success_at/last_refresh_failed_atget_ad_block_stats() -> AdBlockStatsAdBlock.tsx加一个 "Stats" 折叠面板:今日命中次数、最后一次刷新耗时、源失败率验收:
check返回 Some 时hits_*累加check返回 None 且 master switch on 时misses累加(不要把 whitelist 命中算 miss)工作量:1 天。
C. 禁用源 cache 文件清理
现状:用户禁用一个 source 后,
adblock-cache/{id}.txt仍占空间。purge_source(src-tauri/crates/mhost-storage/src/adblock.rs:243,2026-09-15 更新行号)只在源被删除时清 cache,禁用不清。2026-09-15 补充:现在已有
sweep_orphan_caches(同文件 ~L204)清扫不在state.sources中的孤儿 cache 文件(覆盖已删除的源),但禁用源仍在state.sources里、cache 不受影响——本项仍按原计划有效,实现时勿重复造清扫。建议:
delete_cache,下次 enable 时自动重 fetchadd_ad_block_source_impl列表超过 N 条时 LRU 清理最久未刷新成功的 cache验收:
set_ad_block_source_enabled(false, _)末尾delete_cache;切回 enabled 时set_ad_block_source_enabled(true, _)末尾调一次fetch_and_cache_sourcemhost_storage里加prune_idle_caches(root, keep_n: usize),周期任务跑前调一次工作量:0.5 天。
优先
P3。这三项都属于"等真出问题再做也不迟"的优化。但 trie 那项可以在跑大列表源(>200k 规则)用户反馈内存时立刻启动。
涉及文件
src-tauri/crates/mhost-dns/src/adblock.rs(trie + counters)src-tauri/crates/mhost-storage/src/adblock.rs(cache 清理)src-tauri/src/commands/adblock.rs(stats IPC)src-tauri/src/commands/dns.rs(refresh 耗时记录)src/lib/tauri.ts+src/stores/profiles/actions.ts+src/pages/AdBlock.tsx(前端 stats 面板)