Temporäres Schema pro Verbindung?

8

Ich versuche, meine Unit-Tests von H2 nach Postgresql zu migrieren.

Derzeit gibt mir H2 ein speicherinternes Schema, sodass jede Verbindung einem eindeutigen Schema zugeordnet ist, die Tabellen erstellt, den Test ausführt und das Schema löscht. Die Erstellung und Zerstörung des Schemas wird automatisch von H2 übernommen.

Die Komponententests werden gleichzeitig ausgeführt.

Was ist der beste Weg, dies in Postgresql zu tun? Speziell,

  1. Wie erhalte ich ein eindeutiges Schema pro Verbindung?
    • Sollte das Testframework eindeutige Namen generieren oder gibt es dafür einen integrierten Mechanismus?
  2. Wie stelle ich sicher, dass das Schema getrennt wird, wenn die Verbindung getrennt wird?
    • Ich möchte nicht mit baumelnden Schemata enden, wenn Unit-Tests getötet werden.
  3. Welcher Ansatz liefert die höchste Leistung?
    • Ich muss Dutzende von Schemas pro Sekunde erstellen / löschen.

UPDATE : Ich habe hier eine verwandte Antwort gefunden, aber es werden keine Schemas gelöscht, falls der Prozess, der die Komponententests ausführt, abgebrochen wird.

Gili
quelle

Antworten:

13

pg_temp ist ein Alias ​​für das temporäre Schema der aktuellen Sitzung.

Wenn Sie SET search_path TO pg_tempvor dem Ausführen Ihrer Tests eine ausführen, sollte alles nur funktionieren (solange nichts explizit auf ein Schema verweist).

Wenn Sie Ihr Skript überhaupt nicht ändern möchten, legen Sie search_pathfür den Benutzer, bei dem sich die Tests anmelden, Folgendes fest:

> ALTER ROLE testuser SET search_path = pg_temp;

Dann befindet sich alles, was der Benutzer erstellt, in pg_temp, sofern nicht ausdrücklich angegeben.

Hier ist ein Beispiel aus psql, das das tatsächliche Schema (für diese Verbindung) zeigt, in das der Alias ​​aufgelöst wird:

> SET search_path TO pg_temp;
SET
> create table test();
CREATE TABLE
> \dt test
          List of relations
  Schema   | Name | Type  |  Owner
-----------+------+-------+----------
 pg_temp_4 | test | table | postgres
(1 row)

Und wie zu erwarten ist, unterscheidet sich dieses Schema für jede gleichzeitige Verbindung und ist nach dem Schließen der Verbindung nicht mehr vorhanden.

Beachten Sie, dass dies auch für Funktionen funktioniert, obwohl Sie beim Aufrufen explizit auf das Schema pg_temp verweisen müssen.

hbn
quelle
Aber pg_tempstimmt ein einzelnes Schema? Wenn ich also gleichzeitig Unit-Tests durchführe, werden sie dann nicht die Tabellen / Daten des anderen überfrachten?
Gili
1
Nein, es ist ein Alias ​​für das temporäre Schema der aktuellen Sitzung. Ich werde die Antwort mit einem Beispiel aktualisieren.
hbn
Denken Sie daran, dass beim Schließen und Öffnen einer Verbindung möglicherweise dasselbe temporäre Schema angezeigt wird, das jedoch geleert wurde. Öffnen Sie 2 gleichzeitig, um zu sehen, wie verschiedene zugewiesen werden. Sie können das temporäre Schema einer anderen Sitzung nur sehen, wenn Sie ein Superuser sind.
hbn
Sicher, ich habe einen Kommentar von Ihnen gesehen, in dem Sie gefragt haben, wann Sie dies einstellen sollen. Wie auch immer - es wird pro Sitzung festgelegt, wenn Sie nur eine SET search_pathausführen. Verwenden Sie SET LOCAL search_path, um pro Subtransaktion festzulegen, oder wenn Sie möchten, können Sie auf Benutzerebene mit ALTER USER mytestuser SET search_path = 'pg_temp'oder auf Datenbankebene mitALTER DATABASE mytestdb SET search_path = 'pg_temp'
hbn
Gibt es aus Neugier eine Möglichkeit, diese Funktion für Funktionen ohne explizite Schema-Referenz auszuführen? Oder ist das für das pg_tempSchema unmöglich ?
Gili
3

Sie können den Namen des aktuellen temporären Schemas (nach dem Erstellen der ersten temporären Tabelle) wie in dem von Ihnen hinzugefügten Link angegebenen abrufen:

SELECT nspname
FROM   pg_namespace
WHERE  oid = pg_my_temp_schema();

Aber Ihr aktueller Plan würde immer noch nicht viel Sinn machen. Um Tabellen im aktuellen temporären Schema zu erstellen, erstellen Sie einfach temporäre Tabellen. Das ist alles. Standardmäßig search_pathist das so definiert, dass temporäre Tabellen zuerst sichtbar sind. Ein nie muss Schema qualifizieren temporäre Tabellen. Sie sollten das aktuelle temporäre Schema niemals direkt ansprechen müssen - das ist ein Implementierungsdetail.

Erwin Brandstetter
quelle
Einverstanden, dass es ein Hack ist, aber es ist möglicherweise wesentlich einfacher als die Parametrisierung des Erstellungscodes, um die Erstellung temporärer Tabellen zu ermöglichen.
hbn
Guter Punkt, außer wie @hbn erwähnt, möchte ich, dass Unit-Tests und Produktionscode dasselbe SQL-Skript ausführen. Ersteres sollte gegen ein temporäres Schema ausgeführt werden, letzteres jedoch nicht.
Gili
@hbn, aus Neugier, wie würde parametrisierter Erstellungscode aussehen? Ich benutze flywaydb.org und es führt nur einfache SQL-Dateien aus (keine Variablen). Ich möchte diesen Weg wahrscheinlich nicht gehen. Ich bin nur neugierig, worum es geht.
Gili
Ich habe flywaydb noch nie benutzt. Auf einer sehr einfachen Ebene können Sie eine Sprache für Textvorlagen (z. B. Jinja2 in Python) verwenden, um Ihre Erstellungsskripte vorzuverarbeiten, und optional eine "temporäre" hinzufügen, wenn Sie eine Tabelle erstellen. Wenn Sie explizit Funktionen erstellen, ist der Hack zum Abrufen des temporären Schemas wahrscheinlich unvermeidlich, da Sie (soweit ich weiß) keine temporäre Funktion direkt erstellen können.
hbn
@hbn , If you're explicitly sequences ...: Ich denke dein letzter Kommentar enthielt einen Tippfehler. Was wolltest du zwischen explicitlyund sagen sequences?
Gili
1

Umfassen Ihre Tests Transaktionen? DDL ist in PostgreSQL transaktional. Wenn Sie also Ihr Schema und Ihre Tabellen erstellen und dann Ihre Tests ausführen, alles innerhalb einer einzigen Transaktion, die dann zurückgesetzt wird, wird das Schema niemals festgeschrieben und ist für andere Sitzungen sichtbar.

Sie müssten weiterhin einen wahrscheinlich eindeutigen Namen für Ihr Schema verwenden (möglicherweise Hostname und PID), da CREATE SCHEMAdieser sofort fehlschlägt, wenn bereits ein Schema mit identischem Namen vorhanden ist, und blockiert, wenn eine andere Sitzung ein Schema mit identischem Namen in erstellt hat eine nicht festgeschriebene Transaktion.

Eine Alternative wäre möglicherweise nur die Verwendung temporärer Tabellen, wenn Sie Ihre Datenbankerstellungsskripts dazu ändern können.

hbn
quelle
Netter Trick, aber in meinem Fall würde es nicht funktionieren, da ein einzelner Test über mehrere Transaktionen hinweg ausgeführt wird. Jede Testmethode ist ein Webclient, der mehrere serverseitige Transaktionen auslöst. Beispielsweise wird ein Benutzer erstellt, abgefragt und gelöscht. Jeder Aufruf ist eine separate HTTP-Anforderung und wird in einer eigenen Transaktion ausgeführt.
Gili
Fairerweise war mein Ansatz sehr begrenzt.
hbn
@Gili: Beachten Sie, dass diese Technik, niemals zu begehen, CREATE SCHEMAdie einzige ist, die garantieren kann, dass sie verschwinden, wenn der Komponententest beendet wird.
Daniel Vérité
0

Ich habe gerade eine Idee.

Postgresql garantiert, dass eine Sitzung die temporären Tabellen einer anderen Sitzung nicht sehen kann. Ich vermute, dies bedeutet, dass beim Erstellen einer temporären Tabelle ein temporäres Schema erstellt wird. Vielleicht könnte ich folgendes tun:

  1. Erstellen Sie eine (Dummy-) temporäre Tabelle und suchen Sie das Schema.
  2. Verwenden Sie dieses Schema für den Test (erstellen Sie die Tabellen, führen Sie den Test aus).
  3. Wenn die Verbindung geschlossen wird, löscht Postgresql das Schema.

Ich mag es nicht, mich auf Implementierungsdetails zu verlassen, aber in diesem Fall scheint dies ziemlich sicher zu sein.

Gili
quelle