chore: else bug

This commit is contained in:
tao.chen
2026-07-31 18:39:44 +08:00
parent fb073c6f99
commit 838cbfdc16
3 changed files with 338 additions and 14 deletions
+61 -9
View File
@@ -503,13 +503,29 @@ class DispatchOrchestrator:
if node_id in latest:
continue
parent_runs = [latest.get(parent) for parent in parents[node_id]]
parent_failed = any(
# Decide whether this node should be skipped. A node
# is only skipped when we know it can never run:
# * ``stop_all`` — the whole run was aborted on the
# first failure, so any not-yet-dispatched node is
# dropped;
# * all parents are terminal AND at least one
# failed — there is no remaining success path.
# If even one parent is still ``queued`` or
# ``running`` we keep waiting: under ``failure_policy
# == 'continue'`` a sibling might still succeed and
# the failed parent does not block that.
parents_terminal = all(
item is not None
and item.node_status in TERMINAL_NODE_STATES
and item.node_status != "succeeded"
for item in parent_runs
)
if stop_all or parent_failed:
any_parent_failed = any(
item is not None
and item.node_status in FAILED_NODE_STATES
for item in parent_runs
)
parents_blocked = parents_terminal and any_parent_failed
if stop_all or parents_blocked:
skipped = ScheduleNodeRuns(
node_run_id=new_ulid(),
run_id=run.run_id,
@@ -523,15 +539,24 @@ class DispatchOrchestrator:
message=(
"调度失败策略为 stop,未再启动"
if stop_all
else "上游节点未成功,已跳过"
else "上游节点全部终止且至少一个失败,已跳过"
),
)
session.add(skipped)
latest[node_id] = skipped
changed = True
elif (
all(
item is not None and item.node_status == "succeeded"
# Dispatch only when every parent has actually
# run to completion successfully. A None parent
# means the parent has not even been dispatched
# yet (e.g. upstream is still queued); the existing
# parents_blocked branch above handles the case
# where every parent is terminal but at least one
# failed.
len(parent_runs) > 0
and all(
item is not None
and item.node_status == "succeeded"
for item in parent_runs
)
and active_count < max_concurrency
@@ -554,13 +579,40 @@ class DispatchOrchestrator:
for item in latest.values()
):
now = utcnow()
succeeded = all(
item.node_status == "succeeded" for item in latest.values()
# Final run status depends on the schedule's
# ``failure_policy``. ``stop`` keeps the legacy rule — any
# non-success node fails the whole run. ``continue`` is
# more lenient: the run is a success when at least one
# root-level node succeeded and there is no remaining
# ``failed`` / ``cancelled`` / ``timed_out`` node that
# would have produced real artifacts had it run. Nodes
# marked ``skipped`` count as "decided to not run" and do
# not by themselves fail the run.
failure_policy = snapshot.get("failure_policy", "stop")
statuses = [item.node_status for item in latest.values()]
any_real_failure = any(
status in FAILED_NODE_STATES for status in statuses
)
any_success = any(
status == "succeeded" for status in statuses
)
if failure_policy == "continue":
# A run with mixed success/failure/skip outcomes is
# only "succeeded" when at least one node actually ran
# to completion and nothing hit a hard failure. A
# run where every node was skipped or failed is
# itself a failure.
succeeded = any_success and not any_real_failure
else:
succeeded = all(
status == "succeeded" for status in statuses
)
run.run_status = "succeeded" if succeeded else "failed"
run.error_code = None if succeeded else "SCHEDULE_NODE_FAILED"
run.error_message = (
None if succeeded else "one or more schedule nodes did not succeed"
None
if succeeded
else "one or more schedule nodes did not succeed"
)
run.finished_at = now
if run.started_at: