pgtestdb测试模板克隆速度惊人,与模式测试对比结果出人意料!

📅 2026/8/2 15:58:41
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原理pgtestdb基于Postgres的[模板数据库](https://www.postgresql.org/docs/current/manage-ag-templatedbs.html)构建这是个内置功能你能直接在普通的psql shell中尝试CREATE DATABASE dbname TEMPLATE template_to_copy;复制模板的速度极快比从头迁移测试数据库快也比当下一些项目用的基于Docker的重量级技术快得多。从底层看Postgres会列举模板的关系以8 kB页面块为单位复制其物化堆、索引和目录文件。测试动机我几年前读过关于这个功能的介绍可说实话我都忘了它的存在。我很好奇它和其他测试方法相比效果怎样就让Codex把pgtestdb集成到River的测试套件里看看效果。我觉得River的测试方法在速度和可靠性方面堪称黄金标准。它用了一组自定义的测试辅助工具基于模式隔离测试用例。这种方法比[测试事务](fragments/go-test-tx-using-t-cleanup)慢但有这些优点在测试失败时保留测试状态以便检查能测试数据库级别的功能如监听/通知能测试多个事务交互和回滚的边界情况。测试结果在Postgres中模式比数据库轻量级所以基于模式的方法有一定优势。不过你无法克隆模式所以基于模式的方法每次都得运行迁移这让pgtestdb在这方面优势明显。这会带来一场有趣的对比。以下是我的测试结果| 方法 | 数量 | 平均值 | 90%分位数 | 95%分位数 | 最大值 || --- | --- | --- | --- | --- | --- || pgtestdb克隆 | 466 | 98.4毫秒 | 247.4毫秒 | 299.5毫秒 | 465.1毫秒 || 创建并迁移模式 | 81 | 99.4毫秒 | 152.1毫秒 | 209.0毫秒 | 327.0毫秒 |我们发现两种方法耗时很相似设置时间都在100毫秒左右。我一直以为涉及创建新数据库的操作都比较慢所以pgtestdb这种方法的速度之快让我很惊讶。考虑到River现有的基于模式的测试方法已经很快而且测试模式隔离对验证River的[基于模式的配置](https://riverqueue.com/docs/alternate-schema)是否按预期工作很有用我会继续用这种方法。但我会在文档中推荐pgtestdb特别是对那些想进行端到端测试即客户端插入任务 → 工作器完全完成任务的用户。通过复用进行优化我上面的说法有点保守。虽然基于模式的方法的“设置时间”和pgtestdb的完整数据库方法相近但总体而言前者的测试套件运行速度大约快3.5倍| 方法 | 总耗时 || --- | --- || pgtestdb克隆 | 51.07秒 || 创建并迁移模式 | 14.54秒 |但这并非因为模式本身速度快很多。River的测试辅助工具做了一项有用的优化它们会根据Go的即时并行化需求创建尽可能多的测试模式在测试用例完成后把它们放入池中。如果有未被占用的模式可用测试用例将清理并复用它而不是从头生成新的模式。说起来容易做起来难因为你得考虑模式版本等细节。也就是说在跨模式版本进行测试时每个测试用例只能复用它预期版本相同的模式。当然这完全可行但需要思考。我在大语言模型出现之前编写了River的实现花了几天时间才消除所有的bug。我提到复用是因为pgtestdb也能采用这种方式可能作为包的一部分或者作为调用它的项目的增强功能。启动一个测试数据库需要100毫秒已经挺快了但如果你正在构建有10000个测试用例的完整应用程序理想情况下你希望测试设置速度快10倍。复用能将时间降到10 - 20毫秒更接近测试事务的速度。如果测试用例失败其模式不会被复用以便保留状态进行检查/调试。我是否有错误之处请考虑[提交拉取请求](https://github.com/brandur/sorg/edit/master/content/fragments/pgtestdb.md)。