fix(P0): 修复取消链路断裂,前端取消后 GPU 仍继续推理 (#2009)
CI/CD Pipeline / Check push changed paths (pull_request) 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 3s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Style (pull_request) Has been skipped
CI/CD Pipeline / Validate - Security (pull_request) Has been skipped
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 42s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 48s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 2m23s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 3s
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m17s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m24s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been skipped
AI Code Review / AI Code Review (pull_request) Successful in 6m51s
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 1s
CI/CD Pipeline / Check push changed paths (push) Successful in 2s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / Integration Tests (push) Successful in 4m7s
CI/CD Pipeline / Build Staging API Image (push) Successful in 4m14s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 4m28s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 4m47s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 4m49s
CI/CD Pipeline / Retag skipped Staging API Image (push) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (push) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Has been skipped
CI/CD Pipeline / Validate - Style (push) Successful in 5m7s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 6m58s
CI/CD Pipeline / Unit Tests (push) Successful in 12m19s
CI/CD Pipeline / Validate - Security (push) Successful in 13m1s
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Failing after 10m9s
CI/CD Pipeline / Staging E2E Tests (push) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
CI/CD Pipeline / Check push changed paths (pull_request) 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 3s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Style (pull_request) Has been skipped
CI/CD Pipeline / Validate - Security (pull_request) Has been skipped
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 42s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 48s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 2m23s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 3s
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 2m17s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m24s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been skipped
AI Code Review / AI Code Review (pull_request) Successful in 6m51s
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (push) Successful in 1s
CI/CD Pipeline / Check push changed paths (push) Successful in 2s
CI/CD Pipeline / Frontend Lint (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / Integration Tests (push) Successful in 4m7s
CI/CD Pipeline / Build Staging API Image (push) Successful in 4m14s
CI/CD Pipeline / Validate - Python (mypy + alembic) (push) Successful in 4m28s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 4m47s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 4m49s
CI/CD Pipeline / Retag skipped Staging API Image (push) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (push) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (push) Has been skipped
CI/CD Pipeline / Validate - Style (push) Successful in 5m7s
CI/CD Pipeline / Frontend Unit Tests (push) Successful in 6m58s
CI/CD Pipeline / Unit Tests (push) Successful in 12m19s
CI/CD Pipeline / Validate - Security (push) Successful in 13m1s
CI/CD Pipeline / Build Production Web Image (push) Has been skipped
CI/CD Pipeline / Build Production API Image (push) Has been skipped
CI/CD Pipeline / Build Production Worker Image (push) Has been skipped
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Failing after 10m9s
CI/CD Pipeline / Staging E2E Tests (push) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
Co-authored-by: xiaoxia <dev@xiaoxiajianji.com> Co-committed-by: xiaoxia <dev@xiaoxiajianji.com>
This commit was merged in pull request #2009.
This commit is contained in:
@@ -108,11 +108,14 @@ def _check_musetalk_health() -> tuple[bool, dict]:
|
||||
return False, {"error": str(exc)}
|
||||
|
||||
|
||||
def _register(task_id: Optional[str] = None) -> bool:
|
||||
def _register(task_id: Optional[str] = None) -> tuple[bool, bool]:
|
||||
"""向服务端注册 / 心跳,附带 GPU 信息。
|
||||
|
||||
推理期间的心跳线程传 task_id:服务端会同步刷新该 processing 任务的
|
||||
last_heartbeat_at,防止长推理被误判超时回收。
|
||||
last_heartbeat_at,防止长推理被误判超时回收。同时服务端会检查该任务
|
||||
是否已被用户取消,若是则返回 cancel_task=True。
|
||||
|
||||
返回 (ok, cancel_task)。
|
||||
"""
|
||||
ok, info = _check_musetalk_health()
|
||||
if isinstance(info, dict):
|
||||
@@ -142,12 +145,14 @@ def _register(task_id: Optional[str] = None) -> bool:
|
||||
timeout=15,
|
||||
)
|
||||
if r.status_code == 200:
|
||||
return True
|
||||
resp_body = r.json()
|
||||
cancel_task = resp_body.get("cancel_task", False)
|
||||
return True, cancel_task
|
||||
logger.error("注册/心跳失败: HTTP %d body=%s", r.status_code, r.text[:300])
|
||||
return False
|
||||
return False, False
|
||||
except Exception as exc:
|
||||
logger.error("注册/心跳异常: %s", exc)
|
||||
return False
|
||||
return False, False
|
||||
|
||||
|
||||
def _probe_gpu_name() -> str:
|
||||
@@ -329,6 +334,9 @@ class TaskHeartbeat(threading.Thread):
|
||||
期间无法发送,服务端会因任务 last_heartbeat_at 停滞而误判超时回退 pending。
|
||||
本线程每 task_heartbeat_interval 秒(默认 30s)POST /gpu/register 并
|
||||
携带当前 task_id,让服务端持续续期任务心跳;任务处理结束 stop()。
|
||||
|
||||
同时检测服务端返回的 cancel_task 信号:若为 True,说明用户已取消任务,
|
||||
立即调用 _cancel_musetalk() 终止本地推理,并设置 cancelled 标志供主流程检查。
|
||||
"""
|
||||
|
||||
def __init__(self, task_id: str, interval: float):
|
||||
@@ -336,13 +344,21 @@ class TaskHeartbeat(threading.Thread):
|
||||
self.task_id = task_id
|
||||
self.interval = max(5.0, interval)
|
||||
self._stop_event = threading.Event()
|
||||
self.cancelled = False # 外部可读的取消标志
|
||||
|
||||
def run(self) -> None:
|
||||
# 先立即发一次,再按间隔循环(首次心跳失败不影响主流程)
|
||||
while not self._stop_event.is_set():
|
||||
try:
|
||||
if _register(self.task_id):
|
||||
ok, cancel_task = _register(self.task_id)
|
||||
if ok:
|
||||
logger.debug("任务 %s 心跳已发送", self.task_id)
|
||||
if cancel_task:
|
||||
logger.warning("任务 %s 已被用户取消,正在终止本地推理...", self.task_id)
|
||||
self.cancelled = True
|
||||
_cancel_musetalk()
|
||||
self._stop_event.set()
|
||||
return
|
||||
except Exception as exc: # noqa: BLE001
|
||||
logger.warning("任务 %s 心跳异常(忽略): %s", self.task_id, exc)
|
||||
self._stop_event.wait(self.interval)
|
||||
@@ -369,9 +385,17 @@ def _handle_task(task: dict) -> None:
|
||||
if not _download(task["video_url"], video_path):
|
||||
_report_result(task_id, False, 0.0, "下载人物视频失败")
|
||||
return
|
||||
if hb.cancelled:
|
||||
logger.info("任务 %s 在下载阶段被用户取消", task_id)
|
||||
_report_result(task_id, False, 0.0, "用户取消任务")
|
||||
return
|
||||
if not _download(task["audio_url"], audio_path):
|
||||
_report_result(task_id, False, 0.0, "下载驱动音频失败")
|
||||
return
|
||||
if hb.cancelled:
|
||||
logger.info("任务 %s 在下载阶段被用户取消", task_id)
|
||||
_report_result(task_id, False, 0.0, "用户取消任务")
|
||||
return
|
||||
|
||||
# 2. 输入时长前置校验:短视频 MuseTalk 会 division by zero,
|
||||
# 直接上报 failed,不浪费 GPU 时间。ffprobe 不可用/读失败(0.0)
|
||||
@@ -392,12 +416,20 @@ def _handle_task(task: dict) -> None:
|
||||
err = ""
|
||||
retryable = False
|
||||
for attempt in range(Config.task_max_retry + 1):
|
||||
if hb.cancelled:
|
||||
logger.info("任务 %s 在推理前被用户取消", task_id)
|
||||
_report_result(task_id, False, 0.0, "用户取消任务")
|
||||
return
|
||||
if attempt > 0:
|
||||
logger.info("任务 %s 第 %d 次重试(瞬时错误)...", task_id, attempt + 1)
|
||||
time.sleep(2)
|
||||
success, duration, err, retryable = _call_musetalk(video_path, audio_path, out_path)
|
||||
if success or not retryable:
|
||||
break
|
||||
if hb.cancelled:
|
||||
logger.info("任务 %s 被用户取消(推理已终止)", task_id)
|
||||
_report_result(task_id, False, 0.0, "用户取消任务")
|
||||
return
|
||||
if not success:
|
||||
logger.error("任务 %s 推理失败: %s", task_id, err)
|
||||
_report_result(task_id, False, 0.0, err)
|
||||
@@ -467,7 +499,8 @@ def main() -> int:
|
||||
# 心跳
|
||||
now = time.time()
|
||||
if now - last_heartbeat >= Config.heartbeat_interval:
|
||||
if _register():
|
||||
ok, _ = _register()
|
||||
if ok:
|
||||
last_heartbeat = now
|
||||
|
||||
# 轮询任务
|
||||
|
||||
Reference in New Issue
Block a user