[CI/CD] - 为什么 PostgreSQL 测试如此成功
·
PostgreSQL 的测试体系是开源数据库中最严谨、最全面、最工程化的典范之一。它不仅覆盖功能正确性,还深入到并发、崩溃恢复、跨平台兼容性等生产级场景。下面从测试架构、类型、工具、流程和最佳实践五个维度详细解析。
一、整体测试架构:分层 + 多维覆盖
PostgreSQL 的测试不是单一工具,而是一个多层次、多目标的生态系统:
+----------------------------+
| 开发者本地测试 |
| (make check / make installcheck) |
+----------------------------+
↓
+----------------------------+
| 官方 BuildFarm CI |
| (全球 100+ 台机器持续运行) |
+----------------------------+
↓
+----------------------------+
| 社区模糊测试 & 安全审计 |
| (如 oss-fuzz, SQLancer) |
+----------------------------+
所有测试代码位于源码树的 src/test/ 目录下。
二、核心测试类型详解
1. 回归测试(Regression Tests) —— 功能正确性的基石
- 位置:
src/test/regress/ - 原理:
- 执行 SQL 脚本(
.sql文件) - 将输出与预期结果(
.out文件)逐行比对 - 若不一致,视为回归错误(regression)
- 执行 SQL 脚本(
- 示例:
对应的预期输出:-- tests/regress/sql/select.sql SELECT 1 + 1;-- tests/regress/expected/select.out ?column? ---------- 2 (1 row)
- 特点:
- 覆盖 SQL 标准语法、函数、类型、索引、事务等
- 支持 条件跳过(如
-- @exclude_schema pg_catalog) - 自动处理 浮点精度差异、时区、locale 等环境变量
运行命令:
make installcheck(需已安装 PostgreSQL)
2. 隔离测试(Isolation Tests) —— 并发与事务正确性
- 位置:
src/test/isolation/ - 解决的问题:验证 MVCC(多版本并发控制)在并发读写下的行为是否符合 ANSI SQL 隔离级别(如 READ COMMITTED, SERIALIZABLE)
- 原理:
- 使用专用 DSL 描述多个会话的交错操作
- 自动验证最终状态是否符合预期
- 示例 DSL:
setup CREATE TABLE t (a int); INSERT INTO t VALUES (1); session s1 BEGIN ISOLATION LEVEL SERIALIZABLE; SELECT * FROM t; session s2 BEGIN; UPDATE t SET a = 2; COMMIT; session s1 SELECT * FROM t; COMMIT; permutate s1, s2 -- 测试所有可能的执行顺序 - 输出:列出哪些执行顺序会导致串行化失败(符合预期)
这是 PostgreSQL 区别于多数数据库的独特测试能力。
3. TAP 测试(Test Anything Protocol) —— 结构化测试框架
- 位置:
src/test/modules/,src/test/perl/ - 用途:测试无法用 SQL 表达的功能,如:
- 复制(Replication)
- WAL(Write-Ahead Logging)
- 后台进程(autovacuum, bgwriter)
- 扩展(Extensions)
- 技术栈:Perl + Python 脚本,使用标准 TAP 协议输出
- 示例输出:
ok 1 - server started ok 2 - replication slot created not ok 3 - unexpected WAL record type
- 优势:可被任何 TAP 解析器(如
prove)集成到 CI
4. 性能回归测试(pgbench + 自定义基准)
- 工具:内置
pgbench(TPC-B 模拟) - 做法:
- 在每次提交前后运行相同负载
- 对比 TPS(Transactions Per Second)
- 超过阈值则告警
- 扩展:社区使用 pgTAP 编写 SQL 级性能断言
三、关键测试工具链
| 工具 | 作用 | 技术细节 |
|---|---|---|
pg_regress |
回归测试驱动程序 | C 编写,管理临时实例、端口、清理 |
isolationtester |
隔离测试执行器 | 解析 .spec 文件,生成交错会话 |
prove |
TAP 测试运行器 | Perl 标准工具,支持并行、超时 |
| BuildFarm | 分布式 CI 系统 | 全球志愿者提供机器,覆盖: • OS: Linux, Windows, macOS, BSD • 编译器: GCC, Clang, MSVC • 配置: debug, optimized, ICU, LDAP |
| Valgrind / ASan | 内存错误检测 | 在测试中启用,捕获 use-after-free、内存泄漏 |
四、故障注入与鲁棒性测试
PostgreSQL 不仅测“正常路径”,更重视异常路径:
1. WAL 崩溃恢复测试
- 步骤:
- 启动数据库,执行大量写入
- 强制 kill -9(模拟断电)
- 重启,验证数据一致性
- 自动化:通过
src/test/recovery/中的 Perl 脚本实现
2. 磁盘满模拟
- 使用
posix_fallocate()或挂载小容量 tmpfs - 验证 PostgreSQL 是否优雅报错而非崩溃
3. 网络分区测试(用于逻辑复制)
- 使用
iptables或tc模拟网络延迟/丢包 - 验证复制槽(replication slot)是否阻塞或丢失数据
五、开发者工作流:如何写测试?
场景:为新函数 my_upper(text) 添加测试
-
编写 SQL 测试:
-- src/test/regress/sql/my_upper.sql SELECT my_upper('hello'); SELECT my_upper(NULL); -
生成预期输出:
# 首次运行,生成 .out 文件 make installcheck EXTRA_REGRESS_OPTS="my_upper" cp results/my_upper.out expected/my_upper.out -
验证测试通过:
make installcheck # 应显示 "my_upper ... ok" -
提交 PR:测试文件必须随代码一起提交!
规则:任何功能变更必须附带回归测试,否则不会被合并。
六、BuildFarm:全球分布式 CI
- 规模:100+ 台机器(由社区志愿者维护)
- 覆盖维度:
维度 示例 操作系统 Ubuntu 22.04, Windows Server 2022, FreeBSD 13 CPU 架构 x86_64, ARM64, PowerPC 编译选项 --enable-debug,--with-ldap,--with-icu特殊配置 32-bit, valgrind, cassert(断言开启) - 结果公开:https://buildfarm.postgresql.org
- 价值:确保提交不会破坏任何平台的构建或测试
七、模糊测试(Fuzzing)与安全测试
虽然 PostgreSQL 核心团队不直接维护 fuzzers,但社区广泛使用:
- SQLancer:
自动生成语义正确的 SQL,验证优化器与执行器一致性(发现过多个严重 bug) - oss-fuzz(Google):
对 libpq(客户端库)、WAL 解析器进行覆盖率引导模糊测试 - 自定义 AFL:
社区成员对查询解析器进行 fuzzing
八、为什么 PostgreSQL 测试如此成功?
| 原则 | 实践体现 |
|---|---|
| 测试即规范 | 每个 SQL 特性都有对应测试用例 |
| 零容忍回归 | 任何测试失败都会阻塞合并 |
| 生产级思维 | 测试包含崩溃、断电、磁盘满等真实故障 |
| 社区共建 | BuildFarm 依赖全球志愿者 |
| 简单可靠 | 回归测试基于文本比对,无需复杂断言库 |
九、学习资源
- 官方文档:
PostgreSQL Regression Tests - 源码目录:
src/test/ - BuildFarm 实时状态:
https://buildfarm.postgresql.org - 社区工具:
总结
PostgreSQL 的测试哲学是:“如果它没被测试,它就不存在。”
通过回归测试 + 隔离测试 + 全球 CI + 故障注入的组合拳,它实现了:
- 功能正确性(SQL 标准兼容)
- 并发正确性(MVCC 无 bug)
- 生产鲁棒性(崩溃后数据不损坏)
- 跨平台稳定性(从手机到大型机)
这种工程纪律性,正是 PostgreSQL 能成为企业级数据库核心的原因。对于任何严肃的系统软件项目,PostgreSQL 的测试体系都值得深度借鉴。
更多推荐

所有评论(0)