☰
wenyi文译Web工作进程故障恢复指南:中断任务如何自动标记与续跑,进度永不丢失
2026/10/1 13:21:41 网站建设 项目流程

wenyi文译Web工作进程故障恢复指南:中断任务如何自动标记与续跑,进度永不丢失

【免费下载链接】wenyi将被语言阻隔的作品,带到读者的语言中。Bringing literature into your language.项目地址: https://gitcode.com/gh_mirrors/we/wenyi

wenyi(文译)是一个开源的长文本翻译 Web 引擎,能把被语言阻隔的书籍、字幕等作品带到读者的语言中。整本书的翻译动辄运行数小时,网络波动、服务重启都可能导致后台工作进程意外停止。本文将完整讲解 wenyi 的故障恢复机制:中断任务如何被自动标记、已完成的进度为何永不丢失,以及如何一键续跑从断点继续,帮助新手安心运行长时间翻译任务。

一、为什么长任务会中断?中断后会发生什么

wenyi 的 Web 端采用"API + 后台工作进程"的架构:前端发起翻译后,真正的解析、翻译、审校、导出工作都交给独立的后台工作进程(基于 Redis 队列异步执行,工作流与导出各用一条队列)。

长任务运行期间,可能遇到这些情况:

  • 服务器重启、进程被杀(kill、OOM 等);
  • 队列服务(Redis)短暂不可用,任务"卡"在队列里无人执行;
  • 工作进程崩溃,但数据库里的任务状态还停留在"运行中"。

如果什么都不做,界面会永远显示"翻译中",进度和已花费的 Token 全部变成一笔糊涂账。wenyi 的做法是:让状态可信、让进度落盘、让续跑一键可达。

如上图所示,wenyi 的翻译总览页会记录每一次运行的时长与状态(完成、失败、暂停),这是理解"故障恢复"的直观入口:每次中断和续跑,都会在这里留下可追溯的痕迹。

二、自动标记:每 30 秒巡检一次的中断检测

核心逻辑位于 recovery.py。工作进程启动时会拉起一个恢复循环(见 workers/__init__.py),它独立于业务任务运行,每 30 秒做一次巡检:

  1. 找出"疑似中断"的任务:扫描数据库中状态为"排队中/运行中"、且超过 2 分钟没有任何更新的任务。2 分钟的宽限期是为了避免误伤"刚从数据库写入、还没进队列"的正常任务。
  2. 用会话锁确认进程已死:每个项目的工作进程执行期间都持有 PostgreSQL 的会话锁。锁还在 → 进程大概率还活着,跳过;锁拿不到 → 说明原工作进程已经不存在,即使 Redis 里还残留着"进行中"的标记,也判定为中断。
  3. 区分任务类型,打上正确的状态:
    • 翻译/审校等工作流任务→ 标记为interrupted(已中断),项目状态置为paused(已暂停),并写入task_interrupted事件日志;
    • 导出任务→ 标记为error(错误),提示"导出未完成,请新建导出重试",因为渲染结果没有断点价值。

这套"时间戳 + 会话锁"的双重判断非常关键:它既不会把还在跑的任务误判为死任务,也不会让死任务永远挂在"运行中"。

💡 为什么是 2 分钟宽限期?因为任务从写入数据库到真正入队,中间存在正常的间隔。短于这个时间就巡检,可能误判;长太多,则状态"失真"的时间过长。

三、进度为何永不丢失:章节级断点与原子写入

自动标记只是"止血",真正的底气来自进度从落盘那一刻起就是完整的。wenyi 的核心运行状态由 runstore.py 管理,每本书拥有独立的状态目录:

文件作用中断保护
manifest.json章节清单与每章完成状态原子写入,中断不会留下半截文件
chapters/ch{n}.json每章已翻译的原文与译文逐章落盘,翻译完一章存一章
usage.json累计 Token 用量用量先写入"暂存账本",续跑时自动补记
events.jsonl追加式事件日志只增不改,完整记录每次运行

三个设计让"永不丢失"成立:

  • 章节粒度断点:每完成一章就原子更新manifest.json(先写临时文件再原子替换,见 _write_json),进程即使瞬间被杀,磁盘上也只有"完整的上一章"或"完整的下一章",没有中间态。
  • 原文指纹校验:初始化时记录源文件的 SHA-256(source_sha256),续跑前会校验文件没被改动,防止"拿着旧进度翻译新文件"。
  • 配置快照随任务持久化:任务入队时会把当时的完整配置快照存入任务参数(见 job_service.py),续跑复用快照,而不是读取可能被改过的当前配置,保证"续的就是原来那一段"。

四、一键续跑:从断点继续的完整链路

当项目状态为paused、error或interrupted时,界面上会提供"续跑"操作。它对应 projects.py 的/resume接口,链路清晰:

  1. 通过 latest_resumable_job 找到该项目最近一次"可续跑"的任务(状态为暂停/错误/中断的最新工作流任务);
  2. 带着原始任务参数(含配置快照)重新入队;
  3. 新工作进程启动后,从manifest.json读取已完成章节,跳过所有已翻译章节,直接从第一个未完成的章节继续;
  4. Token 用量在usage.json上累加,不会重复计费。

此外还有两道"防串线"保险:

  • 旧任务投递不可覆盖新工作:如果 Redis 里残留着旧任务的投递,执行前会先核对"当前最新任务是否还是自己"(见 tasks.py),不是则直接丢弃,避免幽灵任务覆盖你的新进度。
  • 暂停是"在安全边界停下":手动暂停不会硬杀进程,而是在章节边界优雅收尾并落盘(见 PauseRequested),所以暂停与崩溃后的恢复路径完全一致。

上图的人工校阅页显示"已保存 5/5 段"——这正是章节级断点的另一面:无论自动翻译进行到哪里,已落盘的内容都稳定可查,续跑时也不会重复翻译已完成部分。

五、给普通用户的实用建议

  • 中断后不要急着新建项目:先点"续跑"。除非源文件本身发生了变更(指纹校验会拒绝不一致的源),否则新建项目会丢失全部已完成的章节进度。
  • 导出中断需重新发起:导出没有断点概念,标记为错误后新建一次导出即可,不会消耗模型费用。
  • 关注事件日志:每次task_interrupted、task_paused、task_completed都会写入追加式事件日志(events.jsonl),需要排查"到底断在哪一步"时,它是第一手证据。
  • 部署时把 API、工作进程、数据库分开重启:恢复循环运行在工作进程中,只要工作进程能重新拉起,它会自动完成对历史中断任务的标记,无需人工干预。

小结

wenyi 的故障恢复体系由三块拼图构成:30 秒一次的自动巡检负责把"死掉的任务"从"运行中"诚实地改标为"已中断";章节级原子落盘 + 原文指纹 + 配置快照负责让进度在任何时刻都是完整、可信的;一键续跑负责从断点继续,不重复劳动、不重复计费。对使用者来说,它把"长任务怕中断"的焦虑,变成了一次无感的"继续"。

更多架构细节可参考项目文档:docs/zh/architecture.md、docs/zh/pipeline.md,核心源码位于 apps/api/wenyi_api/workers/ 与 packages/core/wenyi_core/pipeline/。

【免费下载链接】wenyi将被语言阻隔的作品,带到读者的语言中。Bringing literature into your language.项目地址: https://gitcode.com/gh_mirrors/we/wenyi

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询