Pgtestdb 的模板克隆方法使测试速度很快
摘要
pgtestdb 是一个 Go/Postgres 测试包,它利用 Postgres 模板数据库快速克隆测试数据库,实现与基于模式的方法相当的设置时间,同时支持完整的端到端测试。
暂无内容
查看缓存全文
缓存时间: 2026/07/30 16:51
# pgtestdb的模板克隆测试方法速度快
来源:https://brandur.org/fragments/pgtestdb
昨天,我在 *Cup o' Go* (https://cupogo.dev/episodes/proposals-proposals-proposals-and-faster-postgresql-tests-with-peter-downs) 节目中回想起 Peter Downs 的 `pgtestdb` (https://github.com/peterldowns/pgtestdb) 这个 Go/Postgres 测试包的存在。
`pgtestdb` 建立在 Postgres 的**模板数据库** (https://www.postgresql.org/docs/current/manage-ag-templatedbs.html) 之上,这是 Postgres 的一个内置特性,你可以直接在普通的 psql 终端中尝试:
```sql
CREATE DATABASE dbname TEMPLATE template_to_copy;
```
复制模板非常快,远比从头迁移测试数据库要快,而且**远**快于如今某些项目使用的重量级 Docker 方案。在底层,Postgres 会枚举模板的关系,并以 8KB 页块的形式复制其实体化的堆、索引和目录文件。
我记得多年前读到过这个特性,但说实话,我忘了它的存在。我很好奇它与其他测试方法的性能对比,于是让 Codex 将 pgtestdb 集成到 River 的测试套件中,看看表现如何。
我倾向于认为 River 的测试方法在速度和可靠性方面堪称黄金标准。它使用一组自定义测试助手,基于 schema 隔离测试用例。这种方法比**测试事务** (https://brandur.org/fragments/go-test-tx-using-t-cleanup) 慢,但有一些优势:
- 测试失败时,保留测试状态以供检查。
- 支持测试数据库级特性,如 listen/notify。
- 支持测试涉及多个事务交互和回滚的边缘情况。
## 结果 (https://brandur.org/fragments/pgtestdb#results)
在 Postgres 中,schema 比数据库更轻量,因此基于 schema 的方法在这方面有优势。然而,你不能克隆 schema,所以基于 schema 的方法每次都必须运行迁移,这给了 pgtestdb 一个明显的优势。
这应该会提供一个有趣的对比。以下是我得到的结果:
| 方法 | 计数 | 平均值 | p90 | p95 | 最大值 |
|------|------|--------|-----|-----|--------|
| pgtestdb 克隆 | 466 | 98.4ms | 247.4ms | 299.5ms | 465.1ms |
| 创建 + 迁移 schema | 81 | 99.4ms | 152.1ms | 209.0ms | 327.0ms |
我们发现,两种方法的耗时非常相似,大约都在 100ms 左右的设置时间。
我一直以为任何涉及创建新数据库的操作都会相对较慢,所以看到 pgtestdb 的方法实际这么快时,我有点惊讶。
我打算保留 River 现有的基于 schema 的测试方法,因为它已经很快,而且测试 schema 隔离顺便有助于验证 River 的**基于 schema 的配置** (https://riverqueue.com/docs/alternate-schema) 是否按预期工作。但我会在我们的文档中推荐 pgtestdb,特别是对于想进行端到端测试(即客户端插入 job → worker 完全完成)的用户。
## 通过复用进行优化 (https://brandur.org/fragments/pgtestdb#optimizing-via-reuse)
上面我有点保守。虽然基于 schema 的方法的**设置时间**与 pgtestdb 的完整数据库方法相似,但整体测试套件在前者上运行速度快约 3.5 倍:
| 方法 | 墙钟时间 |
|------|----------|
| pgtestdb 克隆 | 51.07s |
| 创建 + 迁移 schema | 14.54s |
但这不是因为 schema 快很多。River 的测试助手有一个有用的优化:它们会根据 Go 的即时并行化需求创建尽可能多的测试 schema,并在测试用例完成后将它们池化。如果一个未占用的 schema 已就绪,测试用例会清理并复用它,而不是从头生成一个新的[^1]。
这说起来容易做起来难,因为你需要考虑 schema 版本之类的细节——例如,当跨 schema 版本进行测试时,每个测试用例只能复用与其预期版本相同的 schema。当然,这完全可行,但需要一些思考。我在 LLM 出现之前编写了 River 的实现,花了几天时间才解决所有 bug。
我提到复用的原因是,pgtestdb 也可以做到这一点,可能作为包本身的一部分,或者作为项目中调用它的增强。100ms 来引导一个测试数据库已经很快了,但如果你在构建一个将有 10,000 个测试的完整应用程序,你理想中希望测试设置快 10 倍。复用可以将其降低到 10-20ms,更接近测试事务的水平。
[^1]: 脚注原文未提供,此处保留标记。
相似文章
@ycombinator: Ardent (@ArdentAI) 让你在 TB 级规模下 <6秒 克隆任何 Postgres 数据库,让编码代理可以测试代码,工程团队可以快速上线而不用担心影响生产…
Ardent 是一款 Y Combinator 支持的工具,能在 TB 级规模下于 6 秒内克隆任何 PostgreSQL 数据库,让编码代理和开发者可以在接近生产环境的克隆副本上测试代码,而不会造成停机风险。该工具已被 Supermemory 和 Surface Labs 等公司采用。
PostgresBench: 一个可复现的 Postgres 服务基准测试
ClickHouse 发布了 PostgresBench,这是一个公开且可复现的基准测试,用于比较托管式 Postgres 服务,它使用标准的 pgbench 工具,在多个缩放因子下运行类似 TPC-B 的工作负载。
PgDog
PgDog 是一款无需修改应用程序即可扩展 PostgreSQL 的工具,提供连接池和负载均衡功能。
PgDog 获得融资,即将登陆您的数据库
PgDog 是一个开源代理,使 Postgres 实现水平扩展,已从 Basis Set、YC 等机构获得 550 万美元融资。该工具已在生产环境中每秒处理超过 200 万次查询。
用Rust重写的Postgres,现已100%通过Postgres回归测试
pgrust是用Rust重写的PostgreSQL 18.3,通过了所有46k+回归测试,旨在通过Rust和AI辅助使Postgres更易于修改。它是磁盘兼容的,并且可以从现有的Postgres数据目录启动。