PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Spring Boot RESTful Service mit gleicher DB Transaktion über mehrere Requests


registrierter Gast
2016-09-07, 20:18:57
Hallo.

Aktuell stehe ich auf dem Schlauch.

Vorhanden ist ein RESTful Service in Spring Boot mit einer Datenbank Anbindung via Hibernate.

Nun ist es so, dass es mehrere, aufeinander folgende REST Requests gibt, welche alle zu einer DB Transaktion gehören. Die Transaktion wird mit einem abschließenden REST Request committed oder rollbacked.

Ein TransactionManager ist da, aber ich sehe bisher nicht, wie mir dieser weiterhelfen kann.

Wie kann ich die Transaktion der jeweiligen Requests steuern? Welche Transaktion gelten soll, wird über eine ebenfalls mitgelieferte TransaktionsID bestimmt.

Viele Grüße
gereggter Gast

Gast
2016-09-07, 21:10:50
Hallo.

Aktuell stehe ich auf dem Schlauch.

Vorhanden ist ein RESTful Service in Spring Boot mit einer Datenbank Anbindung via Hibernate.

Nun ist es so, dass es mehrere, aufeinander folgende REST Requests gibt, welche alle zu einer DB Transaktion gehören. Die Transaktion wird mit einem abschließenden REST Request committed oder rollbacked.

Ein TransactionManager ist da, aber ich sehe bisher nicht, wie mir dieser weiterhelfen kann.

Wie kann ich die Transaktion der jeweiligen Requests steuern? Welche Transaktion gelten soll, wird über eine ebenfalls mitgelieferte TransaktionsID bestimmt.

Viele Grüße
gereggter Gast
Na wie wäre es wenn du dir einfach mal das Interface anschaust oder besser dein Design überdenkst? Falls dir letzteres zu schwer ist, dann schau dir dir Methoden suspend und resume an.


http://docs.oracle.com/javaee/7/api/javax/transaction/TransactionManager.html

registrierter Gast
2016-09-08, 08:35:20
Leider steht mir mit Spring kein JEE zur Verfügung. ;) Es handelt sich um diesen TransactionManager mit sehr übersichtlichem Interface: http://docs.spring.io/autorepo/docs/spring-framework/3.2.17.RELEASE/javadoc-api/org/springframework/transaction/PlatformTransactionManager.html

Mein Design würde ich gerne überdenken. Wie meinst du das?
Die REST Schnittstelle steht indes fest und kann nicht geändert werden.

Frucht-Tiger
2016-09-08, 09:03:50
Ich denke er meint damit, dass es keine gute Idee ist die Transaktion über einen längeren Zeitraum offen zu halten.

Besser wäre es die Daten in einer Session für den Client vorzuhalten (Server-Side State) oder noch besser, ganz darauf zu verzichten und die Daten erstmal "unter Vorbehalt" in der Datenbank abzulegen.

Ganon
2016-09-08, 09:07:39
Wenn du das so machst, verletzt du übrigens das REST-Prinzip der Zustandslosigkeit. Es ist in dem Sinne dann keine REST API mehr... aber egal:

Was wir außerhalb einer REST API mal kurzweilig getan haben ist die UserID mit den nötigen Daten in der Datenbank (in dem Fall in Redis) zu cachen, um halt beim nächsten Request darauf zugreifen zu können.

Ich weiß jetzt natürlich nicht ob dir bei Transaktionen nicht der Garbage Collector zwischen haut und dir die Transaktion dank des fehlenden Commits nicht einfach den ganzen Kram zurückrollt. Du solltest auch mit Spring an die unterliegende Transaktion kommen. Vielleicht hast du da ja mehr Möglichkeiten.

Du könntest auf dem Weg über eine zweite kleine DB auch eine Art "Userland Transaction" machen, indem der ganze Kram beim commit erst überhaupt zur echten Datenbank geschickt wird.

registrierter Gast
2016-09-08, 14:23:53
In einem JEE WebService gäbe es die Möglichkeit von Atomic Transactions. Damit kann eine Transaktion auf dem Server vom Client gesteuert werden.

Das ermöglicht dann ebenfalls 2-Phase-Commits.

Leider unterstützt das Apache CXF mit Spring nicht. Dann hätte man zumindest einen SOAP Service.


Wenn du das so machst, verletzt du übrigens das REST-Prinzip der Zustandslosigkeit. Es ist in dem Sinne dann keine REST API mehr...

Gut zu wissen, wußte ich gar nicht.

Was wir außerhalb einer REST API mal kurzweilig getan haben ist die UserID mit den nötigen Daten in der Datenbank (in dem Fall in Redis) zu cachen, um halt beim nächsten Request darauf zugreifen zu können.

Ich weiß jetzt natürlich nicht ob dir bei Transaktionen nicht der Garbage Collector zwischen haut und dir die Transaktion dank des fehlenden Commits nicht einfach den ganzen Kram zurückrollt. Du solltest auch mit Spring an die unterliegende Transaktion kommen. Vielleicht hast du da ja mehr Möglichkeiten.

Du könntest auf dem Weg über eine zweite kleine DB auch eine Art "Userland Transaction" machen, indem der ganze Kram beim commit erst überhaupt zur echten Datenbank geschickt wird.
Es ist relevant, dass alles innerhalb einer Transaktion geschieht.

Man denke nur mal an eine Reisebuchung, welche verschiedene Subsysteme gleichzeitig und transaktionssicher buchen muss. Wenn ein Zugriff schief geht, dürfen die anderen Systeme nicht buchen.

Da darf man nicht im Cache zwischenzuspeichern oder "unter Vorbehalt" in der Datenbank ablegen, in der Hoffnung, dass das finale Commit dann schon klappen wird oder man zumindest in allen Subsystemen problemlos stornieren kann.

Insofern sehe ich das Design gar nicht als verkehrt an. Im "richtigen" Enterprise Umfeld ist das mehr Gang und Gäbe. Eher die Wahl des Mittels "Spring", welches so etwas nur Low-Level unterstützt. Aber auf das manuelle Zusammenstöpseln von Queries in Zeiten von Repositories habe ich auch keine Lust.

Ganon
2016-09-08, 14:29:34
Transaktionssicher muss aber nicht heißen, dass das unbedingt eine Datenbanktransaktion ist. Gerade große Buchungssysteme wie du beschreibst laufen über mehr als eine Datenbank und da kann man gar keine "eine" DB-Transaktionen benutzen. Es muss sich am Ende einfach nur so verhalten wie eine. 2-Phasen Commit, XA-Transaktion...

Aber da auch dort hin und wieder mal Doppelbuchungen auf einem Sitzplatz auftauchen können, zeigt, dass auch diese nicht perfekt sind.

ORM Systeme wie Hibernate cachen ja ganz von alleine und vollkommen transparent deine Datenbankwerte. D.h. dein "Accounts.find(1)" muss nicht zwangsläufig in einem DB-Zugriff enden.

Das soll jetzt keine Kritik an der Idee sein, sondern nur, dass eine REST-API mit dem Aufbau eher ungünstig ist, da dort angenommen wird, dass jeder Request atomar ist. Vermutlich musst du einiges an "Automagie" rausreißen aus dem System

Trap
2016-09-08, 20:28:09
Nun ist es so, dass es mehrere, aufeinander folgende REST Requests gibt, welche alle zu einer DB Transaktion gehören. Die Transaktion wird mit einem abschließenden REST Request committed oder rollbacked.
Das ist ein konzeptionelles Modell, das von einem kooperierenden Client ausgeht. Ist das keine öffentliche API?

Aquaschaf
2016-09-10, 20:40:03
Dieses Konzept zu kennen lohnt sich in dem Zusammenhang: http://kellabyte.com/2012/05/30/clarifying-the-saga-pattern/

Ich würde aber falls möglich erst einmal überlegen ob es nicht möglich wäre die REST-API so zu ändern dass alles in einem Aufruf abgearbeitet werden kann.

Marscel
2016-09-13, 20:25:15
Ich denke er meint damit, dass es keine gute Idee ist die Transaktion über einen längeren Zeitraum offen zu halten.

Richtig, das kann ein RDBMS ins Koma versetzen. Je nach Isolationsgrad und Last erstickt das System an Transaktionsbuchhaltung oder Warten, wenn Dinge unabsehbar lange uncommitted gehalten werden.

Achill
2016-10-15, 20:46:44
Überspannende Transaktionen stammen aus einen anderen Zeitalter und haben sicherlich ihr Berechtigung - in begrenzten Bereichen.

Wie meine Vorredner schon geschrieben haben, sind REST-Operation atomar und sollten innerhalb ihrer Ausführung die Transaktion auch wieder schließen.

Die Idee von überspannenden Transaktionen (Kopplung von Client-Request and Service-Transaktionen) bringt einen ganzen Sack an Problemen mit: Was passiert wenn der Client aufhört zu kommunizieren und die Transaktion offen bleibt? Wann timed man aus? Was passiert wenn der Client dann doch antwortet? Was passiert, wenn ein anderer Client einen Zwischenzustand sehen muss? ...

M.E. ist das keine Lösung sondern schafft nur noch mehr Probleme und es wurde kein Grund geschrieben (für mich), warum man es braucht. Die Argumentation am Beispiel der Reisebuchung macht es nur noch schlimmer. Hier wird die Abhängigkeiten zwischen den Systemen weiter ausgebaut und implizit die Client-Server-Transaktion über weitere System gespannt.

Eine Lösung könnt das (mentale) Loslösen von Transaktionen sein. Damit meine ich nicht, die "atomare" Transaktion um ein Zustandsänderung für ein Objekt(netz) zu speichern, sondern das man das SW-System so gestaltet, das es keine Transaktionen zwischen Calls und/oder Systemgrenzen braucht.

Das Pattern heißt "Event-Sourcing" / "Event-Processing" und beinhaltet ganz stark zusammengefasst und verallgemeinert die Idee, jede Zustandsänderung als Event zu beschrieben. Jedes Event ist imutable und die Ketter aller Events zu einer Entität gibt dessen aktuellen Zustand wieder:
http://www.confluent.io/blog/making-sense-of-stream-processing/
https://www.authorea.com/users/13936/articles/86622/_show_article

Nach diesem Pattern ist jeden deiner Rest-Calls ein Event, an bestimmten Stellen können andere Event erzeugt werden wie: Kommunikation mit einen anderen System, Erfolgreicher Abschluss, Verwerfen der Entität - die Entitäten verschwinden dabei nicht automatisch jedoch ist ihr Zustand immer definiert.

Damit Daten nicht immer weiter wachsen bzw. die Ketten berechenbar bleiben, kann man "Snapshots" (Aggregation von mehreren Events) erzeugen sowie alte Event-Ketten (die nicht mehr bebraucht werden) löschen.