プログラミング
Pgtestdbのテンプレートクローニングによるテストアプローチは高速
Pgtestdb's template cloning approach to testing is fast (brandur.org)
要約
Postgresのテンプレートデータベース機能を利用したpgtestdbパッケージは、テストデータベースのセットアップにおいて、スキーマ移行よりも高速ではないものの、非常に高速なアプローチを提供します。しかし、テストスイート全体の実行速度では、スキーマベースのアプローチが再利用最適化により約3.5倍速く、pgtestdbはエンドツーエンドテストなどに推奨されるものの、再利用の最適化を適用することでさらに高速化できる可能性が示唆されています。
全文翻訳
Pgtestdbのテンプレートクローニングによるテストアプローチは高速
公開日 Jul 29, 2026
昨日、Cup o’ GoからPeter Downs氏のGo/Postgresテストパッケージであるpgtestdbの存在を思い出しました。pgtestdbはPostgresのテンプレートデータベースを中心に構築されています。これは、バニラのpsqlシェルからすぐに試せる組み込み機能です。
CREATE DATABASE dbname TEMPLATE template_to_copy;
テンプレートのコピーは非常に高速で、テストデータベースをゼロからマイグレーションするよりも、そして最近のヘビー級Dockerベースの手法よりもはるかに高速です。低レベルでは、Postgresはテンプレートのリレーションを列挙し、そのマテリアライズされたヒープ、インデックス、およびカタログファイルを8 kBのページチャンクでコピーします。
この機能については数年前に読んだ記憶がありますが、正直なところ忘れていました。他のテストアプローチと比較してどの程度パフォーマンスが出るのか興味があったため、CodexにpgtestdbをRiverのテストスイートに組み込んでもらい、その結果を見てみました。Riverのテスト手法は、速度と信頼性においてゴールドスタンダードに近いと考えています。スキーマに基づいてテストケースを分離するカスタムテストヘルパーセットを使用しています。このアプローチはテストトランザクションよりも遅いですが、いくつかの利点があります。
失敗したテストを検査するためにテスト状態を残しておける。
listen/notifyのようなデータベース全体機能をテストできる。
複数のトランザクションの相互作用やロールバックのエッジケースをテストできる。
結果
Postgresでは、スキーマはデータベースよりも軽量であるため、スキーマベースのアプローチにはその利点があります。しかし、スキーマをクローンすることはできないため、スキーマベースのアプローチは毎回マイグレーションを実行する必要があり、その点でpgtestdbは明確な利点があります。これは興味深い比較になるはずです。
得られた結果は以下の通りです。
手法 カウント 平均 p90 p95 最大
pgtestdb clone 466 98.4ms 247.4ms 299.5ms 465.1ms
Create + migrate schema 81 99.4ms 152.1ms 209.0ms 327.0ms
両方のアプローチのセットアップ時間は、約100msという驚くほど似通ったタイミングであることがわかります。新しいデータベースの作成には比較的時間がかかるものだと常々思っていたので、pgtestdbのアプローチがここでこれほど高速であることがわかり驚きました。Riverのテストは既存のスキーマベースの方法でそのままにしておきます。これはすでに高速であり、スキーマ分離のテストはRiverのスキーマベースの設定が謳い文句通りに機能することを確認するのに役立つためです。しかし、ドキュメントにはpgtestdbを推奨する項目を追加するつもりです。特に、エンドツーエンド(つまり、クライアントによって挿入されたジョブがワーカーによって完全に完了する)のテストを目指すユーザーには役立つでしょう。
再利用による最適化
上記では少し控えめに言いました。スキーマベースのアプローチのセットアップ時間はpgtestdbのフルデータベースに似ていますが、テストスイート全体の実行速度は前者の方が約3.5倍高速です。
手法 ウォールタイム
pgtestdb clone 51.07s
Create + migrate schema 14.54s
しかし、これはスキーマがそれほど速いからではありません。Riverのテストヘルパーには、Goの瞬間的な並列化に必要な数のテストスキーマを作成し、テストケースが終了したらそれらをプールしておくという便利な最適化があります。もし利用可能なスキーマがあれば、テストケースは新しいスキーマをゼロから生成するのではなく、それをクリーンアップして再利用します1。これは、スキーマバージョンなどの詳細を考慮する必要があるため、少し言うは易く行うは難しです。つまり、スキーマバージョンをまたいでテストする場合、各テストケースは期待するバージョンと同じバージョンのスキーマのみを再利用する必要があります。これはもちろん非常に可能ですが、少し考えが必要です。私はLLM以前にRiverの実装を書きましたが、バグをすべて取り除くのに数日かかりました。再利用について言及したのは、pgtestdbでも同様のことが可能であり、パッケージの一部として、あるいはそれを呼び出すプロジェクトでの拡張として実現できる可能性があるからです。
テストデータベースをブートストラップするのに100msというのはかなり速いですが、10,000件のテストがあるフルアプリケーションを構築している場合、理想的には10倍高速なテストセットアップが必要です。再利用により、これは10〜20msにまで低下し、テストトランザクションにより近いものになります。
1テストケースが失敗した場合、そのスキーマは再利用されず、検査/デバッグのために状態が利用可能になります。
間違いを犯しましたか?プルリクエストを送ることを検討してください。