fix(P0+P1): 孤儿任务清理 + 时间时区统一 #535

Merged
auto-approve-bot merged 5 commits from fix/p1-datetime-utc-timezone into develop 2026-07-18 19:58:08 +08:00
Owner

P0: 孤儿任务清理

worker 启动和任务调度时,自动清理超时未更新的 running 任务。

问题

worker 重启或崩溃后,数据库中 running 状态的任务变成孤儿,一直卡着不动,前端显示进度条卡住。

修复

  • GenerationTaskModel 新增 updated_at 字段(onupdate 自动刷新)
  • repo 新增 cleanup_stale_running 方法:status=running 且 updated_at > 10分钟 → 标记 failed
  • worker 启动时(on_worker_ready signal)自动清理一次
  • 任务调度时(generate_video 入口)也清理一次,防止其他 worker 崩溃留下的孤儿
  • error_message: "任务执行中断(worker重启/超时)"

P1: 时间时区统一

数据库 DateTime 列存 UTC 但不带时区标识,.isoformat() 输出无 Z 后缀,前端按本地时间解析,显示差 8 小时。

修复

  • 新增 format_utc_datetime 工具函数,统一输出带 Z 后缀的 ISO 8601 UTC 时间
  • 成片库 generated_at:改为带时区的字符串
  • 素材库 created_at:改为带时区的字符串
  • 任务列表 created_at/updated_at:从 datetime 类型改为带时区字符串
  • 前端 new Date() 自动转本地时间,无需额外处理
## P0: 孤儿任务清理 worker 启动和任务调度时,自动清理超时未更新的 running 任务。 ### 问题 worker 重启或崩溃后,数据库中 running 状态的任务变成孤儿,一直卡着不动,前端显示进度条卡住。 ### 修复 - GenerationTaskModel 新增 updated_at 字段(onupdate 自动刷新) - repo 新增 cleanup_stale_running 方法:status=running 且 updated_at > 10分钟 → 标记 failed - worker 启动时(on_worker_ready signal)自动清理一次 - 任务调度时(generate_video 入口)也清理一次,防止其他 worker 崩溃留下的孤儿 - error_message: "任务执行中断(worker重启/超时)" ## P1: 时间时区统一 数据库 DateTime 列存 UTC 但不带时区标识,`.isoformat()` 输出无 `Z` 后缀,前端按本地时间解析,显示差 8 小时。 ### 修复 - 新增 `format_utc_datetime` 工具函数,统一输出带 `Z` 后缀的 ISO 8601 UTC 时间 - 成片库 `generated_at`:改为带时区的字符串 - 素材库 `created_at`:改为带时区的字符串 - 任务列表 `created_at/updated_at`:从 datetime 类型改为带时区字符串 - 前端 `new Date()` 自动转本地时间,无需额外处理
xiaoxia added the bugbackendP1 labels 2026-07-18 19:11:41 +08:00
xiaoxia added 1 commit 2026-07-18 19:11:41 +08:00
fix(P1): 时间字段返回带UTC时区标识,修复前端显示差8小时
CI Build & Deploy Pipeline / Build Staging API Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Production API Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Production Web Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI Build & Deploy Pipeline / Deploy Production (pull_request) Has been skipped
CI Build & Deploy Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 18s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 1m2s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m3s
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Failing after 1m35s
Auto Merge CI PRs / Auto Merge on CI Green + Approved (pull_request) Successful in 2m0s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 3m9s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 1m33s
Auto Approve CI PRs / Auto Approve on CI Green (pull_request) Successful in 3m40s
AI Code Review / AI Code Review (pull_request) Successful in 4m24s
c8cb43ae1c
- 新增 format_utc_datetime 工具函数,统一输出带 Z 后缀的 ISO 8601
- 成片库 generated_at: 无时区字符串 -> 带 Z 的 UTC 时间
- 素材库 created_at: 无时区字符串 -> 带 Z 的 UTC 时间
- 任务列表 created_at/updated_at: datetime类型 -> 带 Z 的字符串
- 前端 new Date() 自动转本地时间,无需额外处理

🚀 预览环境已部署

项目 详情
PR号 #535
预览链接 https://pr-535.preview.xiaoxiajianji.com
API环境 staging

💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。

🔄 每次提交新代码后预览环境会自动更新。

🗑️ PR 关闭或合并后,预览环境会自动清理。

🚀 **预览环境已部署** | 项目 | 详情 | |------|------| | PR号 | #535 | | 预览链接 | [https://pr-535.preview.xiaoxiajianji.com](https://pr-535.preview.xiaoxiajianji.com) | | API环境 | staging | > 💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。 > > 🔄 每次提交新代码后预览环境会自动更新。 > > 🗑️ PR 关闭或合并后,预览环境会自动清理。
xiaoxia added 1 commit 2026-07-18 19:19:34 +08:00
feat(P0): 孤儿任务清理 - worker启动+任务调度时自动清理超时running任务
CI Build & Deploy Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Staging API Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Production API Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Production Web Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Deploy Production (pull_request) Has been skipped
CI Build & Deploy Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI Build & Deploy Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI Build & Deploy Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI Build & Deploy Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 24s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 55s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 56s
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Failing after 1m29s
Auto Merge CI PRs / Auto Merge on CI Green + Approved (pull_request) Successful in 2m5s
AI Code Review / AI Code Review (pull_request) Successful in 2m32s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 1m36s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 3m8s
Auto Approve CI PRs / Auto Approve on CI Green (pull_request) Successful in 3m42s
ddf861b02f
- GenerationTaskModel 新增 updated_at 字段(onupdate自动刷新)
- 领域模型 GenerationTask 新增 updated_at
- repo 新增 cleanup_stale_running 方法:running且updated_at超10分钟→failed
- worker启动时(on_worker_ready signal)自动清理
- 任务调度时(generate_video入口)也清理一次
- error_message: 任务执行中断(worker重启/超时)
- 配套5个单元测试
xiaoxia changed title from fix(P1): 时间字段返回带UTC时区标识,修复前端显示差8小时 to fix(P0+P1): 孤儿任务清理 + 时间时区统一 2026-07-18 19:19:46 +08:00
xiaoxia added P0 and removed P1 labels 2026-07-18 19:19:46 +08:00
xiaoxia added 1 commit 2026-07-18 19:29:28 +08:00
fix: black格式 + isort排序
CI Build & Deploy Pipeline / Build Staging API Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Production API Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Production Web Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI Build & Deploy Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI Build & Deploy Pipeline / Deploy Production (pull_request) Has been skipped
CI Build & Deploy Pipeline / Production Browser E2E (pull_request) Has been skipped
Preview Deploy / Deploy Preview Environment (pull_request) Failing after 16s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 22s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 55s
AI Code Review / AI Code Review (pull_request) Successful in 2m21s
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Failing after 2m41s
Auto Merge CI PRs / Auto Merge on CI Green + Approved (pull_request) Successful in 3m9s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 3m10s
Auto Approve CI PRs / Auto Approve on CI Green (pull_request) Successful in 4m17s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 1m55s
3758a07ccd
xiaoxia added 1 commit 2026-07-18 19:44:12 +08:00
fix: 新增generation_tasks.updated_at字段的migration
CI Build & Deploy Pipeline / Build Staging API Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Production API Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Production Web Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI Build & Deploy Pipeline / Deploy Production (pull_request) Has been skipped
CI Build & Deploy Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI Build & Deploy Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 21s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 55s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m1s
AI Code Review / AI Code Review (pull_request) Successful in 2m12s
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Failing after 1m57s
Auto Merge CI PRs / Auto Merge on CI Green + Approved (pull_request) Successful in 2m24s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 3m0s
Auto Approve CI PRs / Auto Approve on CI Green (pull_request) Successful in 3m26s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 1m30s
acb25ab33d
xiaoxia added 1 commit 2026-07-18 19:53:11 +08:00
fix: 更新schema快照,补充generation_tasks.updated_at
CI Build & Deploy Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Staging API Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Production API Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Production Web Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI Build & Deploy Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI Build & Deploy Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI Build & Deploy Pipeline / Deploy Production (pull_request) Has been skipped
CI Build & Deploy Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 24s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 53s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 56s
AI Code Review / AI Code Review (pull_request) Successful in 2m22s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 2m55s
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Successful in 3m1s
Auto Approve CI PRs / Auto Approve on CI Green (pull_request) Successful in 3m28s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 1m23s
Auto Merge CI PRs / Auto Merge on CI Green + Approved (pull_request) Successful in 4m56s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 27s
a59b86b453
Collaborator

代码审查结果 - PR #535

⚠️ 问题(4个需要修改)

  1. packages/adapters/sqlalchemy_impl/generation_task_repository.py 第237行:时区类型不匹配导致运行时错误。

    • 原因:代码中 cutoff 使用了带时区的 datetime.now(timezone.utc),而数据库模型 GenerationTaskModel.updated_at 的列定义为 sa.DateTime(未显式指定 timezone=True)。在大多数数据库(如SQLite、MySQL)配置下,从数据库读取出的 datetime 是 naive(无时区)对象。直接比较 naive 和 aware datetime 会抛出 TypeError
    • 后果:Worker 启动或执行任务时调用清理函数会直接崩溃,导致孤儿任务清理失败,甚至阻塞 Worker 启动。
    • 建议:将 cutoff 改为无时区的 UTC 时间(datetime.utcnow()),或者在模型定义中强制开启时区支持。考虑到迁移成本,建议修改此处逻辑为 datetime.utcnow() - timedelta(...)
  2. apps/worker/worker_app/tasks/_startup.py 第36行:数据库连接资源泄漏风险。

    • 原因session.close() 放在了 try 块中,如果 repo.cleanup_stale_running() 执行过程中抛出异常,代码会跳过 session.close() 直接进入 except 块。
    • 后果:发生异常时数据库连接不会释放,长时间运行可能导致连接池耗尽。
    • 建议:使用 try...finally 结构确保 session.close() 必定执行,或使用 with SessionLocal() as session: 上下文管理器。
  3. apps/worker/worker_app/tasks/generation.py 第1412-1420行:冗余的性能开销与异常吞没。

    • 原因generate_video 是核心任务入口,频率极高。在此处调用 cleanup_orphan_tasks 是不必要的,因为 Worker 启动时(_on_worker_ready)已经执行过清理。此外,使用了裸 except Exception: pass 吞没了所有错误。
    • 后果:每个视频生成任务都会额外触发一次数据库查询扫描,严重影响吞吐量;且清理逻辑出错时完全无感知,难以排查。
    • 建议:删除 generate_video 中的清理逻辑,仅保留 _on_worker_ready 中的调用即可。
  4. alembic/versions/043_add_updated_at_to_generation_tasks.py 第22行:数据库默认值时区不一致。

    • 原因server_default=sa.func.now() 使用数据库服务器的本地时间(通常是系统时间),而代码中 Python 层的 default 使用的是 timezone.utc
    • 后果:如果数据库服务器时区不是 UTC,旧数据的 updated_at 将是本地时间,新数据是 UTC 时间,导致时间排序和超时判断逻辑混乱。
    • 建议:如果数据库支持,使用 server_default=sa.text('CURRENT_TIMESTAMP') 并确保 DB 时区为 UTC,或者在脚本中显式更新旧数据的时区。

💡 建议(2个可选)

  1. apps/api/app/api/routes/_helpers.py 第129行format_utc_datetime 函数对字符串类型的处理过于宽松。

    • 内容:当输入已经是字符串时直接返回,未进行格式校验或补全 'Z'。如果上游数据源发生变化返回了不带时区的字符串,前端仍会解析错误。建议增强鲁棒性,尝试解析并标准化。
  2. apps/worker/worker_app/tasks/_startup.py:多 Worker 并发清理的竞态考虑。

    • 内容:如果部署了多个 Worker 进程,启动时会同时执行清理逻辑。虽然逻辑是幂等的,但会并发执行多条 UPDATE 语句。建议加锁或仅由 Leader Worker 执行(如果 Celery 配置支持),不过当前实现影响仅限于性能,属于优化点。

格式检查通过 | 逻辑审查需修改 | ⚠️ 建议关注性能


🤖 由 AI 代码审查机器人自动生成 | 2026-07-18 19:55:34 | 模型:

## 代码审查结果 - PR #535 ### ⚠️ 问题(4个需要修改) 1. **packages/adapters/sqlalchemy_impl/generation_task_repository.py 第237行**:时区类型不匹配导致运行时错误。 - **原因**:代码中 `cutoff` 使用了带时区的 `datetime.now(timezone.utc)`,而数据库模型 `GenerationTaskModel.updated_at` 的列定义为 `sa.DateTime`(未显式指定 `timezone=True`)。在大多数数据库(如SQLite、MySQL)配置下,从数据库读取出的 datetime 是 naive(无时区)对象。直接比较 naive 和 aware datetime 会抛出 `TypeError`。 - **后果**:Worker 启动或执行任务时调用清理函数会直接崩溃,导致孤儿任务清理失败,甚至阻塞 Worker 启动。 - **建议**:将 `cutoff` 改为无时区的 UTC 时间(`datetime.utcnow()`),或者在模型定义中强制开启时区支持。考虑到迁移成本,建议修改此处逻辑为 `datetime.utcnow() - timedelta(...)`。 2. **apps/worker/worker_app/tasks/_startup.py 第36行**:数据库连接资源泄漏风险。 - **原因**:`session.close()` 放在了 `try` 块中,如果 `repo.cleanup_stale_running()` 执行过程中抛出异常,代码会跳过 `session.close()` 直接进入 `except` 块。 - **后果**:发生异常时数据库连接不会释放,长时间运行可能导致连接池耗尽。 - **建议**:使用 `try...finally` 结构确保 `session.close()` 必定执行,或使用 `with SessionLocal() as session:` 上下文管理器。 3. **apps/worker/worker_app/tasks/generation.py 第1412-1420行**:冗余的性能开销与异常吞没。 - **原因**:`generate_video` 是核心任务入口,频率极高。在此处调用 `cleanup_orphan_tasks` 是不必要的,因为 Worker 启动时(`_on_worker_ready`)已经执行过清理。此外,使用了裸 `except Exception: pass` 吞没了所有错误。 - **后果**:每个视频生成任务都会额外触发一次数据库查询扫描,严重影响吞吐量;且清理逻辑出错时完全无感知,难以排查。 - **建议**:删除 `generate_video` 中的清理逻辑,仅保留 `_on_worker_ready` 中的调用即可。 4. **alembic/versions/043_add_updated_at_to_generation_tasks.py 第22行**:数据库默认值时区不一致。 - **原因**:`server_default=sa.func.now()` 使用数据库服务器的本地时间(通常是系统时间),而代码中 Python 层的 `default` 使用的是 `timezone.utc`。 - **后果**:如果数据库服务器时区不是 UTC,旧数据的 `updated_at` 将是本地时间,新数据是 UTC 时间,导致时间排序和超时判断逻辑混乱。 - **建议**:如果数据库支持,使用 `server_default=sa.text('CURRENT_TIMESTAMP')` 并确保 DB 时区为 UTC,或者在脚本中显式更新旧数据的时区。 ### 💡 建议(2个可选) 1. **apps/api/app/api/routes/_helpers.py 第129行**:`format_utc_datetime` 函数对字符串类型的处理过于宽松。 - **内容**:当输入已经是字符串时直接返回,未进行格式校验或补全 'Z'。如果上游数据源发生变化返回了不带时区的字符串,前端仍会解析错误。建议增强鲁棒性,尝试解析并标准化。 2. **apps/worker/worker_app/tasks/_startup.py**:多 Worker 并发清理的竞态考虑。 - **内容**:如果部署了多个 Worker 进程,启动时会同时执行清理逻辑。虽然逻辑是幂等的,但会并发执行多条 `UPDATE` 语句。建议加锁或仅由 Leader Worker 执行(如果 Celery 配置支持),不过当前实现影响仅限于性能,属于优化点。 --- ✅ 格式检查通过 | ❌ 逻辑审查需修改 | ⚠️ 建议关注性能 --- <sub>🤖 由 AI 代码审查机器人自动生成 | 2026-07-18 19:55:34 | 模型: </sub> <!-- AI_CODE_REVIEW_AUTO_COMMENT -->
auto-approve-bot approved these changes 2026-07-18 19:56:40 +08:00
auto-approve-bot left a comment
Collaborator

CI全绿,自动审批通过。

CI全绿,自动审批通过。
auto-approve-bot approved these changes 2026-07-18 19:56:40 +08:00
auto-approve-bot left a comment
Collaborator

CI全绿,自动审批通过。

CI全绿,自动审批通过。
auto-approve-bot merged commit 48c8d1fe3e into develop 2026-07-18 19:58:08 +08:00
auto-approve-bot deleted branch fix/p1-datetime-utc-timezone 2026-07-18 19:58:08 +08:00

🗑️ 预览环境已清理

PR #535 已关闭或合并,对应的预览环境已被清理。

如有需要,可以重新打开 PR 来重新生成预览环境。

🗑️ **预览环境已清理** PR #535 已关闭或合并,对应的预览环境已被清理。 > 如有需要,可以重新打开 PR 来重新生成预览环境。
Sign in to join this conversation.