PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

CREATE SCHEMA IF NOT EXISTS löst einen doppelten Schlüsselfehler aus

Dies ist ein kleiner Fehler in der Implementierung von IF NOT EXISTS für Tabellen und Schemata. Im Grunde handelt es sich um einen Upsert-Versuch, und PostgreSQL behandelt die Race-Bedingungen nicht sauber. Es ist sicher, aber hässlich.

Wenn das Schema gleichzeitig in einer anderen Sitzung erstellt wird, aber noch nicht festgeschrieben ist, dann existiert es und existiert nicht, je nachdem, wer Sie sind und wie Sie aussehen. Andere Transaktionen können das neue Schema in den Systemkatalogen nicht „sehen“, da es nicht festgeschrieben ist, also in pg_namespace eingetragen ist ist für andere Transaktionen nicht sichtbar. Also CREATE SCHEMA / CREATE TABLE versucht, es zu erstellen, weil das Objekt seiner Meinung nach nicht existiert.

Dadurch wird jedoch eine Zeile mit einer eindeutigen Einschränkung in eine Tabelle eingefügt. Eindeutige Einschränkungen müssen in der Lage sein, nicht festgeschriebene Zeilen zu sehen, um zu funktionieren. Das Einfügen blockiert (stoppt) also bis zur ersten Transaktion, die CREATE ausgeführt hat entweder festschreibt oder zurückrollt. Wenn es festgeschrieben wird, bricht die zweite Transaktion ab, weil sie versucht hat, eine Zeile einzufügen, die gegen eine eindeutige Einschränkung verstößt. CREATE SCHEMA ist nicht klug genug, um diesen Fall zu erkennen und es erneut zu versuchen.

Um dieses PostgreSQL richtig zu reparieren, wäre wahrscheinlich eine Prädikatsperre erforderlich, bei der es das Potenzial für eine Zeile sperren könnte . Dies könnte als Teil der aktuellen Arbeit zur Implementierung von UPSERT hinzugefügt werden .

Für diese speziellen Befehle könnte PostgreSQL wahrscheinlich einen dirty read durchführen der Systemkataloge, wo nicht festgeschriebene Änderungen angezeigt werden. Dann könnte es warten, bis die nicht festgeschriebene Transaktion festgeschrieben oder rückgängig gemacht wird, das Dirty Read erneut durchführen, um zu sehen, ob jemand anderes wartet, und es erneut versuchen. Dies hätte jedoch eine Race-Condition zur Folge, bei der jemand anderes das Schema zwischen dem Lesen zum Prüfen und dem Versuch, es zu erstellen, erstellen könnte.

Also der IF NOT EXISTS Varianten müssten:

  • Überprüfen Sie, ob das Schema existiert; Wenn ja, beenden Sie, ohne etwas zu tun.
  • Versuchen Sie, die Tabelle zu erstellen
  • Wenn die Erstellung aufgrund eines eindeutigen Einschränkungsfehlers fehlschlägt, versuchen Sie es am Anfang erneut
  • Wenn die Tabellenerstellung erfolgreich ist, beenden Sie sie

Soweit ich weiß, hat das niemand implementiert, oder sie haben es versucht und es wurde nicht akzeptiert. Bei diesem Ansatz könnten Probleme mit der Transaktions-ID-Verbrennungsrate usw. auftreten.

Ich denke, das ist eine Art Fehler, aber es ist ein "Ja, wir wissen"-Fehler, kein "Wir werden uns gleich darum kümmern"-Fehler. Fühlen Sie sich frei, darüber auf pgsql-bugs zu posten; zumindest sollte die Dokumentation diesen Vorbehalt bezüglich IF NOT EXISTS erwähnen .

Ich empfehle nicht, DDL so gleichzeitig zu verwenden.