Ich habe einen Webdienst (http api), mit dem ein Benutzer eine Ressource in Ruhe erstellen kann. Nach der Authentifizierung und Validierung übergebe ich die Daten an eine Postgres-Funktion und erlaube ihr, die Autorisierung zu überprüfen und die Datensätze in der Datenbank zu erstellen.
Ich habe heute einen Fehler gefunden, als zwei http-Anfragen innerhalb derselben Sekunde gestellt wurden, wodurch diese Funktion zweimal mit identischen Daten aufgerufen wurde. Innerhalb der Funktion gibt es eine Klausel, die eine Auswahl in einer Tabelle vornimmt, um festzustellen, ob ein Wert vorhanden ist. Wenn er vorhanden ist, nehme ich die ID und verwende sie bei meiner nächsten Operation. Wenn dies nicht der Fall ist, füge ich die Daten ein Sichern Sie die ID und verwenden Sie diese bei der nächsten Operation. Unten ist ein einfaches Beispiel.
select id into articleId from articles where title = 'my new blog';
if articleId is null then
insert into articles (title, content) values (_title, _content)
returning id into articleId;
end if;
-- Continue, using articleId to represent the article for next operations...
Wie Sie wahrscheinlich erraten können, habe ich ein Phantom auf die Daten gelesen, bei denen beide Transaktionen in den if articleId is null then
Block eingegeben und versucht haben, sie in die Tabelle einzufügen. Einer war erfolgreich und der andere explodierte aufgrund einer einzigartigen Einschränkung für ein Feld.
Ich habe mich umgesehen, wie ich mich dagegen verteidigen kann, und ein paar verschiedene Optionen gefunden, aber keine scheint aus einigen Gründen unseren Bedürfnissen zu entsprechen, und ich habe Mühe, Alternativen zu finden.
insert ... on conflict do nothing/update...
Ich habe mir zuerst dieon conflict
Option angesehen, die gut aussah, aber die einzige Option ist,do nothing
die dann nicht die ID des Datensatzes zurückgibt, der die Kollision verursacht hat, unddo update
nicht funktioniert, da dies dazu führt, dass Trigger ausgelöst werden, wenn in Wirklichkeit die Daten vorliegen hat sich nicht geändert. In einigen Fällen ist dies kein Problem, aber in vielen Fällen können Sitzungen Benutzersitzungen ungültig machen, was wir nicht tun können.set transaction isolation level serializable;
Dies scheint die attraktivste Antwort zu sein, aber selbst unsere Testsuite kann Lese- / Schreibabhängigkeiten verursachen, bei denen wir wie oben einfügen möchten, wenn etwas nicht vorhanden ist, und es zurückgeben möchten, wenn dies der Fall ist, und weitere Vorgänge fortsetzen möchten. Wenn mehrere Transaktionen anstehen, die den obigen Code ausführen, führt dies zu einem Lese- / Schreibabhängigkeitsfehler, wie in der Transaktions-ISO der Postgres-Dokumente beschrieben .
Wie soll diese Art von gleichzeitiger Lese- / Schreibtransaktion behandelt werden?
Weder ich noch mein Team behaupten, Datenbankexperten zu sein, geschweige denn Postgres-Experten, aber ich bin der Meinung, dass dies ein gelöstes Problem sein muss oder dass eine Person in der Vergangenheit auf sie gestoßen ist. Wir sind offen für Vorschläge. Wenn die oben angegebenen Informationen nicht ausreichen, kommentieren Sie dies bitte und ich werde bei Bedarf weitere Informationen hinzufügen.
if new is not distinct from old then return new; end if;
an die Spitze aller Aktualisierungsauslöser.