Files
xiaoxia-saas/apps/worker/worker_app/tasks/cleanup.py
xiaoxia 54916aff86
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 5s
CI/CD Pipeline / Check push changed paths (push) Successful in 8s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 29s
CI/CD Pipeline / Build Staging API Image (push) Successful in 31s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 37s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 2m1s
CI/CD Pipeline / Integration Tests (push) Successful in 2m22s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m11s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Successful in 1m16s
CI/CD Pipeline / Validate - Style (push) Successful in 2m58s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m41s
CI/CD Pipeline / ACR Image Cleanup (push) Successful in 1m30s
CI/CD Pipeline / Staging E2E Tests (push) Failing after 1m36s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 5m47s
CI/CD Pipeline / Validate - Security (push) Successful in 6m2s
CI/CD Pipeline / Staging API Integration Tests (push) Successful in 3m33s
AI Code Review / AI Code Review (pull_request) Successful in 6m27s
CI/CD Pipeline / Unit Tests (push) Successful in 8m30s
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 31s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 32s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 38s
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 241h24m45s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 241h24m46s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 241h24m56s
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Failing after 241h25m4s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Failing after 241h25m7s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 241h25m17s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 241h25m19s
CI/CD Pipeline / Check push changed paths (pull_request) Failing after 241h25m57s
CI/CD Pipeline / Canary Release to Production (push) Failing after 241h34m12s
CI/CD Pipeline / CI Gate (push) Failing after 241h34m16s
CI/CD Pipeline / Build Production Worker Image (push) Failing after 241h34m16s
CI/CD Pipeline / Build Production API Image (push) Failing after 241h34m16s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Failing after 241h38m54s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 0s
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Failing after 241h41m44s
CI/CD Pipeline / Retag skipped Staging API Image (push) Failing after 241h41m46s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 0s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 0s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 241h25m51s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 241h25m52s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 241h25m52s
CI/CD Pipeline / Validate - Security (pull_request) Failing after 241h25m56s
CI/CD Pipeline / Validate - Style (pull_request) Failing after 241h25m56s
CI/CD Pipeline / Frontend Lint (push) Failing after 241h42m46s
CI/CD Pipeline / PR Build Worker Image (push) Failing after 241h42m51s
CI/CD Pipeline / PR Build API Image (push) Failing after 241h42m52s
CI/CD Pipeline / Check if frontend-only change (push) Failing after 241h42m57s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 241h59m22s
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Failing after 241h59m42s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 241h59m53s
CI/CD Pipeline / Deploy Production (push) Failing after 242h8m49s
CI/CD Pipeline / Build Production Web Image (push) Failing after 242h8m52s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 0s
CI/CD Pipeline / Retag skipped Staging Web Image (push) Failing after 242h16m21s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 0s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 242h0m28s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Failing after 242h0m28s
CI/CD Pipeline / PR Build Web Image (push) Failing after 242h17m27s
fix(#1714): 同名兜底去重误杀新视频 + ingest 链路孤儿清理/启动恢复 (#1734)
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com>
Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
2026-09-06 13:41:26 +08:00

128 lines
5.3 KiB
Python
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.
"""定期清理任务 — Celery Beat 调度。
包含:
- cleanup_stale_pending_tasks: 定期清理卡在 pending 超时的 generation_tasks(worker 停止消费时占位)
- cleanup_stale_running_tasks: 定期清理卡在 running 超时的 generation_tasks(容器重启/进程被杀后的孤儿)
"""
import logging
from celery import shared_task
from worker_app.tasks._startup import (
ORPHAN_TASK_TIMEOUT_MINUTES,
PENDING_TASK_TIMEOUT_MINUTES,
cleanup_orphan_tasks,
cleanup_stale_jobs,
cleanup_stale_pending_tasks,
)
from packages.application.ingest_orphan_cleanup import (
ASSET_ORPHAN_TIMEOUT_MINUTES,
INGEST_PENDING_TIMEOUT_MINUTES,
INGEST_PROCESSING_TIMEOUT_MINUTES,
)
logger = logging.getLogger(__name__)
@shared_task(name="worker.cleanup_stale_pending_tasks")
def scheduled_cleanup_stale_pending(timeout_minutes: int = PENDING_TASK_TIMEOUT_MINUTES) -> dict:
"""Celery Beat 调度的定期任务:清理超时的 pending 任务。
每 5 分钟执行一次(由 celery_app.py 的 beat_schedule 配置),
查找所有 status='pending' 且 created_at < NOW() - timeout_minutes
的 generation_tasks,批量更新为 failed,释放限流名额;同时 revoke 并清除
Redis 队列中对应的 Celery 消息,杜绝作废消息重投执行(#1714)。
Args:
timeout_minutes: 超时时间(分钟),默认 45 分钟(pending 排队阈值放宽,
与 running 孤儿 20 分钟区分,避免正常排队任务被误杀)
Returns:
{"cleaned": int}
"""
count = cleanup_stale_pending_tasks(timeout_minutes)
if count > 0:
logger.info("[Beat] 清理了 %d 个超时 pending 任务(超时阈值 %d 分钟)", count, timeout_minutes)
return {"cleaned": count}
@shared_task(name="worker.cleanup_stale_running_tasks")
def scheduled_cleanup_stale_running(timeout_minutes: int = ORPHAN_TASK_TIMEOUT_MINUTES) -> dict:
"""Celery Beat 调度的定期任务:清理超时的 running 孤儿任务。
每 5 分钟执行一次。worker_ready 信号只在 worker 启动时清一次,
若 worker 没重启但任务卡死(上传挂起、进程 OOM 被内核杀掉等),
任务会永久卡在 running 占位。此任务做持续兜底:
查找 status='running' 且 updated_at < NOW() - timeout_minutes 的任务,
标记为 failed(原因:容器重启/超时中断),同时清理 Job 表孤儿。
Args:
timeout_minutes: 超时时间(分钟),默认 20 分钟
(worker.generate_video 硬超时 11 分钟,正常任务不可能超过 20 分钟)
Returns:
{"generation_tasks": int, "jobs": int}
"""
gen_count = cleanup_orphan_tasks(timeout_minutes)
job_count = cleanup_stale_jobs(timeout_minutes)
total = gen_count + job_count
if total > 0:
logger.warning(
"[Beat] 清理孤儿任务: running GenerationTask=%d, Job=%d(超时阈值 %d 分钟)",
gen_count,
job_count,
timeout_minutes,
)
return {"generation_tasks": gen_count, "jobs": job_count}
@shared_task(name="worker.cleanup_stale_ingest_jobs")
def scheduled_cleanup_stale_ingest_jobs(
processing_timeout_minutes: int = INGEST_PROCESSING_TIMEOUT_MINUTES,
pending_timeout_minutes: int = INGEST_PENDING_TIMEOUT_MINUTES,
orphan_asset_timeout_minutes: int = ASSET_ORPHAN_TIMEOUT_MINUTES,
) -> dict:
"""Celery Beat 调度:清理上传/转码链路(IngestJob + Asset)孤儿记录。
每 10 分钟执行一次。worker 容器重启/进程 OOM 时,已 prefetch 的 transcode
celery 消息会丢失(队列里也不存在),ingest_job 永久卡 pending/processing、
asset 永久卡 processing/uploading,没有兜底永远不会恢复(#1714)。
- ingest_job processing > processing_timeout_minutes / pending > pending_timeout_minutes
→ 标 failed;关联 asset 占位(processing/uploading)联动标 error
- 无 ingest_job 关联、created_at > orphan_asset_timeout_minutes 的占位 asset
→ 标 error
- 作废 celery 消息 revoke + 物理清除(防重投,执行前守卫是第二道防线)
"""
from worker_app.db import SessionLocal
from packages.application.ingest_orphan_cleanup import (
cleanup_orphan_processing_assets,
cleanup_stale_ingest_jobs,
revoke_stale_ingest_messages,
)
session = SessionLocal()
try:
job_items, asset_ids = cleanup_stale_ingest_jobs(
session,
processing_timeout_minutes=processing_timeout_minutes,
pending_timeout_minutes=pending_timeout_minutes,
)
orphan_asset_ids = cleanup_orphan_processing_assets(session, timeout_minutes=orphan_asset_timeout_minutes)
finally:
session.close()
purged = revoke_stale_ingest_messages(job_items) if job_items else 0
total_jobs = len(job_items)
total_assets = len(set(asset_ids) | set(orphan_asset_ids))
if total_jobs or total_assets:
logger.warning(
"[Beat] 清理 ingest 链路孤儿: stale_jobs=%d, assets→error=%d, 队列清除消息=%d",
total_jobs,
total_assets,
purged,
)
return {"stale_jobs": total_jobs, "assets_to_error": total_assets, "purged_messages": purged}