Databend 是一个使用 Rust 研发、开源的、完全面向云架构的新式数仓,致力于提供极速的弹性扩展能力,打造按需、按量的 Data Cloud 产品体验。
开源地址:https://github.com/datafuselabs/databend

Databend 从第一天就是开源的,测试系统也是基于开源生态所构建,使用了大量的 github CI(免费),已经支撑了我们半年来的快速迭代。

对于一个开源数据库项目,做到可测试性是加速迭代的不二法宝。一个 Pull Request (通常说的 Patch) 从提交到合并到主干分支,作为一个 Review 人员会比较关注以下几个问题:

  1. 是否会导致功能不正常?
  2. 是否会影响分布式执行?
  3. 是否有跨平台编译问题?
  4. 是否会导致性能下降?

本篇就从一个 Pull Request 测试周期说起,看看它从创建再到合并入主干分支,Databend 经过了哪些测试,针对上面四个问题做到心中有数,让每个 Pull Request 都有质量保障。

单元测试

单元测试是最小的测试单元。

我们每写一个函数都要做到可独立测试,如果这个函数有其他状态依赖,那状态也要是可以 Mock 的。

在 Databend 中,单元测试都放在一个独立的文件中,比如,x_test.rs:

1
2
3
4
#[test]
fn test_y() -> Result<()> {
... ...
}

目前 Databend 有 500+ 的单元测试,并对一些状态做了全局 Mock,让开发者更加容易的编写测试用例,以从代码层面确保函数执行都符合预期,尽早的发现和解决问题。

Databend 的单元测试会在 Ubuntu 和 MacOS 两个系统上运行 (Databend 研发主要使用 Mac 和 Ubuntu 两个主力系统)。

功能测试

当单元测试通过后,并不一定保证功能是正确的,因为功能通常来说是由多个函数逻辑性的贯穿而成。

功能测试又分为 Stateless 和 Stateful 两种模型,其中 Stateless 测试模型不需要加载数据集, Stateful 测试模型则需要加载预值的数据集,接下来我们着重看下 Stateless 测试模型。

Databend 参考了 ClickHouse 的做法,使用表函数 numbers_mt 来便捷的做 Stateless 测试。

比如这个稍微“复杂”的SQL:

1
SELECT number%3 as c1, number%2 as c2 FROM numbers_mt(10000) WHERE number > 2 GROUP BY number%3, number%2 ORDER BY c1, c2;

它先根据条件过滤数据,然后再进行 GROUP BY 分组,最后做一个排序,这个 SQL 执行时,会涉及非常多的函数,所以我们必须有一套便捷的机制来保证多个函数组成的功能也是正确的。

Databend 是如何做的呢?

我们会先定义一个需要测试的 SQL 集,x.sql:

1
SELECT number%3 as c1, number%2 as c2 FROM numbers_mt(10000) WHERE number > 2 GROUP BY number%3, number%2 ORDER BY c1, c2;

然后再定义一个预期的结果集,x.result:

1
2
3
4
5
6
0  0
0 1
1 0
1 1
2 0
2 1

每次做功能测试的时候,Databend 会调用这个 x.sql 文件,然后把得到的结果集和 x.result 文件进行对比,如果有出入则报错并给出提示信息。

由于 Databend 具备分布式的 MPP 能力,所以功能测试会在单机(Standalone)和 集群(Cluster)两种模式下进行回归测试,以确保 Patch 对功能没有影响。

性能测试

当单元测试和功能测试都通过后,我们还会关注一个重要的指标:这个 Patch 是否导致性能下降?或者是一个性能优化的 Patch 提升了多少性能?

针对这个问题,Databend 使用数字来做量化,我们只需在 Pull Request 里回复: /run-perf master CI 会自动编译当前分支然后跑相应的性能测试,再跟 master 做对比并生成一份性能对比报告:

这样,Review 人员就可以根据这个报告清晰的知道当前 Patch 对性能的影响,以确保每个 Patch 在性能上都是可控的。

编译测试

Databend 目标是打造一个跨平台的 Cloud Warehouse,所以要求每个 Patch 在以下几个平台都可以正常编译和工作:

1
2
3
4
5
- {os: ubuntu-latest, toolchain: stable, target: x86_64-unknown-linux-gnu, cross: false}
- {os: ubuntu-latest, toolchain: stable, target: aarch64-unknown-linux-gnu, cross: true}
- {os: ubuntu-latest, toolchain: stable, target: arm-unknown-linux-gnueabi, cross: true}
- {os: ubuntu-latest, toolchain: stable, target: armv7-unknown-linux-gnueabihf, cross: true}
- {os: macos-latest, toolchain: stable, target: x86_64-apple-darwin, cross: false}

当这个 CI 跑完后,我们可以明确的知道当前 Patch 对跨版本编译是没有影响的。

小结

以上所有的 CI 测试都通过后,我们的 Pull Request 才算合格,具备合并到主干分支的条件。

如果没有这些自动化测试 CI 做保障,每个问题都会消耗 Review 人员大量的精力去做验证,这种模式肯定不可持久,严重影响产品的迭代速度,拖慢社区的节奏。

Databend 从第一天开始就在努力打造一个可测试的系统,为此我们研发了 test-infra 以及社区协作的 fusebot 机器人,以加速 Databend 产品迭代,尽快提供一个可试用的 Alpha 版本。

References

  1. Databend: A Modern Real-Time Data Processing & Analytics DBMS with Cloud-Native Architecture
  2. Databend Github Workflows
  3. Databend Test Infra
  4. Databend FuseBots
  5. ClickHouse Testing