生产 v0.3.8~v0.3.13 均由 8013196 加工作区修改构建部署,此前未提交;本提交补齐源码,
并含未部署的 v0.3.14 原地入库。中间各版本源码状态未保存,无法逐版拆分,详见
docs/plan/17-full-upgrade.md 与 docs/plan/18-organize-release.md。
- v0.3.8 全面升级:Deno workspace 依赖、Router 8 / Vite 8、camofox 1.18.0 / Node 24、
PostgreSQL 18.6、备份客户端 18.6、演示 qB 5.2.4
- v0.3.9 媒体整理:分类规则、整理来源与目录快照(0059)、文件管理整理入口
- v0.3.10 手动下载与入库:独立手动下载、详情页资源下载、TMDB / Local 元数据兜底
(metadata.resolve_organize)、正常多版本 / 一次性洗版(0060)
- v0.3.11 搜索详情与桌面黑边修复
- v0.3.12 列表分页、搜索历史、首页概况
- v0.3.13 统一整理弹窗与队列
- v0.3.14 原地入库(0061,未部署)
同批审计修复:
- 刮削入队:确认原记录存在后才删重放产生的新记录;写回 run 关联失败时保持 queued,
由处理器补关联(对齐 MP 先登记、只在入队失败时记失败)
- 识别升级链只在结果更确定时采用,不再用 no_match 顶替原有歧义候选
- bluray_dir 资产以原盘目录本身为刮削根(MP 同做法)
- metadata.resolve_organize 登记到入库排障 Skill 与能力地图
- db/src/types.ts 按迁移重新生成;contracts 整理动作枚举顺序与数据库对齐
- 测试:原地入库用例改为直接查记录表;domain 测试模板补 version
- 前端:通知铃铛弹层与用户菜单统一样式;站点卡片共用卡头、底栏贴底、分页间距
依赖升级证据只提交摘要文件,registry-snapshot.json 移至 .local/(SHA-256 记于文档)。
Co-Authored-By: Claude Opus 5.5 <[email protected]>
32 lines
5.7 KiB
Markdown
32 lines
5.7 KiB
Markdown
# 主线程解耦审计(2026-09-27,只读审计结论 → W4-D 修复清单)
|
||
|
||
> 历史审计:本文记录当时的问题和修复过程。当前 `GET /inventory/media-servers` 已在 `routes/library.ts` 实现多实例并行、单实例并发限制和总超时,末尾「串行 Emby」不再是当前遗留。v0.3.7 的状态与新的工程检查见 [开发情况基线](13-development-baseline.md) 和 [工程检查与调整建议](14-engineering-review.md)。
|
||
|
||
## 结论
|
||
生产容器 API 与 worker 分进程;RSS/快照/刮削/入库主链路走任务引擎 + outbox。漏网三类:
|
||
1. HTTP 请求内同步重活:`POST /library/reconcile`(全量 stat)、`POST /download-cache/cleanup`(删种删数据)、`POST /organization`(planImport 递归 walk)、`POST /capabilities/{id}/invoke` 中 release.search/release.inspect/identity.resolve/catalog.lookup(远程站点/TMDB)、`GET /inventory/media-servers`(串行 Emby)。
|
||
2. acquisition-organize 后台循环(index.ts)绕过引擎直接调 `submitDownload`、`executeRecord`(Copy/Move)、`cleanupDownloadCache`、`reconcileLibrary`,串行 await;无租约/attemptToken 保护;一次大复制卡住 qB 同步与阈值暂停。
|
||
3. worker 并发度固定 4(openworkflow-engine.ts),runAgent 整段会话占 1 槽数分钟,与 verifyAndImport 大文件复制抢同一池。
|
||
事件循环丢租约风险低(复制/哈希流式让出;租约 30s/续租 15s);强制 sha256 路径(目标已存在、跨设备恢复)会全量读两遍。
|
||
|
||
## 修复清单(W4-D)
|
||
- P0 后台循环只编排:sweepPendingImports → enqueue verifyAndImport;sweepReservedPlans → enqueue submitDownload;超阈值清理与 6h 对账 → enqueue(幂等键按时间槽);tick 只剩 syncDownloads + 阈值判断,qB 同步独立定时器。
|
||
- P0 `/download-cache/cleanup`、`/library/reconcile` 改入队返回 {taskId, runId}(enqueueTracked);前端经任务页/SSE 追踪。
|
||
- P1 `WORKER_CONCURRENCY` 配置;worker 分组:`--role worker`(io 组:除 runAgent/resolveDecision 外,并发 4)与 `worker:agent`(并发 1–2),`startWorker` 两次各带 versions;模块 start() 循环只在 io 组;entrypoint 起三进程。
|
||
- P1 capabilities invoke 分级:外部 IO 型能力在 HTTP 路径强制 `AbortSignal.timeout(15s)` + 按站点/TMDB 限并发,或改入队返回 taskId;`POST /organization` dryRun 的 walk 加文件数上限。
|
||
- P2 fs/copy.ts:highWaterMark 1MiB;强制 sha256 全量比对改子进程 `sha256sum`(仿 ffprobe.ts 的 Deno.Command);executeRecord 打点。
|
||
- P2 worker 事件循环延迟探针(setInterval 偏差 >5s warn)。
|
||
|
||
## W4-D 修复状态(2026-09-27)
|
||
| 项 | 状态 | 说明 |
|
||
|---|---|---|
|
||
| P0 后台循环只编排 | 已完成(fe9693d) | `acquisition-organize/index.ts` 拆成两个自调度定时器:`sync`(syncDownloads + 阈值暂停/恢复)与 `plan`(编排)。超阈值 → 入队 `cleanupDownloadCache`(业务键 `threshold:<5min 槽>`,上一轮 pending/running 时跳过);`sweepReservedPlans` → 入队 `submitDownload`(业务行 `plan:<id>`,引擎键 `…:<5min 槽>`,上次 run 仍活跃则跳过);`sweepPendingImports` → 入队 `verifyAndImport`(业务行 `import:<dtId>`,同上);6h 对账 → 入队 `reconcileLibrary`(`auto:<6h 槽>`)。顺带修正:补入库巡检改按 `completed_at` 选(同步每轮刷新 `updated_at`,原条件永远选不中)。测试 `__tests__/background-loop.test.ts`:巡检只入队、50MB 入库(复制 + sha256 + 2.5s 慢 ffprobe)进行中 mock qB `torrents/info` 请求间隔 < 1s |
|
||
| P0 cleanup / reconcile 端点入队 | 已完成(fe9693d) | `POST /download-cache/cleanup`、`POST /library/reconcile` 改 `enqueueTracked` 返回 `{taskId, runId}`(202,幂等键按分钟槽);前端"立即清理"提示任务 id 并跳 `/console/engine/<runId>` |
|
||
| P1 worker 分组 + 并发配置 | 已完成(a9cee69、473c5e9、3792868) | `WORKER_CONCURRENCY`(4)/`WORKER_AGENT_CONCURRENCY`(2);`--role worker`(io 组 + relay + 模块循环)、`--role worker:agent`(runAgent/resolveDecision)、`--role all`(两池同进程);`@umt/tasks` 带 versions 的 worker 在认领 SQL 上按任务名过滤(OpenWorkflow 原生认领不看任务名,误认领会按 5s→5m 退避)。entrypoint 三进程互相看护。测试 `packages/tasks/test/worker-groups.test.ts`(去掉过滤即失败) |
|
||
| P1 capabilities invoke 分级 | 已完成(473c5e9) | `modules/agent/invoke-limits.ts`:release.search(实时搜索)/release.inspect(refreshDetail)/identity.resolve/catalog.lookup(includeRemote)在 HTTP 路径 15s 超时(502)+ 站点并发 2 / TMDB 并发 4(排队超时 429);许可在底层调用真正结束后才归还 |
|
||
| P1 `POST /organization-records` walk 上限 | 已完成(f9c20c6) | `ORGANIZE_WALK_MAX_FILES`(5000);dryRun 超出返回 `truncated: true`(计划只含前 N 个),正式执行 `too_many_files` 失败 |
|
||
| P2 copy highWaterMark / sha256 子进程 / 打点 | 已完成(f9c20c6) | 读写流 1 MiB;强制全量 sha256(复制后回读、目标已存在比对、跨卷恢复、计划期同尺寸目标)走 `Deno.Command("sha256sum")`,不可用(未安装 / 无 --allow-run)回退进程内流式,结果记 `hashVia`,`sha256Stats()` 计数;executeRecord 每文件打 `organize.transfer` 日志(耗时/字节/MB/s/hashVia) |
|
||
| P2 worker 事件循环延迟探针 | 已完成(473c5e9) | 1s 采样,偏差 > 5s warn + 计数,随心跳事件发 `loopLagWarnings/maxLoopLagMs` |
|
||
| 打包遗留 | 已完成(f6457a2、3792868) | 镜像带静态 ffprobe(mwader/static-ffmpeg:9.0.2),编译 `--allow-run=ffprobe,sha256sum`;`settings.agent.sessionDir`(null = DATA_DIR/agent-sessions)进设置页;镜像压缩 107→160.5 MB(ffprobe 层 135 MB 未压缩);umtw4d 临时部署验证 health、三进程、ffprobe/sha256sum 可调用 |
|
||
| 未做 | 遗留 | `GET /inventory/media-servers` 串行 Emby 仍在请求内(审计列出但不在 W4-D 清单);invoke 超时后底层调用继续跑完(handler 不全认 signal) |
|