Files
Neo-ZQYY/docs/audit/changes/2026-03-22__zombie-task-graceful-shutdown-rerun.md
Neo 14a12342b5 chore(audit): 补追 96 份未入仓审计孤本 — 覆盖 2026-02-26 ~ 2026-04-08
这些审计记录原本堆积在 docs/audit/changes/changes/ 嵌套误产物目录下(由开发机迁移
79d3c2e 前后的不明批量操作产生)。由于同期 .gitignore 屏蔽了 docs/audit/ 全目录,
它们从未入过 git 任何分支 history。删除即永久丢失。

按 docs/specs/audit-gap-recovery/tasks.md 阶段 1 执行,将全部 96 份 D 类孤本
(主目录无同名、git history 亦无记录)复制到 docs/audit/changes/ 主目录入仓。

涵盖主题: P1-P18 全栈集成 / 多模块累积变更 / ETL bug 修复 / 业务日切 /
   召回与任务引擎改造 / 租户管理与审批 / 董事会财务 / 客户与助教详情 /
   DDL 基线合并 / Kiro 到 Claude Code 迁移

阶段 2(B 类内容漂移 1 份)和阶段 4(嵌套目录删除)独立推进。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 06:35:42 +08:00

68 lines
3.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 变更审计记录:僵尸任务修复 + 优雅关闭 + 重新执行按钮
| 字段 | 值 |
|------|-----|
| 日期 | 2026-03-22 |
| 触发原因 | 执行 ID bb9a0761 永远卡在 running 状态uvicorn 重启导致子进程丢失 |
## 操作摘要
解决后端重启时 ETL 子进程丢失导致 DB 记录永远停留在 running 的架构缺陷。新增优雅关闭3s 超时、启动时僵尸清理仅本机、interrupted 状态、重新执行按钮(所有历史任务)。
## 根因分析
`asyncio.create_task()` 启动的协程在 uvicorn 重启时被丢弃,`execute()``finally` 块未执行DB 中 `task_execution_log` 记录永远停留在 `status='running'``finished_at=NULL`
## 文件变更清单
### 后端
| 文件 | 变更类型 | 说明 |
|------|----------|------|
| `apps/backend/app/services/task_executor.py` | 修改 | 新增 `shutdown()` 优雅关闭方法terminate→wait→kill`recover_stale()` 启动僵尸清理、`execute()` finally 末尾添加 `cleanup()` 防内存泄漏 |
| `apps/backend/app/main.py` | 修改 | lifespan startup 调用 `recover_stale()`shutdown 调用 `shutdown(timeout=3.0)` |
| `apps/backend/app/routers/execution.py` | 修改 | 新增 `POST /api/execution/{id}/rerun` 端点 |
### 前端
| 文件 | 变更类型 | 说明 |
|------|----------|------|
| `apps/admin-web/src/api/execution.ts` | 修改 | 新增 `rerunExecution()` API 函数 |
| `apps/admin-web/src/pages/TaskManager.tsx` | 修改 | STATUS_COLOR 添加 interrupted、操作列添加重新执行按钮所有非 running 任务) |
### 数据修复
- 测试库 `bb9a0761` 记录已手动标记为 `interrupted`
## 关键设计决策
1. **优雅关闭 3s 超时**uvicorn 关闭后子进程无意义,快速终止
2. **僵尸清理仅限本机**:通过 `command LIKE '[hostname]%'` 匹配,多实例安全
3. **interrupted 状态**:区别于 cancelled用户主动取消和 failed执行出错
4. **rerun 用默认 config**DB 未存完整 TaskConfig仅保留 task_codes`api_full` + `increment_only` 默认配置重新执行
5. **cleanup 调用**execute() finally 末尾释放内存缓冲区,防止长期运行内存泄漏
## 风险评估
- shutdown 超时后强制 kill不会阻塞 uvicorn 关闭流程
- recover_stale 在 task_queue.start() 之前执行,不会与新任务冲突
- rerun 不保留原始 window/lookback 参数(已知限制,后续可扩展存储完整 config
## 回滚策略
revert 5 个文件即可。DB 中 interrupted 状态记录无副作用,无需回滚数据。
## 验证步骤
```sql
-- 1. 确认无僵尸任务
SELECT id, status FROM task_execution_log WHERE status = 'running';
-- 预期0 行(或仅有真正在运行的任务)
-- 2. 确认 bb9a0761 已修复
SELECT id, status, finished_at FROM task_execution_log
WHERE id = 'bb9a0761-95ff-4663-9053-9bb68b2603bb';
-- 预期status='interrupted', finished_at IS NOT NULL
-- 3. 确认 interrupted 状态存在
SELECT DISTINCT status FROM task_execution_log ORDER BY status;
-- 预期:包含 interrupted
```