Pgtestdb 通过模板克隆进行测试的方法非常快。
Pgtestdb's template cloning approach to testing is fast

原始链接: https://brandur.org/fragments/pgtestdb

作者将利用 Postgres 模板数据库进行测试的 Go 语言包 `pgtestdb`,与 River 项目中现有的基于模式(schema)的测试方法进行了对比。 虽然 `pgtestdb` 在引导数据库方面的速度出人意料地快(平均约为 100 毫秒),但在设置时间上与作者自定义的模式测试方法表现相当。然而,River 目前的测试套件整体运行速度要快 3.5 倍。这种效率并非源于模式本身,而是得益于一套强大的“池化”与重用机制——它在测试之间循环利用现有的模式,而不是从零开始生成新的模式。 最终,作者认为,尽管 `pgtestdb` 是一款出色且高性能的工具(尤其适用于端到端测试),但要使大型测试套件达到极致速度,仍需实施巧妙的资源重用策略。他们计划在文档中推荐 `pgtestdb`,同时保留现有的基于模式的系统,并指出模式隔离和可靠重用所带来的收益值得为此投入实现成本。

这篇 Hacker News 帖子讨论了 `pgtestdb` 的优势,这是一款利用模板克隆技术实现高性能数据库测试的工具。 用户称赞了该工具的速度,并指出其性能可与 River 等高级测试套件中的“黄金标准”相媲美。参与者强调 `pgtestdb` 是端到端测试的绝佳解决方案,开发者可以通过它验证从客户端作业插入到工作进程完全结束的整个工作流。一位开发者分享道,他们将 `pgtestdb` 与 LLM 生成的初始框架结合使用,从而有效地学习和测试了 River 的功能。讨论还涉及了进一步改进的可能性,例如实现连接池以提高效率。
相关文章

原文

I was reminded by Cup o’ Go yesterday of the existence of Peter Downs’ pgtestdb, a Go/Postgres testing package.

pgtestdb is built around Postgres template databases, a built-in feature that you can try right from a vanilla psql shell:

CREATE DATABASE dbname TEMPLATE template_to_copy;

Copying a template is very fast, more so than migrating a test database from scratch, and much more so than some of the heavyweight Docker-based techniques some projects are using these days. At a low level, Postgres enumerates the template’s relations and copies their materialized heap, index, and catalog files in 8 kB page chunks.

I remember reading about this feature years ago, but to be honest I’d forgotten it existed, and I was curious how it performed compared to other testing approaches, so I had Codex splice pgtestdb into River’s test suite to see how it’d fare.

I like to think that River’s testing methodology is more or less a gold standard for speed and reliability. It uses a custom set of test helpers that isolate test cases based on schema, an approach that’s slower than test transactions, but which has some advantages:

  • Leaves test state in place to examine in case of a failing test.
  • Enables testing of database-wide features like listen/notify.
  • Enables testing edges around multiple transactions interacting and rollbacks.

In Postgres a schema is lighter weight than a database, so the schema-based approach has that edge. However, you can’t clone a schema, so the schema-based approach has to run migrations every time, giving pgtestdb a distinct advantage in that respect.

That should give us an interesting comparison. Here are the results I got:

Method Count Mean p90 p95 Max
pgtestdb clone 466 98.4ms 247.4ms 299.5ms 465.1ms
Create + migrate schema 81 99.4ms 152.1ms 209.0ms 327.0ms

What we find is that the timing of both approaches is remarkably similar, right around 100 ms of setup time.

I’d always internalized that anything involving creating new databases would be relatively slow, so I was surprised at how fast pgtestdb’s approach turned out to be here.

I’m going to leave River’s tests on its existing schema-based method given it’s already fast and testing schema isolation is incidentally useful in verifying that River’s schema-based configuration works as advertised, but I’m going to add a recommendation in our docs for pgtestdb, particularly for users aiming to test end-to-end (i.e. job inserted by client → fully completed by worker).

I was sandbagging a little above. Although setup time for the schema-based approach is similar to pgtestdb’s full databases, overall the test suite runs ~3.5x faster on the former:

Method Wall time
pgtestdb clone 51.07s
Create + migrate schema 14.54s

But it’s not because schemas are that much faster. River’s test helpers have a useful optimization in that they’ll create as many test schemas as Go’s instantaneous parallelization requires, but keep them pooled as test cases finish. If an unclaimed schema is ready, a test case will clean and reuse it instead of generating a new one from scratch .

This is a little easier said than done because you need to think about details like schema version – i.e. when testing across schema versions, each test case must only reuse a schema on the same version it expects. This is very doable, of course, but takes a little thought. I wrote River’s implementation pre-LLM, and it took me a few days to squeeze all the bugs out.

I mention reuse because it could be done with pgtestdb as well, potentially as part of the package, or as an augmentation in projects that call into it. 100 ms to bootstrap a test database is pretty fast, but if you’re building a full application that’s going to have 10,000 tests, ideally you want a test setup on the order of 10x faster. Reuse gets it down to 10-20 ms, and more in line with test transactions.

联系我们 contact @ memedata.com