メモリ内でのみPostgreSQLを実行する


102

私が書いたユニットテストごとに、メモリのみで実行される小さなPostgreSQLデータベースを実行したいと思います。例えば:

@Before
void setUp() {
    String port = runPostgresOnRandomPort();
    connectTo("postgres://localhost:"+port+"/in_memory_db");
    // ...
}

理想的には、単体テストで使用するバージョンコントロールに単一のpostgres実行可能ファイルをチェックインします。

のようなものHSQLですが、postgres用です。どうやってやるの?

そのようなPostgresバージョンを入手できますか?ディスクを使用しないように指示するにはどうすればよいですか?

回答:


48

これはPostgresでは不可能です。HSQLDBやMySQLのようなインプロセス/インメモリエンジンは提供していません。

自己完結型の環境を作成する場合、PostgresバイナリをSVNに配置できます(ただし、単一の実行可能ファイルだけではありません)。

これで何かを行う前に、initdbを実行してテストデータベースをセットアップする必要があります。これは、バッチファイルから、またはRuntime.exec()を使用して行うことができます。ただし、initdbは高速ではないことに注意してください。あなたは間違いなく、各テストに対してそれを実行したくないでしょう。ただし、テストスイートの前にこれを実行することもできます。

ただし、これは可能ですが、専用のPostgresをインストールして、テストを実行する前にテストデータベースを再作成することをお勧めします。

テンプレートデータベースを使用してテストデータベースを再作成できます。これにより、データベースを非常に高速に作成できます(各テスト実行でinitdbを実行するよりもはるかに高速です)。


8
下記のErwinによる2番目の回答が正しい回答としてマークされているようです
vfclists

3
@vfclists実際、ramdisk上のテーブルスペースは本当に悪い考えです。そんなことしないで。postgresql.org/docs/devel/static/manage-ag-tablespaces.html、stackoverflow.com / q / 9407442/398670
Craig Ringer

1
@CraigRinger:この特定の質問を明確にするために:貴重なデータと混ぜることは悪い考えです(そして警告をありがとう)。専用DBクラスターを使用した単体テストの場合、ramdiskで問題ありません。
Erwin Brandstetter 2015

1
docker-useが当たり前のように、一部の人々はtestcontainers本質的に、テストの起動を使い捨てのドッキングされたpostgres-instanceにすることができるのようなツールで成功しています。github.com/testcontainers/testcontainers-java/blob/master/…を
Hans Westerbeek

1
@ekcrisp。それはPostgresの真の組み込みバージョンではありません。これは、Postgresインスタンスを(別のプロセスで)簡単に開始するためのラッパーライブラリです。Postgresは引き続きJavaアプリケーションの「外部」で実行され、JVMを実行する同じプロセスに「埋め込まれる」ことはありません
a_horse_with_no_name

77

インメモリPostgreSQLの使用から私の回答を移動して一般化する):

あなたはインプロセス、インメモリでPgを実行することはできません

テストのためにインメモリPostgresデータベースを実行する方法がわかりません。出来ますか?

いいえ、できません。PostgreSQLはCで実装され、プラットフォームコードにコンパイルされます。H2やDerbyとは異なり、jarをロードして使い捨てのインメモリDBとして起動することはできません。

Cでも記述され、プラットフォームコードにコンパイルされるSQLiteとは異なり、PostgreSQLもインプロセスでロードできません。マルチスレッド化ではなくマルチプロセッシングアーキテクチャであるため、複数のプロセス(接続ごとに1つ)が必要です。マルチプロセッシング要件手段は、あなたがしなければならないスタンドアロン・プロセスとしてのpostmasterを起動します。

代わりに、接続を事前設定してください

特定のホスト名/ユーザー名/パスワードが機能することを期待してテストをCREATE DATABASE作成し、使い捨てデータベースをテストハーネスDROP DATABASEに設定して、実行の最後に実行することをお勧めします。プロパティファイルからデータベース接続の詳細を取得し、ターゲットプロパティ、環境変数などをビルドします。

単体テストに指定するユーザーがスーパーユーザーではなくCREATEDB権限を持つユーザーである限り、気になるデータベースがすでに存在する既存のPostgreSQLインスタンスを使用しても安全です。最悪の場合、他のデータベースでパフォーマンスの問題が発生します。そのため、テストのために完全に分離されたPostgreSQLインストールを実行することを好みます。

代わりに、使い捨てのPostgreSQLインスタンスをテスト用に起動します

あなたがしている場合は別の方法として、本当にあなたは可能性が熱心持っているあなたのテストハーネスを見つけinitdbおよびpostgres実行バイナリ、initdb修正、データベースを作成するのpg_hba.conftrust実行し、postgresDBを作成し、ユーザーを作成し、ランダムなポート上で起動して、テストを実行します。複数のアーキテクチャのPostgreSQLバイナリをjarにバンドルし、テストを実行する前に、現在のアーキテクチャのバイナリを一時ディレクトリに解凍することもできます。

個人的には、それは避けなければならない大きな痛みだと思います。テストDBを構成するだけの方が簡単です。ただし、でのinclude_dirサポートの登場により、少し簡単になりましたpostgresql.conf。これで、1行追加するだけで、残りすべての生成された構成ファイルを書き込むことができます。

PostgreSQLを使用したより高速なテスト

テスト目的でPostgreSQLのパフォーマンスを安全に改善する方法の詳細については、このトピックで以前に書いた詳細な回答を参照してください:高速テストのためのPostgreSQLの最適化

H2のPostgreSQL方言は真の代用ではありません

代わりに、PostgreSQL方言モードでH2データベースを使用してテストを実行する人もいます。これは、テストにSQLiteを使用し、本番環境にPostgreSQLを使用しているRailsの人々と同じくらい悪いと思います。

H2はいくつかのPostgreSQL拡張機能をサポートし、PostgreSQL方言をエミュレートします。ただし、それだけです-エミュレーション。H2がクエリを受け入れるがPostgreSQLは受け入れない、動作が異なるなどの領域があります。また、執筆時点で、ウィンドウ関数のように、PostgreSQLがH2だけでは実行できない処理をサポートしている場所もたくさんあります。

このアプローチの制限を理解していて、データベースへのアクセスが簡単であれば、H2は大丈夫かもしれません。しかしその場合は、とにかく興味深い機能を使用していないため、データベースを抽象化するORMのより良い候補となるでしょう。その場合、データベースの互換性を気にする必要はありません。

テーブルスペースは答えではありません!

「インメモリ」データベースの作成にテーブルスペースを使用しないでください。とにかくパフォーマンスを大幅に向上させることができないので、それは不必要であるだけでなく、同じPostgreSQLインストールで気になる可能性のある他のすべてへのアクセスを中断する優れた方法でもあります。9.4ドキュメントには、次の警告が含まれています

警告

メインのPostgreSQLデータディレクトリの外にある場合でも、テーブルスペースはデータベースクラスターの不可欠な部分であり、データファイルの自律的なコレクションとして扱うことはできません。これらはメインデータディレクトリに含まれるメタデータに依存しているため、別のデータベースクラスターにアタッチしたり、個別にバックアップしたりすることはできません。同様に、テーブルスペースを失うと(ファイルの削除、ディスク障害など)、データベースクラスターが読み取り不能になるか、起動できなくなる可能性があります。ramdiskのような一時ファイルシステムにテーブルスペースを配置すると、クラスター全体の信頼性が低下します。

あまりにも多くの人がこれをやっていて、トラブルに遭遇していることに気づいたからです。

(これを行った場合mkdir、不足しているテーブルスペースディレクトリを使用してPostgreSQLを再起動できますDROP。不足しているデータベースやテーブルなどがあります。実行しない方がよいでしょう。)


1
ここで提供される警告については不明です。ユニットテストを高速に実行しようとしているのに、なぜクラスタが必要なのですか?これは、私のローカルで使い捨てのPGのインスタンスだけにあるのではないですか?(1つの)クラスターが破損している場合、それが問題になるのはなぜか、とにかくそれを削除することを計画していたからです。
ゲイツ副社長、

1
@GatesVP PostgreSQLは、PostgreSQLインスタンス(データディレクトリ、データベースのコレクション、ポストマスターなど)を指すために、「クラスター」という用語を少し変わった方法で使用します。したがって、「計算クラスタ」という意味の「クラスタ」ではありません。はい、それは厄介であり、私はその用語が変わるのを見たいのですが。そして、それが使い捨ての場合はもちろん問題ありませんが、人々は定期的にPostgreSQLのインストールで使い捨てのメモリ内テーブルスペースを作成しようと試みますが、そうでなければ他の方法で必要なデータが含まれています。それは問題だ。
クレイグリンガー、

OK、それは「私が思ったこと」「非常に怖いこと」の両方です。RAMDriveソリューションは明らかに、有用なデータを含まないローカルDBにのみ属しています。しかし、なぜ自分のマシンではないマシンに対してユニットテストを実行したいのでしょうか。あなたの答えに基づいて、Tablespaces + RamDiskは、ローカルマシンでのみ実行されているPGSQLの実際の単体テストインスタンスに対して完全に正当に聞こえます。
ゲイツ副社長、

1
@GatesVP一部の人々は、ローカルマシンで自分が気にしていることを続けます-これは問題ありませんが、同じDBインストールに対してユニットテストを実行するのは少しばかげています。しかし、人々は愚かです。また、適切なバックアップを保持しないものもあります。嘆きが続く。
クレイグリンガー、

いずれにせよ、ramdiskオプションを使用する場合は、ramdiskにもWALが本当に必要なので、initdbそこにまったく新しいPgをインストールすることもできます。しかし、実際には、通常のストレージ(fsync = offおよびその他のデータの耐久性/安全機能がオフ)での高速テスト用に微調整されたPgは、少なくともLinuxでは、ramdiskで実行する場合とほとんど違いがありません。
クレイグリンガー、

66

または、ramfs / tempfsにTABLESPACEを作成し、そこにすべてのオブジェクトを作成することもできます。
私は最近、Linuxでそれを正確に行うことについて記事を指摘されました。

警告

これにより、データベースクラスタ全体の整合性が損なわれる可能性があります。
マニュアルに追加された警告を読んでください。
したがって、これは消費可能なデータのオプションにすぎません。

以下のためにユニットテストのそれはうまく動作するはずです。同じマシンで他のデータベースを実行している場合は、安全を確保するために、別のデータベースクラスター(独自のポートを持つ)を使用してください。


4
これは悪いアドバイスだと思います。こんなことしないで。代わりinitdbに、tempfsまたはramdisk内の新しいpostgresインスタンス。tempfsなどでテーブルスペースを使用しないでください。テーブルスペースは壊れやすく、無意味です。通常のテーブルスペースを使用してテーブルを作成する方がよいでしょうUNLOGGED-それは同様に実行されます。また、DB全体の整合性を危険にさらすような行動をとらない限り、WALのパフォーマンスとfsyncの要素には対応しません(stackoverflow.com/q/9407442/398670を参照)。しないでください。
クレイグリンガー

29

OpenTableの埋め込みPostgreSQLコンポーネント(https://github.com/opentable/otj-pg-embedded)を介して、JUnitテストでPostgreSQLのメモリ内インスタンスを実行できるようになりました。

依存関係をotj-pg-embeddedライブラリ(https://mvnrepository.com/artifact/com.opentable.components/otj-pg-embedded)に追加することにより、PostgreSQLの独自のインスタンスを@Beforeおよび@Aferフック:

EmbeddedPostgres pg = EmbeddedPostgres.start();

これらは、JUnitが自動的にPostgreSQLデータベースサーバーを起動および停止するJUnitルールを提供します。

@Rule
public SingleInstancePostgresRule pg = EmbeddedPostgresRules.singleInstance();

1
6か月後のこのパッケージの体験はいかがですか。うまく機能しますか、それともバグだらけですか?
オリゴフレン2018年

@Rubms JUnit5に移行しましたか?の置き換えをどのように使用@Rule@ExtendWithますか?.start()inを使用するだけ@BeforeAllですか?
フランキードレイク

私はJUnit5に移行していないため、まだ質問に答えることはできません。ごめんなさい。
ラブム

これはうまくいきました。ありがとう。:あなたのような場合は、あなたの春の設定でデータソースを作成するには、次の使用DataSource embeddedPostgresDS = EmbeddedPostgres.builder().start().getPostgresDatabase();
Sackyサン

12

TestContainersを使用して、テスト用のPosgreSQL Dockerコンテナーを起動できます:http ://testcontainers.viewdocs.io/testcontainers-java/usage/database_containers/

TestContainersはJUnit @ Rule / @ ClassRuleを提供します。このモードは、テストの前にコンテナー内のデータベースを開始し、後でそれを破棄します。

例:

public class SimplePostgreSQLTest {

    @Rule
    public PostgreSQLContainer postgres = new PostgreSQLContainer();

    @Test
    public void testSimple() throws SQLException {
        HikariConfig hikariConfig = new HikariConfig();
        hikariConfig.setJdbcUrl(postgres.getJdbcUrl());
        hikariConfig.setUsername(postgres.getUsername());
        hikariConfig.setPassword(postgres.getPassword());

        HikariDataSource ds = new HikariDataSource(hikariConfig);
        Statement statement = ds.getConnection().createStatement();
        statement.execute("SELECT 1");
        ResultSet resultSet = statement.getResultSet();

        resultSet.next();
        int resultSetInt = resultSet.getInt(1);
        assertEquals("A basic SELECT query succeeds", 1, resultSetInt);
    }
}

7

Yandexという名前のロシアの検索会社からのPostgreSQLのインメモリバージョンがあります:https : //github.com/yandex-qatools/postgresql-embedded

Flapdoodle OSSの埋め込みプロセスに基づいています。

使用例(githubページから):

// starting Postgres
final EmbeddedPostgres postgres = new EmbeddedPostgres(V9_6);
// predefined data directory
// final EmbeddedPostgres postgres = new EmbeddedPostgres(V9_6, "/path/to/predefined/data/directory");
final String url = postgres.start("localhost", 5432, "dbName", "userName", "password");

// connecting to a running Postgres and feeding up the database
final Connection conn = DriverManager.getConnection(url);
conn.createStatement().execute("CREATE TABLE films (code char(5));");

しばらく使っています。それはうまくいきます。

更新:このプロジェクトはもはや積極的に維持されていません

Please be adviced that the main maintainer of this project has successfuly 
migrated to the use of Test Containers project. This is the best possible 
alternative nowadays.

1
複数のスレッドを使用したり、JVMまたはMonoランタイムを組み込んだり、独自の子プロセスをfork()したりする場合、あらゆる種類の新しいエキサイティングな方法で爆発する必要があります。編集:実際には埋め込まれていません。単なるラッパーです。
クレイグリンガー

3

また、PostgreSQLの構成設定(質問やここで受け付ける回答など)を使用して、インメモリデータベースに頼ることなくパフォーマンスを実現することもできます。


OPの主な問題は、パフォーマンスではなく、開発環境およびCI環境での単体テストのブートストラップを簡単にするために、Postgresインスタンスをメモリ内で起動することです。
triple.vee

0

NodeJSを使用している場合、pg-mem(免責事項:私は作成者です)を使用して、postgres dbの最も一般的な機能をエミュレートできます。

PGの動作をレプリケートする完全なインメモリの分離された、プラットフォームにとらわれないデータベースがあります(ブラウザでも実行されます)。

単体テストでの使用方法を示す記事をここに書きました。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.