Skip to content

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

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

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

Conversation

@dengyh

@dengyh dengyh commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

Summary

为 BKFlow 模板编辑引入一套增强版「流程调试」能力,复用 bamboo-engine 在 Interface 侧编排,前端通过 ^debug 路由直连(不经 APIGW)。

核心能力:

  • 全局调试:抢锁(CAS)→ 重置 → 物化 mock → 以 create_method=DEBUG 创建并启动任务 → 懒回写节点态/全局变量/log_ref。DEBUG 任务从普通任务列表与统计中排除。
  • 单步调试step_run 支持 mock 直出与 real 微型任务(最小 start→node→end pipeline);compute_can_step 依据数据依赖判定可单步性并回报缺失变量。
  • 节点 mock:成功/失败两态(fail_nodes/errors 物化,引擎侧 BKFlowBaseService.mock_execute/mock_schedule 向后兼容注入失败)。
  • 上下文变量编辑 / reset / terminate / history / input_schema
  • 变更影响(reset_impact):只读对比上次运行指纹与当前草稿指纹,沿控制流∪数据流闭包告知受影响节点(不改库)。
  • 旧 mock 兼容:首次同步节点态时,把历史 TemplateMockScheme 勾选节点初始化为 mock 并填充默认 TemplateMockData,不覆盖用户选择,无需 data migration。
  • 调试锁超时回收:持锁超过 TTL(BKFLOW_DEBUG_LOCK_TTL_SECONDS,默认 600s)时 CAS 复位为 idle 并尽力撤销孤儿任务,避免前端关闭/轮询中断导致模板卡死。

权限:6 个写操作(global_run/reset/terminate/step_run/node_mock/context_var)走 MOCK 权限,4 个读操作(context/input_schema/history/reset_impact)走 VIEW 权限;空间/模板维度严格隔离。

设计与计划见 docs/specs/2026-06-24-debug-enhancement-redesign-design.mddocs/plans/2026-06-25-debug-enhancement-redesign.md

Test Plan

  • Interface 调试套件全绿:pytest tests/interface/template/debug/(68 passed)
  • Engine 侧调试相关:test_debug_create_instance.py / test_node_id_map.py / test_bkflow_base_plugin_service.py(25 passed)
  • 更广引擎回归 tests/engine/task tests/engine/statistics:256 passed(5 个 settings.redis_inst 失败为本地无 redis 的既有环境问题,非本分支引入,相关测试文件不在本 PR 改动范围)
  • black / isort / flake8 / pyupgrade 全部通过(pre-commit)
  • 集成环境联调:模板编辑器中实际触发全局调试 / 单步 / 节点 mock / 终止 / 历史
  • 验证 DEBUG 任务不出现在任务列表与统计

Notes / Follow-ups

  • 调试快照(global_vars/inputs/outputs/mock_outputs)按既定决策使用普通 JSONField 存储(SecretSingleJsonField 不支持嵌套结构)。
  • 后续运维项(非本期阻塞):DEBUG 任务 TTL 清理、模板删除时级联清理 DebugContext/DebugNodeState

Made with Cursor

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.23747% with 74 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.39%. Comparing base (9a63478) to head (87934c7).
⚠️ Report is 5 commits behind head on master.

Files with missing lines Patch % Lines
bkflow/template/debug/service.py 91.07% 40 Missing ⚠️
bkflow/template/views/debug.py 72.47% 30 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.39%   +0.28%     
==========================================
  Files         307      312       +5     
  Lines       18167    18917     +750     
==========================================
+ Hits        15098    15776     +678     
- Misses       3069     3141      +72     

☔ 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 等端点保持一致。

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.

2 participants