Ausführen von PostgreSQL nur im Speicher

101

Ich möchte für jeden von mir geschriebenen Komponententest eine kleine PostgreSQL-Datenbank ausführen, die nur im Speicher ausgeführt wird. Zum Beispiel:

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

Im Idealfall wird eine einzelne ausführbare Postgres-Datei in die Versionskontrolle eingecheckt, die vom Komponententest verwendet wird.

So etwas wie HSQL, aber für Postgres. Wie kann ich das machen?

Kann ich eine solche Postgres-Version bekommen? Wie kann ich es anweisen, die Festplatte nicht zu verwenden?

Chi-Lan
quelle

Antworten:

48

Dies ist mit Postgres nicht möglich. Es bietet keine In-Process / In-Memory-Engine wie HSQLDB oder MySQL.

Wenn Sie eine in sich geschlossene Umgebung zu schaffen , möchten Sie können die Postgres - Binärdateien in SVN setzen (aber es ist mehr als nur eine einzige ausführbare Datei).

Sie müssen initdb ausführen, um Ihre Testdatenbank einzurichten , bevor Sie etwas damit tun können. Dies kann aus einer Batch-Datei oder mit Runtime.exec () erfolgen. Beachten Sie jedoch, dass initdb nicht schnell ist. Sie werden das definitiv nicht für jeden Test ausführen wollen. Sie könnten dies jedoch vor Ihrer Testsuite ausführen.

Obwohl dies möglich ist, würde ich eine dedizierte Postgres-Installation empfehlen, bei der Sie einfach Ihre Testdatenbank neu erstellen, bevor Sie Ihre Tests ausführen.

Sie können die Testdatenbank mithilfe einer Vorlagendatenbank neu erstellen, wodurch die Erstellung sehr schnell erfolgt ( viel schneller als das Ausführen von initdb für jeden Testlauf).

ein Pferd ohne Name
quelle
8
Es sieht so aus, als ob die zweite Antwort von Erwin unten als die richtige Antwort markiert werden sollte
vfclists
3
@vfclists Eigentlich ist ein Tablespace auf einer Ramdisk eine wirklich schlechte Idee. Tu das nicht. Siehe postgresql.org/docs/devel/static/manage-ag-tablespaces.html , stackoverflow.com/q/9407442/398670
Craig Ringer
1
@CraigRinger: Um diese spezielle Frage zu klären: Es ist eine schlechte Idee, mit wertvollen Daten zu mischen (und danke für die Warnung). Für Unit-Tests mit einem dedizierten DB-Cluster ist eine Ramdisk in Ordnung.
Erwin Brandstetter
1
Da Docker-Verwendung an der Tagesordnung ist, waren einige Leute mit einem Tool wie erfolgreich, mit testcontainersdem Ihr Test im Wesentlichen eine wegwerfbare, dockerisierte Postgres-Instanz starten kann. Siehe github.com/testcontainers/testcontainers-java/blob/master/…
Hans Westerbeek
1
@ekcrisp. Das ist keine echte eingebettete Version von Postgres. Es ist nur eine Wrapper-Bibliothek, um das Starten einer Postgres-Instanz (in einem separaten Prozess) zu vereinfachen. Postgres wird weiterhin "außerhalb" der Java-Anwendung ausgeführt und nicht "eingebettet" in denselben Prozess, in dem die JVM ausgeführt wird
a_horse_with_no_name
77

(Verschieben meiner Antwort von der Verwendung von PostgreSQL im Speicher und Verallgemeinern):

Sie können Pg nicht im Prozess oder im Speicher ausführen

Ich kann nicht herausfinden, wie die Postgres-Datenbank im Speicher zum Testen ausgeführt wird. Ist es möglich?

Nein, das ist nicht möglich. PostgreSQL ist in C implementiert und zu Plattformcode kompiliert. Im Gegensatz zu H2 oder Derby können Sie die nicht einfach laden jarund als wegwerfbare In-Memory-Datenbank starten.

Im Gegensatz zu SQLite, das ebenfalls in C geschrieben und zu Plattformcode kompiliert ist, kann PostgreSQL auch nicht in Bearbeitung geladen werden. Es sind mehrere Prozesse erforderlich (einer pro Verbindung), da es sich um eine Multiprozessor- und keine Multithreading-Architektur handelt. Die Multiprozessor-Anforderung bedeutet, dass Sie den Postmaster als eigenständigen Prozess starten müssen .

Stattdessen: Konfigurieren Sie eine Verbindung vor

Ich schlage vor, einfach Ihre Tests zu schreiben, um zu erwarten, dass ein bestimmter Hostname / Benutzername / Passwort funktioniert, und den Test am Ende des Laufs CREATE DATABASEeine Wegwerfdatenbank zu verwenden DROP DATABASE. Rufen Sie die Datenbankverbindungsdetails aus einer Eigenschaftendatei ab, erstellen Sie Zieleigenschaften, Umgebungsvariablen usw.

Es ist sicher, eine vorhandene PostgreSQL-Instanz zu verwenden, in der Sie bereits Datenbanken haben, die Sie interessieren, solange der Benutzer, den Sie für Ihre Komponententests angeben, kein Superuser ist, sondern nur ein Benutzer mit CREATEDBRechten. Im schlimmsten Fall verursachen Sie Leistungsprobleme in den anderen Datenbanken. Aus diesem Grund bevorzuge ich eine vollständig isolierte PostgreSQL-Installation zum Testen.

Stattdessen: Starten Sie eine wegwerfbare PostgreSQL-Instanz zum Testen

Alternativ , wenn Sie wirklich keen könnten Sie Ihre Testumgebung haben suchen Sie die initdbund postgresBinärdateien, führen Sie initdbeine Datenbank zu erstellen, ändern , pg_hba.confum trust, führen postgressie an einem zufälligen Port zu starten, einen Benutzer erstellen, erstellen Sie eine DB, und die Tests ausführen . Sie können sogar die PostgreSQL-Binärdateien für mehrere Architekturen in einem JAR bündeln und die für die aktuelle Architektur in ein temporäres Verzeichnis entpacken, bevor Sie die Tests ausführen.

Persönlich denke ich, dass dies ein großer Schmerz ist, der vermieden werden sollte; Es ist viel einfacher, nur eine Test-DB zu konfigurieren. Mit dem Aufkommen der include_dirUnterstützung in wird es jedoch ein wenig einfacher postgresql.conf. Jetzt können Sie nur noch eine Zeile anhängen und für den Rest eine generierte Konfigurationsdatei schreiben.

Schnelleres Testen mit PostgreSQL

Weitere Informationen dazu, wie Sie die Leistung von PostgreSQL zu Testzwecken sicher verbessern können, finden Sie in einer ausführlichen Antwort, die ich zuvor zu diesem Thema geschrieben habe: Optimieren Sie PostgreSQL für schnelle Tests

Der PostgreSQL-Dialekt von H2 ist kein echter Ersatz

Einige Benutzer verwenden stattdessen die H2-Datenbank im PostgreSQL-Dialektmodus, um Tests auszuführen. Ich denke, das ist fast so schlimm wie die Rails-Leute, die SQLite zum Testen und PostgreSQL für die Produktionsbereitstellung verwenden.

H2 unterstützt einige PostgreSQL-Erweiterungen und emuliert den PostgreSQL-Dialekt. Es ist jedoch nur das - eine Emulation. Sie werden Bereiche , wo H2 eine Abfrage übernimmt aber PostgreSQL nicht der Fall, wo das Verhalten unterscheidet, etc . Es gibt auch viele Stellen, an denen PostgreSQL etwas unterstützt, was H2 zum Zeitpunkt des Schreibens einfach nicht kann - wie Fensterfunktionen.

Wenn Sie die Einschränkungen dieses Ansatzes verstehen und Ihr Datenbankzugriff einfach ist, ist H2 möglicherweise in Ordnung. Aber in diesem Fall sind Sie wahrscheinlich ein besserer Kandidat für ein ORM, das die Datenbank abstrahiert, weil Sie die interessanten Funktionen sowieso nicht nutzen - und in diesem Fall müssen Sie sich nicht mehr so ​​sehr um die Datenbankkompatibilität kümmern.

Tablespaces sind nicht die Antwort!

Sie nicht einen Tabellen verwenden , um eine „in-memory“ Datenbank zu erstellen. Dies ist nicht nur unnötig, da es die Leistung ohnehin nicht wesentlich verbessert, sondern auch eine großartige Möglichkeit, den Zugriff auf andere zu stören, die Sie in derselben PostgreSQL-Installation interessieren könnten. Die 9.4-Dokumentation enthält jetzt die folgende Warnung :

WARNUNG

Obwohl sich Tablespaces außerhalb des PostgreSQL-Hauptdatenverzeichnisses befinden, sind sie ein integraler Bestandteil des Datenbankclusters und können nicht als autonome Sammlung von Datendateien behandelt werden. Sie hängen von den im Hauptdatenverzeichnis enthaltenen Metadaten ab und können daher nicht an einen anderen Datenbankcluster angehängt oder einzeln gesichert werden. Wenn Sie einen Tabellenbereich verlieren (Löschen von Dateien, Festplattenfehler usw.), ist der Datenbankcluster möglicherweise nicht mehr lesbar oder kann nicht mehr gestartet werden. Das Platzieren eines Tablespace in einem temporären Dateisystem wie einer Ramdisk gefährdet die Zuverlässigkeit des gesamten Clusters.

weil ich bemerkte, dass zu viele Leute dies taten und in Schwierigkeiten gerieten.

(Wenn Sie dies getan haben, können Sie mkdirdas fehlende Tablespace-Verzeichnis verwenden, um PostgreSQL erneut zu starten, dann DROPdie fehlenden Datenbanken, Tabellen usw. Es ist besser, es einfach nicht zu tun.)

Craig Ringer
quelle
1
Ich bin mir nicht sicher über die Warnung hier. Wenn ich versuche, Unit-Tests schnell auszuführen, warum ist dann ein Cluster beteiligt? Sollte dies nicht alles auf meiner lokalen, wegwerfbaren Instanz von PG sein? Wenn der Cluster (von einem) beschädigt ist, warum ist das wichtig? Ich hatte vor, ihn trotzdem zu löschen.
Gates VP
1
@GatesVP PostgreSQL verwendet den Begriff "Cluster" auf etwas seltsame Weise, um sich auf die PostgreSQL-Instanz (Datenverzeichnis, Sammlung von Datenbanken, Postmaster usw.) zu beziehen. Es handelt sich also nicht um einen "Cluster" im Sinne von "Compute Cluster". Ja, das ist ärgerlich, und ich würde gerne sehen, dass sich die Terminologie ändert. Und wenn es wegwerfbar ist, spielt es natürlich keine Rolle, aber die Leute versuchen regelmäßig, einen wegwerfbaren In-Memory- Tablespace in einer PostgreSQL-Installation zu haben, der Daten enthält, die ihnen sonst wichtig sind . Das ist ein Problem.
Craig Ringer
OK, das ist sowohl "was ich dachte" als auch "sehr beängstigend" . Die RAMDrive-Lösung gehört eindeutig nur zu einer lokalen Datenbank, die keine nützlichen Daten enthält. Aber warum sollte jemand Unit-Tests für eine Maschine durchführen wollen, die nicht seine eigene Maschine ist? Basierend auf Ihrer Antwort klingt Tablespaces + RamDisk für eine tatsächliche Unit Test-Instanz von PGSQL, die ausschließlich auf Ihrem lokalen Computer ausgeführt wird, absolut legitim.
Gates VP
1
@GatesVP Einige Leute behalten Dinge, die ihnen wichtig sind, auf ihrem lokalen Computer - was in Ordnung ist, aber es ist dann ein bisschen albern, Unit-Tests für dieselbe DB-Installation durchzuführen. Die Leute sind allerdings albern. Einige von ihnen führen auch keine ordnungsgemäßen Backups. Klagen folgen.
Craig Ringer
In jedem Fall, wenn Sie die Ramdisk-Option wählen, möchten Sie wirklich auch WAL auf der Ramdisk, also können Sie dort auch initdbeine ganz neue Pg installieren. Aber wirklich, es gibt kaum einen Unterschied zwischen einer Pg, die für schnelle Tests auf normalem Speicher optimiert wurde (fsync = off und andere deaktivierte Funktionen für Datenbeständigkeit / Sicherheit), als auf einer Ramdisk, zumindest unter Linux.
Craig Ringer
66

Oder Sie können einen TABLESPACE in einem ramfs / tempfs erstellen und dort alle Ihre Objekte erstellen.
Ich wurde kürzlich auf einen Artikel hingewiesen, in dem es darum geht, genau das unter Linux zu tun .

Warnung

Dies kann die Integrität Ihres gesamten Datenbankclusters gefährden .
Lesen Sie die hinzugefügte Warnung im Handbuch.
Dies ist also nur eine Option für Verbrauchsdaten.

Für Unit-Tests sollte es gut funktionieren. Wenn Sie andere Datenbanken auf demselben Computer ausführen, müssen Sie aus Sicherheitsgründen einen separaten Datenbankcluster (der über einen eigenen Port verfügt) verwenden.

Erwin Brandstetter
quelle
4
Ich denke wirklich, dass dies ein schlechter Rat ist. Mach das nicht. Stattdessen initdbeine neue Postgres-Instanz in einem Tempfs oder einer Ramdisk. Sie nicht eine Tabelle in einem tempfs usw. verwenden, es ist zerbrechlich und sinnlos. Verwenden Sie besser einen normalen Tabellenbereich und erstellen Sie UNLOGGEDTabellen - dies funktioniert ähnlich. Die WAL-Leistung und die fsync-Faktoren werden nur berücksichtigt, wenn Sie Maßnahmen ergreifen, die die Integrität der gesamten Datenbank gefährden (siehe stackoverflow.com/q/9407442/398670 ). Tu es nicht.
Craig Ringer
29

Jetzt ist es möglich, eine In-Memory-Instanz von PostgreSQL in Ihren JUnit-Tests über die eingebettete PostgreSQL-Komponente von OpenTable auszuführen: https://github.com/opentable/otj-pg-embedded .

Durch Hinzufügen der Abhängigkeit zur in otj-pg eingebetteten Bibliothek ( https://mvnrepository.com/artifact/com.opentable.components/otj-pg-embedded ) können Sie Ihre eigene PostgreSQL-Instanz in Ihrem @Before und starten und stoppen @ Nach Haken:

EmbeddedPostgres pg = EmbeddedPostgres.start();

Sie bieten sogar eine JUnit-Regel an, mit der JUnit Ihren PostgreSQL-Datenbankserver automatisch für Sie startet und stoppt:

@Rule
public SingleInstancePostgresRule pg = EmbeddedPostgresRules.singleInstance();
Rubms
quelle
1
Wie ist Ihre Erfahrung mit diesem Paket sechs Monate später? Funktioniert gut oder voller Fehler?
Oligofren
@Rubms Haben Sie auf JUnit5 migriert? Wie benutzt man den Ersatz des @Rulemit @ExtendWith? Verwenden Sie einfach die .start()in @BeforeAll?
Frankie Drake
Ich bin nicht auf JUnit5 migriert, daher kann ich Ihre Frage noch nicht beantworten. Es tut uns leid.
Rubms
Das hat gut funktioniert. Vielen Dank. Verwenden Sie Folgendes, um eine Datenquelle in Ihrer Frühlingskonfiguration zu erstellen, wenn Sie DataSource embeddedPostgresDS = EmbeddedPostgres.builder().start().getPostgresDatabase();
möchten
12

Sie könnten verwenden TestContainers einen PosgreSQL Docker Behälter für Tests spin up: http://testcontainers.viewdocs.io/testcontainers-java/usage/database_containers/

TestContainer bieten eine JUnit @ Rule / @ ClassRule : Dieser Modus startet eine Datenbank in einem Container vor Ihren Tests und reißt sie anschließend ab.

Beispiel:

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);
    }
}
Andrejs
quelle
7

Es gibt jetzt eine In-Memory-Version von PostgreSQL von der russischen Suchfirma Yandex: https://github.com/yandex-qatools/postgresql-embedded

Es basiert auf dem Einbettungsprozess von Flapdoodle OSS.

Anwendungsbeispiel (von der Github-Seite):

// 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));");

Ich benutze es einige Zeit. Es funktioniert gut.

AKTUALISIERT : Dieses Projekt wird nicht mehr aktiv gepflegt

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.
Akvyalkov
quelle
1
Das muss auf alle möglichen neuen und aufregenden Arten explodieren, wenn Sie mehrere Threads verwenden, eine JVM- oder Mono-Laufzeit einbetten, Ihre eigenen untergeordneten Prozesse gabeln () oder ähnliches. Bearbeiten : Es ist nicht wirklich eingebettet, es ist nur ein Wrapper.
Craig Ringer
3

Sie können auch PostgreSQL-Konfigurationseinstellungen verwenden (wie die in der Frage und der akzeptierten Antwort hier beschriebenen ), um Leistung zu erzielen, ohne unbedingt auf eine speicherinterne Datenbank zurückgreifen zu müssen.

Dan
quelle
Das Hauptproblem des OP besteht darin, eine Postgres-Instanz im Arbeitsspeicher hochzufahren, nicht aus Gründen der Leistung, sondern aus Gründen der Einfachheit beim Booten von Unit-Tests in einer Entwicklungs- und CI-Umgebung.
Triple.vee
0

Wenn Sie NodeJS verwenden, können Sie pg-mem (Haftungsausschluss: Ich bin der Autor) verwenden, um die häufigsten Funktionen einer Postgres-Datenbank zu emulieren.

Sie verfügen über eine vollständige speicherinterne, isolierte, plattformunabhängige Datenbank, die das PG-Verhalten repliziert (sie wird sogar in Browsern ausgeführt ).

Ich schrieb einen Artikel zu zeigen , wie es zu benutzen für Ihr Gerät testet hier .

Olivier
quelle