七月月报:vanilla 对照组 ——bug 归属的实验法

覆盖范围:7.6 / 7.13 / 7.20 / 7.27 四周。本月工作背景一句话:在华为实习,研读鲲鹏 Omni 大数据加速生态并完成首批 SQL 表达式开发,月底调研九问 Agent 生态。

本月方法地图

mindmap
  root((七月方法))
    源码研读
      模块关系图先行
      主线切入到源码层
    开发验证
      编译地图
      vanilla 对照组
      结论文档化
    经验沉淀
      审计进流程
      skill 固化重复动作
    Agent 调研
      声明式编排
      动态生成执行

原理深读:崩溃了,先问 "这是谁的 bug"

是什么:修 bug 前先做归属判断。方法是把同一用例同时跑在 vanilla(原生 Flink)和 native(自研加速引擎)上对照:vanilla 也崩 → 上游问题;vanilla 正常、native 崩 → 自己的问题。

为什么成立(机理):修复决策完全取决于归属 —— 上游 bug 自己修不了,白改代码;自研 bug 不修就是漏。但直觉归因不可靠:本月 BETWEEN 表达式的崩溃,投影 + CAST 路径和 filter 路径都是 "跑 BETWEEN 就崩",崩溃栈却完全不同,直觉会把两类问题混成一类。对照实验把 "归属" 从猜测变成可观测事实:同一个输入在参照系上能否复现,就是归属的判据。这是控制变量实验在调试里的直接应用 —— 参照系消掉了所有共同变量,剩下的差异就是问题所在。

本月结论:

flowchart TD
    A[BETWEEN 用例崩溃] --> B{vanilla 是否也崩}
    B -->|是| C[上游 bug<br/>Calcite Sarg.isComplementedPoints<br/>记录 + 测试规避]
    B -->|否| D[自研 bug<br/>FilterCodeGen 生成 BetweenExpr 崩溃<br/>自己修复]

  • 投影 + CAST 路径:vanilla 同样崩溃,栈在 Sarg.isComplementedPoints ← ImmutableRangeSet.span。事后查证这确是上游已知缺陷 CALCITE-5722——"Sarg.isComplementedPoints fails with anti-points which are equal under compareTo but not equals":1 与 1.00000000000 在 BigDecimal.equals 下不等、在 compareTo 下相等,范围集判断用错了相等语义。该 issue 直到 Calcite 1.35.0 才修复(2023-07 关闭),官方只标注影响 1.34.0,而 Flink 1.16 内置的是 Calcite 1.26.0(release-1.16 分支 pom 可查)——1.26.0 是否中招,直接依据是 vanilla 实测复现。归属上游,测试主动规避这类输入。
  • filter 路径:vanilla 正常、native 崩溃,根因在自研 FilterCodeGen,属于自己,本周修复。

配套动作同样重要:上游 bug 的正确处置不是修(不在你的代码里),而是记录 + 让测试规避;排查结论写成可交接文档 —— 临时结论会丢,文档才是团队资产。

边界在哪:

  • 对照必须同版本同输入:vanilla 与 native 的 Flink 基线要一致(1.16.3),否则差异可能来自版本差而不是归属差。
  • 环境相关的 bug 两边可能都不崩 —— 对照组阴性不等于没有 bug,只说明这个用例分不出归属。
  • 有的问题没有现成参照系(纯自研模块),此时用 "最小复现用例" 替代:把输入裁剪到最小,让归属自然浮现。

原理清单

  • 模块关系图是路线图:多模块项目先画模块关系、定切入重点,再钻代码。Omni 生态四个仓库,先确定 Adaptor-Flink → OmniStream 这条链路,阅读才有方向。
  • 编译地图是开发验证的地基:把 "依赖 → 安装脚本 → 产物 → 运行时依赖" 记成可复现步骤(OmniStream 依赖 JDK、BoostKit、LLVM、rocksdb 等几十个库)。先解决 "能跑起来",再谈 "跑得好"—— 没有验证链路,代码写了也不敢说能用。
  • 调研大系统抓一条主线:选一个贯穿全流程的概念(这里是 skill)挖到源码层,用代码验证文档。到处看文档建立不了真实理解 —— 文档和实际行为可能对不上。
  • 声明式编排 + 动态生成:源码级调研到的九问链路 ——skill 定义 workflow.md(声明子 agent、上下文、工具)→ 框架按条件生成 workflow.py 执行。注意:公开 README 只写到 "用 Python 脚本定义确定性多阶段工作流",声明式生成机制是源码层结论、非官方文档记载。它与微软 Agent Framework "把编排定义为数据(YAML)而非代码" 是同一思路 —— 业务用可读声明描述,执行交给框架,复杂多步骤任务既可控又可扩展。
  • 经验沉淀到流程,不沉淀到记忆:人工审计迟早会漏。把项目特有注意点提炼进审计 skill,开发完自动触发 —— 经验在所有后续开发里持续生效,而不是依赖 "这次记得"。
  • 重复动作固化成 skill:环境搭建、构建、测试固化后变成 "一条命令",出错概率和上手成本同时下降。
  • 环境受限时做纯脑力工作:网卡没到位的那周正好读源码、理架构、写总结;环境就绪立即进入开发验证。等待不等于空转。
  • 学习周留消化时间:连续输入不整理,吸收效率反而低。周期性复盘是输入流程的一部分。

反例与边界

  • 把上游缺陷当自己的修(反例):不在自己代码里的 bug,改自己的代码属于白费功,正确处置是记录 + 规避。
  • 只读文档不看代码(反例):文档描述与实际行为可能不一致,关键机制必须用代码验证。
  • 对照组的局限:见深读边界 —— 版本不一致或环境相关 bug 会污染归因。

参考资料