8.8 KiB
8.8 KiB
id, title, phase, deps, status, created, issue, context_ref, claim_branch, work_branch, write_paths
| id | title | phase | deps | status | created | issue | context_ref | claim_branch | work_branch | write_paths | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| T-606 | 收紧图标缓存并发与内存边界 | 2 |
|
DONE | 2026-07-17 | null | 8873a5261d |
null | agent/codex/T-606 |
|
问题 / 背景
Phase 2 交叉审核确认,T-204 的 IconCache.Load 从取得全局 mutex 到返回全程持锁,锁内包含磁盘读取、图片校验、远端 Fetch 和磁盘原子写。一个慢图标会阻塞所有其他 key,包括本应立即返回的内存命中;简单地把 Fetch 移出锁又会使同一 key 重复下载并竞争写盘。
现有 IconFetcher 直接返回完整 []byte,IconCache 只能在 Fetch 完成后检查 2 MiB 上限。该检查能阻止超大对象进入缓存,却不能阻止未来网络 Fetcher 先下载并分配任意大响应。与此同时,IconCache.memory 没有容量/LRU 上限,modern/win7 的 shell.icons 也不会在 Catalog 移除 app 后剪枝,长期运行时内存只增不减。
当前 IconCache.Load 尚无生产调用者,T-204 也明确把真实图标分发映射与网络装配留给后续,所以这些不是已经复现的滚动验收失败。它们仍是正式图标后台加载接入前必须关闭的并发与资源边界。
方案
- 把
IconFetcher调整为可有界读取的流式响应合约,由IconCache掌握读取边界与关闭责任:- 响应提供
io.ReadCloserbody 和可选的声明长度;未知长度使用明确哨兵值。 - 声明长度超过
maxBytes时在读取 body 前拒绝。 - 对所有响应使用
io.LimitReader(maxBytes+1)或等价逻辑;实际读到上限加一字节时返回ErrIconTooLarge。 - 成功、校验失败、读取失败、context 取消等所有路径都关闭 body,并保留关闭错误的既定错误优先级。
- 不在本任务猜测 Catalog 内容哈希到 URL/DPI 变体的映射,也不实现具体发布端协议;后续 HTTP 适配器必须使用该流式合约。
- 响应提供
- 为
IconCache建立 Go 1.20 兼容的按 cache key in-flight 去重:- 全局 mutex 只保护 memory/LRU/in-flight 元数据,不横跨磁盘、网络、图片解码或磁盘写入。
- 同一 digest+DPI 只允许一个 leader 执行 disk→fetch→validate→store,其余调用者等待并复用结果。
- 不同 key 可以并行,慢 key 不阻塞另一 key 的 memory hit。
- follower 的 context 取消只停止自身等待,不得取消 leader;leader 失败/取消后发布同一结果、移除 flight,后续调用可重试。
- 每个调用者取得独立的结果字节副本,不能修改 cache 或其他等待者观察到的内容。
- 将内存层改为按 key 的 LRU,同时限制总字节与条目数:
- 默认上限冻结为 32 MiB、256 个 key;命中提升 recency,插入/替换时更新准确字节计数并持续淘汰最旧项直到两个上限同时满足。
- 同一 digest 的不同 DPI 仍是不同 key;磁盘缓存与现有内容校验/原子写语义不变。
- LRU 操作与 in-flight 完成发布在同一同步纪律下,不得产生负计数、重复 list 节点或返回已被复用的内部切片。
- modern/win7 的
AppShell.SetItems按新 Catalog app ID 集合剪枝shell.icons,保留仍存在 ID 的paint.ImageOp;与现有 rows/categories 剪枝语义对齐。 - 扩展并发、流边界、LRU 与双 Gio 测试,使用 channel/barrier 等确定性同步,不以
time.Sleep猜测并发时序。 - 同步图标缓存协议、架构与编码规则,明确“内存→磁盘→流式 Fetcher”的边界、内存容量、按 key 去重以及真实 UI 接入仍须走 application event/UI goroutine。
验收要点
- 一个阻塞的远端 key 不会阻塞另一 key 的内存命中;两个不同远端 key 能实际重叠执行。
- 同一 key 的并发 miss 只执行一次 Fetch 和一次成功存储,所有等待者获得内容相同但 backing array 独立的结果。
- follower context 取消能及时返回且不影响 leader;leader 失败或取消后 in-flight 被清理,下一次调用可以重新 Fetch。
- 声明
Content-Length超过 2 MiB 默认上限时不读取 body;未知/伪造较小长度但实际 body 超限时最多读取maxBytes+1;所有路径关闭 body。 - 哈希不符、非法图片、磁盘损坏在线修复、离线读取已验证磁盘缓存、DPI 分键和非致命磁盘写 warning 的既有语义保持通过。
- 默认 memory LRU 同时受 32 MiB 与 256 key 限制;命中提升顺序,替换正确更新字节数,淘汰后可从磁盘/远端重新装入。
- modern/win7
SetItems删除 app 后对应shell.icons项消失,保留 app 的 ImageOp 不变;rows/categories 既有稳定性测试继续通过。 - 并发测试使用确定性同步并可重复运行;若当前工具链支持 race detector,执行
go test -race并记录结果,不把 race 支持作为 Win7 交叉构建的前提。 - Go 1.20.14 +
GOWORK=off下go vet ./...、go test -count=1 ./...通过。 - modern 根 workspace 与 win7 独立 workspace 的
ui/gio测试和 Windows amd64 构建通过;./scripts/verify_phase0.ps1全绿。 python scripts/validate_agent_context.py、python scripts/validate_harness_governance.py与提交前差异检查通过。
边界(不改什么)
- 不决定
sha256:图标引用到下载 URL、CDN 路径或 DPI 变体的发布端映射,不增加未经协议冻结的 Catalog 字段。 - 不把
IconCache正式装配进应用启动或列表滚动链路;后台事件→UI goroutine→ApplyIcon/Invalidate 的接线按审核顺序另立后续任务。 - 不补 modern/win7 完整适配器交互契约测试,不修改
VisibleItems快照 API,不拆分shell.go;这些按审核后续顺序分别处理。 - 不对磁盘图标缓存实现 LRU/容量清理,不自动删除或 quarantine symlink/reparse point;
ErrIconCacheUnsafe继续 fail closed。 - 不引入
x/sync/singleflight或其他第三方缓存库,不升级 Go/Gio,core 与 win7 继续保持 Go 1.20 兼容。 - 不处理 ZIP 中央目录预扫描、断电耐久、Catalog 签名向量或 T-302 安装整合。
- 不修改、提交或删除用户的
soft_quay.code-workspace。
协作约束
- 按仓库当前规则由单 Agent 串行执行,不启动子 Agent。
- 本任务只允许修改 frontmatter 中的
write_paths;若流式 Fetcher 需要新增公共协议字段或真实 URL 规则,必须停止并记录,不得在 T-606 内猜测协议。 - T-606 完成、完整验证并提交前,不落成或领取 Phase 2 审核顺序中的后续任务。
执行记录
- 2026-07-17:根据
docs/review/phase2-review.md交叉复核定稿的第一优先级整改落成任务;现有全局最大任务为 T-605,因此取 T-606。 - 2026-07-17:任务合并收口按 key in-flight、流式有界读取、memory LRU 与双 shell 图标剪枝;UI 线程投递、适配器契约、VisibleItems 快照和文件拆分继续按审核顺序串行拆分。
- 2026-07-17:在
agent/codex/T-606分支领取任务,基线为8873a5261db76b30db30521197e9ec898ca86077;保持单 Agent 串行执行。 - 2026-07-17:
IconFetcher改为返回 context-boundio.ReadCloser与可选声明长度;IconCache在分配完整响应前拒绝超大声明并只读取maxBytes+1,成功、超限、取消和校验失败路径统一关闭 body。未增加 URL/CDN 字段或具体 HTTP 映射。 - 2026-07-17:
IconCache全局 mutex 收缩为只保护 memory/LRU/in-flight 元数据;同 digest+DPI 使用单 leader,不同 key 的磁盘/网络工作可并行,follower 可独立取消,leader 失败后 flight 清理并允许重试。每个调用者与 LRU 都持有独立字节副本。 - 2026-07-17:新增默认 32 MiB/256-key 的双上限 LRU,覆盖命中提升、替换计数、条目/字节淘汰、禁用边界与淘汰后 verified disk 重载;modern/win7
SetItems同步剪枝已移除 app 的 ImageOp 并保留现存 app 图标。 - 2026-07-17:定向验证通过:Go 1.20.14
go vet ./catalog、go test -count=20 ./catalog;modern/win7go test -count=5 ./ui/gio。并发用例使用 channel/barrier 与 2 秒仅防挂死超时,覆盖跨 key 并行、memory hit 不受慢 key 阻塞、同 key 单 Fetch、follower/leader 取消、flight 重试、body 最大读取量和独立 backing array。 - 2026-07-17:尝试 Go 1.20.14
go test -race -count=1 ./catalog;当前 Windows 环境缺少 GCC(cgo: C compiler "gcc" not found),race detector 不可用。按任务边界记录限制,未把它伪报为通过;确定性并发测试连续 20 次通过。 - 2026-07-17:完整
./scripts/verify_phase0.ps1通过,包含治理/上下文/边界/版本校验、Go 1.20.14 core vet/test、modern Go 1.25 与 win7 Go 1.20.14 的 UI/平台测试及 Windows amd64 构建。