fix: 拒绝环形 DAG 并避免无效工作流堵塞任务队列

This commit is contained in:
2026-09-11 17:19:40 +08:00
parent 6eb65e4356
commit f99f8de171
5 changed files with 242 additions and 9 deletions
+15 -1
View File
@@ -13,6 +13,7 @@ from fastapi import APIRouter, Depends, HTTPException
from wov_app.db import Database
from wov_app.schemas import WorkflowCreate
from wov_app.scheduler import topological_sort
from wov_sdk.models import WorkflowDefinition
router = APIRouter(prefix="/api/admin/workflows", tags=["workflows"])
@@ -32,10 +33,17 @@ def _slugify(value: str) -> str:
def _validate_definition(raw: dict) -> WorkflowDefinition:
"""解析并校验 DAG 定义,非法时转换为 422 HTTP 异常。"""
"""解析并校验 DAG 定义,非法时转换为 422 HTTP 异常。
除 `WorkflowDefinition.validate()` 的结构校验(名称/版本/节点 ID 唯一/
边引用存在)之外,还要求 DAG **可拓扑排序**:环形依赖虽然结构上合法,
但执行时无法确定节点顺序,必须拒绝保存与发布(R04)。
"""
try:
definition = WorkflowDefinition.from_dict(raw)
definition.validate()
# 环形 DAG(含自环)在这里以 "workflow contains a cycle" 被拒绝。
topological_sort(definition)
return definition
except (KeyError, TypeError, ValueError) as exc:
raise HTTPException(status_code=422, detail=str(exc)) from exc
@@ -119,6 +127,12 @@ def publish_workflow(workflow_id: str, db: Database = Depends(_get_db)) -> dict:
raise HTTPException(status_code=404, detail="workflow not found")
if workflow["latest_version"] == 0:
raise HTTPException(status_code=422, detail="workflow has no version")
# 发布前重新校验待发布版本:历史遗留的无效定义(例如修复前保存的环形 DAG)
# 不能进入用户应用中心,否则创建出来的任务会在执行期失败(R04)。
latest = db.get_latest_workflow_version(workflow_id)
if latest is None:
raise HTTPException(status_code=422, detail="workflow has no version")
_validate_definition(latest["definition"])
# 发布只是状态切换,不修改已保存的版本数据。
db.upsert_workflow(
{