Skip to content

feat(debug): 流程调试增强(全局调试/单步/节点mock/变更影响/旧mock兼容) - #768

Open
dengyh wants to merge 42 commits into
TencentBlueKing:masterfrom
dengyh:feat/debug-enhancement-redesign
Open

feat(debug): 流程调试增强(全局调试/单步/节点mock/变更影响/旧mock兼容)#768
dengyh wants to merge 42 commits into
TencentBlueKing:masterfrom
dengyh:feat/debug-enhancement-redesign

Conversation

@dengyh

@dengyh dengyh commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

Token 资源校验修复(2026-07-23)

  • 修复 DEBUG 调试任务因默认任务列表过滤而无法申请任务 Token 的问题。
  • Token 任务资源校验改为任务详情查询,并核对任务 ID 与 space_id
  • 成功日志不再打印完整任务详情。
  • 新增 4 个回归用例;master 基线验证 4 passed,black/isort/flake8/pyupgrade 通过。

调试终止状态收敛(2026-07-30)

  • 根调试任务为 REVOKED 时,将仍处于 runningwaitingpaused 的节点统一收敛为节点级 revoked
  • 保留节点耗时以及已完成/失败节点的原结果,并清空等待原因。
  • 新增迁移和 3 组状态回归用例;master 调试模块验证 77 passed。
  • 引擎调度入口的根因修复见 bamboo-engine #275
  • develop 集成 PR 见 BKFlow #840

已释放调试上下文自愈(2026-07-30)

  • 修复旧版本已撤销并释放锁后,节点仍遗留 waiting/running/paused,导致前端继续显示“调试中”的问题。
  • idle + last_run_status=revoked 上下文执行节点状态自愈,不再访问引擎,不影响正常运行任务。
  • 新增回归用例;master 调试模块验证 78 passed。
  • develop 集成 PR 见 BKFlow #843

调试 Mock 输出回显(2026-07-30)

  • debug_context.nodes[] 新增 mock_outputs 字段。
  • 成功 Mock 返回已保存输出,真实节点和失败 Mock 返回 null,支持页面刷新后回填。
  • 新增成功、失败及真实节点回归用例;master 调试模块验证 80 passed。
  • develop 集成 PR 见 BKFlow #845

dengyh and others added 30 commits June 25, 2026 16:32
以 DebugContext 为中心重设计流程调试能力,覆盖全局调试、单步调试、
统一调试上下文、变更重置规则、mock 单步与失败注入、调试态可观测、
调试历史与输入复用等需求(req1-req14),并对标 Dify/Coze 调试体验。

Co-authored-by: Cursor <cursoragent@cursor.com>
基于 2026-06-24 设计文档,按 TDD 分步编排 req1-req14 的后端实现:
Phase1 调试上下文模型与统一读、Phase2 全局调试、Phase3 单步与 mock
失败注入、Phase4 变更重置与旧 mock 兼容。落地三项关键决策:
SecretSingleJsonField 加密、模型归属 bkflow.template、单步用微型 DEBUG 任务。

Co-authored-by: Cursor <cursoragent@cursor.com>
按计划自洽性评审修复 5 处问题:
- 单步 real 用 get_node_id_map 精确定位活动 runtime id,避免误读 start/end 事件
- 单步 real 从 elapsed_time 落库 duration_ms,对齐 req12
- node_mock 改为纯配置,不再误标节点 finished/failed,
  mock 角标由 execution_mode 体现
- 补 sync 子节点结构(并行网关/子流程)校准注记
- global_run 的 inputs 初值注入升级为阻塞校准并补单测

新增 test_node_mock_does_not_mark_status、
test_step_run_real_targets_activity_and_records_duration 两个回归测试。

Co-authored-by: Cursor <cursoragent@cursor.com>
代码评审发现 SecretSingleJsonField 遇嵌套 dict/list 会抛 ValueError,
而 inputs/outputs/mock_outputs/global_vars 等调试快照天然嵌套。
决策:这些快照不加密,统一用 models.JSONField;同步更新决策TencentBlueKing#1、Task1.1 模型与测试。

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
dengyh and others added 4 commits June 26, 2026 15:19
Co-authored-by: Cursor <cursoragent@cursor.com>
- reset_impact 不再 get_or_create,改为 filter().first(),无基线直接返回空,避免 VIEW 权限下写入 DebugContext
- build_dependency_graph 深拷贝 constants,避免 classify_constants 就地污染共享树
- 补充数据流传播/删除节点/拓扑变更/无基线只读 四类用例

Co-authored-by: Cursor <cursoragent@cursor.com>
DebugContext 的锁原本仅在 GET /debug/context 触发回写时释放;前端关闭或
轮询中断会让模板长期停留在 running/terminating 且无 reaper 释放。新增
_reclaim_stale_lock:持锁超过 TTL(BKFLOW_DEBUG_LOCK_TTL_SECONDS,默认 600s)
时以 CAS 原子复位为 idle 并尽力撤销孤儿任务,接入 global_run/reset/step_run/
node_mock/context_var 的并发预检。

Co-authored-by: Cursor <cursoragent@cursor.com>

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

审查总结

本 PR 实现了完整的流程调试增强功能,架构清晰、测试覆盖充分。以下列出需关注的问题:

Important 级别 (3 条)

  • sync_from_debug_task 中循环内逐条查询 DebugNodeState(N+1)
  • _apply_legacy_mock_scheme 循环内逐条查询 TemplateMockData(N+1)
  • DebugViewSet 序列化器缺少 space_id 字段,TemplateRelatedResourcePermission 鉴权会 ValidationError

Minor 级别 (2 条)

  • mock_execute 非失败路径会对 TaskMockData 发起两次重复 DB 查询
  • sync_from_debug_task 循环内逐条 ns.save() 可优化为 bulk_update

整体设计扎实,CAS 抢锁/超时回收/孤儿清理/幂等重入均有考虑。建议优先修复权限字段缺失和 N+1 问题。

acts_outputs = self._acts_outputs()

for tpl_node_id, runtime_id in id_map.items():
ns = DebugNodeState.objects.filter(debug_context=ctx, node_id=tpl_node_id).first()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚡ N+1 查询:循环内逐条 DebugNodeState.objects.filter(...).first()。建议在循环外一次性 {ns.node_id: ns for ns in DebugNodeState.objects.filter(debug_context=ctx)} 批量加载后按字典取值。

"""
targets = self._legacy_scheme_nodes().intersection(new_node_ids)
for node_id in targets:
default_md = TemplateMockData.objects.filter(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚡ N+1 查询:targets 集合循环内逐条查 TemplateMockData。建议批量查询 TemplateMockData.objects.filter(template_id=..., node_id__in=targets, is_default=True) 后用 dict 索引。



class DebugViewSet(GenericViewSet):
permission_classes = [AdminPermission | SpaceSuperuserPermission | TemplateRelatedResourcePermission]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 TemplateRelatedResourcePermission.has_permission 内部用 TemplateRelatedResourceSerializer 校验请求参数,该序列化器同时要求 space_idtemplate_id。但 Debug 各序列化器只传了 template_id,对依赖 token 鉴权的非 admin 用户会导致 400 ValidationError。需在 Debug 序列化器中补上 space_id 字段(或在 permission 内从 template_id 反查 space_id)。

return True

def mock_execute(self, data, parent_data):
taskflow_id = parent_data.get_one_of_inputs("task_id")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_get_mock_fail_info 和后续 get_mock_outputs 各自调用 get_taskflow_mock_data(两次 DB 查询同一行)。非失败节点走到第 83 行时会第二次查询。可考虑在 mock_execute 入口只查一次 mock_data 后分别取 fail_nodesoutputs

for out_key, var_key in acts_outputs.get(tpl_node_id, {}).items():
if out_key in outputs:
ctx.global_vars[var_key] = outputs[out_key]
ns.save()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✨ 循环内逐条 ns.save() 可改为收集后 bulk_update(updated_nodes, ["status", "duration_ms", "log_ref", "outputs", "error_detail"]),减少 DB round-trip。

@codecov-commenter

codecov-commenter commented Jun 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.30100% with 87 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.49%. Comparing base (9a63478) to head (0d92d70).
⚠️ Report is 12 commits behind head on master.

Files with missing lines Patch % Lines
bkflow/template/debug/service.py 91.09% 47 Missing ⚠️
bkflow/template/views/debug.py 72.47% 30 Missing ⚠️
bkflow/task/operations.py 83.33% 6 Missing ⚠️
bkflow/template/debug/dependency.py 95.23% 3 Missing ⚠️
bkflow/contrib/api/collections/task.py 50.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #768      +/-   ##
==========================================
+ Coverage   83.10%   83.49%   +0.38%     
==========================================
  Files         307      312       +5     
  Lines       18167    19044     +877     
==========================================
+ Hits        15098    15900     +802     
- Misses       3069     3144      +75     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@dengyh
dengyh force-pushed the feat/debug-enhancement-redesign branch from faae4c2 to 87934c7 Compare June 26, 2026 08:14

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

增量审查总结

之前报告的 5 个问题(N+1 查询 ×2、权限 space_id 缺失、mock_execute 重复查询、循环内 ns.save())在本次提交中均未修复,不再重复评论。

新发现问题 (1 条 — 🚨 Critical)

  • get_node_id_map 端点返回 Response(mapping) 裸 dict,但 DebugService 消费时按 {"result": True, "data": {...}} 协议解析(id_map_resp.get("result"))。两端格式不一致会导致 sync_from_debug_task_step_run_real 中拿到的 id_map 永远为空,调试结果无法回写。需把视图响应统一为 OperationResult 格式,或修改 Service 端解析逻辑。

Comment thread bkflow/task/views.py
task_instance = self.get_object()
activities = (task_instance.execution_data or {}).get("activities", {})
mapping = {act.get("template_node_id", act_id): act_id for act_id, act in activities.items()}
return Response(mapping)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 响应格式不匹配:此处返回裸 mapping dict,但 DebugService.sync_from_debug_task_step_run_real{"result": True, "data": {...}} 解析(id_map_resp.get("result"))。建议改为 return Response({"result": True, "data": mapping, "message": ""}) 或使用 OperationResult,与 get_states/operate 等端点保持一致。

@tencentblueking-adm

tencentblueking-adm commented Jul 16, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

增量审查总结

本次提交(587e054)将 real 单步调试从同步轮询改为异步启动 + context 惰性同步,架构变更合理,测试覆盖充分(新增 4 个同步场景测试用例)。

之前报告的问题状态:

  • 🚨 get_node_id_map 响应格式不匹配 — 仍未修复,且影响范围扩大:_step_run_real 重构后将 get_node_id_map 调用前移到 start 之前并移除了 fallback(原轮询器可在 runtime_id 为空时优雅降级),现在会直接触发 DebugStateError("node id map missing"),导致 real 单步必定失败
  • ⚡ N+1 查询(sync_from_debug_task 循环内逐条查 DebugNodeState)— 未修复
  • ns.save() 循环 — 未修复
  • 其余 2 条 Minor — 未修复

以上不再重复留评。

新增代码审查结果:无新问题

本次变更质量良好:

  • 状态模型拆分清晰(status 管锁、last_run_status 管结果)
  • _flatten_state_children 用迭代 BFS 处理子流程嵌套,避免递归深度问题
  • include_schedule 按需查询 DBSchedule,一次批量 filter 覆盖所有节点
  • 异常路径正确清理孤儿任务并释放锁,_acquire_lock 在 try 块外保证不误释他人锁
  • Migration 与 Model 字段一一对应,默认值设置合理

@dengyh

dengyh commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator Author

已追加 stage 回归问题修复,最新提交:5deccad5

修复内容:

  • 暂停节点缺少活跃 Schedule 时,从休眠进程对应服务定义兜底识别 schedule_type
  • 网关纳入 template_node_id 注入和运行时节点映射
  • 状态树缺少 ex_data 时回查失败节点详情,保留网关真实错误
  • 调试历史通过批量任务状态接口校准 failed/paused/revoked 等状态

master 基线验证:

  • 任务状态与节点映射:36 passed
  • Interface 调试套件:74 passed
  • pre-commit:black / isort / flake8 / pyupgrade 全部通过

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

增量审查总结

本次提交(5deccad)修复了暂停节点 schedule_type 推断、网关 template_node_id 注入/映射、状态树 ex_data 缺失兜底、调试历史状态校准四个回归问题,代码逻辑清晰,测试覆盖充分。

之前报告的问题状态:

  • 🚨 get_node_id_map 响应格式不匹配 — ✅ 经复核此问题不存在TaskInstanceViewSet 继承自 SimpleGenericViewSet,其 finalize_response 会自动将 Response(mapping) 包装为 {"result": True, "data": mapping, ...},与 DebugService 消费端协议一致。测试 test_node_id_map.py:91 也验证了 response.data["data"] 可正确访问。
  • ⚡ N+1 查询(sync_from_debug_task 循环内逐条查 DebugNodeStatens.save() 循环)— 未修复,不再重复评论
  • 其余 Minor — 未修复,不再重复评论

新增代码审查结果(1 条 Minor):

"FAILED": "failed",
"REVOKED": "revoked",
"EXPIRED": "expired",
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

status_map 建议提取为模块级常量(参照 ENGINE_RUN_STATE_MAP 的定义方式),避免每次 history() 调用重复创建 dict,也更易复用。

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

增量审查总结

本次提交(cfc9466)将 TokenResourceValidator.task_existstask_list 批量查询改为 get_task_detail 精确查询,并增加了 space_id + task_id 双重校验,逻辑更健壮,测试覆盖 4 个场景(同空间通过、跨空间拒绝、ID 不匹配拒绝、查询失败拒绝)。

之前报告的问题状态:

  • ⚡ N+1 查询(sync_from_debug_task / _apply_legacy_mock_scheme)— 未修复,不再重复评论
  • ✨ 循环内 ns.save() / mock_execute 重复查询 — 未修复,不再重复评论

新发现问题 (2 条 — Minor):

  • 新增测试文件缺少开源协议头
  • 测试中存在无效 mock setup(client.task_list.return_value 已不再被调用)

@@ -0,0 +1,64 @@
from unittest.mock import patch

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✨ 新增 Python 文件缺少开源协议头(# -*- coding: utf-8 -*- 及 MIT 版权声明),项目规范要求所有新文件顶部须包含。

@patch("bkflow.apigw.serializers.token.TaskComponentClient")
def test_task_exists_accepts_debug_task_in_same_space(self, mock_client_cls):
client = mock_client_cls.return_value
client.task_list.return_value = {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

client.task_list.return_value 设置无效——当前 task_exists 已改用 get_task_detail,此 mock 永远不会被调用,建议删除避免误导。

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

增量审查总结

本次提交(4cef2aa)收敛调试终止后的活跃节点状态为 revoked,实现简洁正确:

  • DebugNodeState 新增 revoked 状态枚举,附带正确的 migration
  • sync_from_debug_task 在引擎状态为 REVOKED 时,批量将活跃节点(running/waiting/paused)update 为 revoked,避免逐条操作
  • 测试覆盖 3 种活跃态(running、waiting/poll、paused)全部通过参数化验证

之前报告的问题状态:

  • ⚡ N+1 查询(sync_from_debug_task 循环内逐条查 DebugNodeStatens.save() 循环)— 未修复,不再重复评论
  • _apply_legacy_mock_scheme N+1 — 未修复,不再重复评论
  • ✨ 其余 Minor — 未修复,不再重复评论

新增代码审查结果:无新问题

本次变更质量良好,逻辑正确且与现有 ENGINE_RUN_STATE_MAP / ENGINE_FINISHED_STATES 配置一致,_release_lock 会在终态时正确释放锁回到 idle。

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

增量审查总结

本次提交(6403def)为 sync_from_debug_task 的早返回路径增加了遗留节点状态修复逻辑:当调试锁已释放(idle)但上次运行为 revoked 时,批量将残留的活跃节点(running/waiting/paused)收敛为 revoked。

代码质量评估:

  • 逻辑正确且幂等,与 line 574-578 的正常路径 revoked 清理保持一致
  • 使用批量 update() 而非循环逐条操作,符合规范
  • 测试用例完整覆盖场景(idle + revoked + stale waiting node),并验证 task_client 未被调用

之前报告的问题状态:

  • ⚡ N+1 查询(sync_from_debug_task 循环内逐条查 DebugNodeState + ns.save())— 未修复,不再重复评论
  • _apply_legacy_mock_scheme N+1 — 未修复,不再重复评论

新增代码审查结果:无新问题

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

增量审查总结

本次提交(0d92d70)在 build_context_view 中补充了 mock_outputs 字段的回显,逻辑简洁正确:仅当节点处于 mock 模式且 mock_result == "success" 时才返回 mock_outputs,否则返回 None。测试覆盖了 success 预设回填和 fail 预设屏蔽两个分支。

之前报告的问题状态:

  • sync_from_debug_task 循环内逐条查 DebugNodeState(N+1)— 仍未修复,不再重复
  • _apply_legacy_mock_scheme 循环内逐条查 TemplateMockData(N+1)— 仍未修复,不再重复
  • ✨ 循环内 ns.save() 可改 bulk_update — 仍未修复,不再重复
  • status_map 提取为模块级常量 — 仍未修复,不再重复

新发现问题:无

本次变更无新增问题,代码质量良好。✅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants