# Wie würdet ihr vorgehen: Datensatzsperrung (mit Hibernate)



## sparrow (16. Mrz 2008)

Hallo Forum,

diese Frage betrifft kein aktuelles Problem, sondern mich interessiert die allgemeine Vorgehensweise.

Vor einiger Zeit, damals habe ich gerade angefangen mit PostgreSQL zu arbeiten, habe ich mir Gedanken gemacht wie man wohl Datensätze sperren kann um möglichst effektiv arbeiten zu können.

Als Beispiel habe ich folgende Situation konstruiert:
Nehmen wir an wir sprechen von einer Software für Kinos. Bestandteil dieser Software ist natürlich auch das Drucken der Eintrittskarten. Im Hintergrund muss natürlich der etwaige Sitz gesperrt werden.
Jetzt startet also der große neue Blockbuster, es ist Freitag 19:45 Uhr und alle vier Kassen sind geöffnet um Karten für die Abendvorstellung zu verkaufen.
In dieser Situation ist es sehr wahrscheinlich, dass es mehrere Zugriffe auf den selben Datensatz (Sitzplatz in einer Vorstellung) geben kann, trotzdem ist es wichtig, dass es keine falsche Auswertung gibt.

Folgende Lösung funktioniert unter PostgreSQL:
Der Client der einen, oder mehrere Sitze, reservieren will startet eine Transaktion und führt danach _SELECT sitz FOR UPDATE_ aus, checkt ob diese Sitze schon reserviert sind, führt (falls sie es nicht sind) ein update aus und sendet den commmit.
Das ganze könnte in etwas so aussehen:
Wenn zeitgleich ein anderer Client versucht diese Plätze zu reservieren stoppt die Abfrage bei dem SELECT FOR UPDATE weil dieser Befehl offensichtlich eine Datensatzsperrung vornimmt. Die Abfrage läuft weiter sobald die andere Anfrage abgeschlossen ist und selektiert dabei das Ergebnis der ersten Abfrage.
Somit ist eine Konsistenz der Daten absolut gegeben.

Würdet ihr in diesem Fall ähnlich vorgehen?

Was genau macht ihr, wenn es sich um Fälle handelt die weniger kurzfristig mit Daten arbeiten? Zum Beispiel wenn jemand einen Datensatz aus dem Kundenstamm einer Warenwirtschaft bearbeiten würde. Würdet ihr dann eine Verbindung herstellen, eine Transaktion beginnen und die Verbindung die ganze Zeit offen lassen um den Datensatz vor einen Schreibzugriff durch andere Clients zu schützen?


Zuletzt noch eine Frage:
Wie würde man so etwas wohl unter Hibernate angehen? Dort ist man ja für die Verbinungen selber nicht mehr verantwortlich.


Einen schönen Wochenanfang!

Sparrow


----------



## Guest (17. Mrz 2008)

Das Zauberwort heisst Optimistic Locking.


----------



## sparrow (17. Mrz 2008)

Optimistic Locking finde ich für das Beispiel mit dem Kino eher unpassend. Wobei ja das kurze sperren und updaten des Datensatzes dem ganzen sehr ähnlich ist.

Für das Beispiel mit dem Kundenstamm bin ich mir eher unsicher. Was passiert denn wenn jemand sich einen Datensatz aufmacht, diesen umfassend bearbeitet (dafür natürlich eine Weile braucht) und beim speichern kommt es dann zu einem Fehler á la "Dieser Datensatz wurde in der Zwischenzeit von einem anderen Benutzer bearbeitet, ein Speichern ist daher nicht möglich", nur weil ein anderer Benutzer schnell die Leerzeichen in der Telefonnummer entfernt hat.

Ich stehe Optimistic Locking eher skeptisch gegenüber was Desktop-Anwendungen angeht. Bisher bin ich immer so vorgegangen, dass, wie im ersten Post beschrieben, ein Datensatz gesperrt wird sobald man in den "Bearbeiten"-Modus des Datensatzes geht. Bei PostgreSQL ist es nach einem SELECT ... FOR UPDATE durchaus noch möglich mit einem SELECT den Datensatz zu lesen, nur ein Update und ein SELECT ... FOR UPDATE wartet bis der Datensatz wieder frei gegeben ist.

Gruß
Sparrow


----------



## maki (17. Mrz 2008)

Ich würde da folgendermassen vorgehen:
Client-Server Anwendung, und nein, die DB ist keine "ganzer" Server, vielmehr eine Java (EE) Anwendung die als einzige direkt mit der DB kommuniziert (über hibernate).
Sperren von Datensätzen auf DB Ebene würde ich gar nicht machen, sondern der Server "sperrt" Entitäten, hat eine Liste von denen die gerade in Bearbeitung sind. Timeout nicht vergessen.


----------



## Guest (17. Mrz 2008)

sparrow hat gesagt.:
			
		

> Für das Beispiel mit dem Kundenstamm bin ich mir eher unsicher. Was passiert denn wenn jemand sich einen Datensatz aufmacht, diesen umfassend bearbeitet (dafür natürlich eine Weile braucht) und beim speichern kommt es dann zu einem Fehler á la "Dieser Datensatz wurde in der Zwischenzeit von einem anderen Benutzer bearbeitet, ein Speichern ist daher nicht möglich", nur weil ein anderer Benutzer schnell die Leerzeichen in der Telefonnummer entfernt hat.


Wie wahrscheinlich ist so ein Szenario überhaupt? Sowas passiert einmal alle paar Schaltjahre.



			
				sparrow hat gesagt.:
			
		

> Ich stehe Optimistic Locking eher skeptisch gegenüber was Desktop-Anwendungen angeht. Bisher bin ich immer so vorgegangen, dass, wie im ersten Post beschrieben, ein Datensatz gesperrt wird sobald man in den "Bearbeiten"-Modus des Datensatzes geht. Bei PostgreSQL ist es nach einem SELECT ... FOR UPDATE durchaus noch möglich mit einem SELECT den Datensatz zu lesen, nur ein Update und ein SELECT ... FOR UPDATE wartet bis der Datensatz wieder frei gegeben ist.


Bei Optimistic Locking kannst du die Daten erst recht lesen. Beim Update hättest du eine Sonderbehandlung für 
den Fall, dass ein anderer den Datensatz bearbeitet hat.
Du könntest z.B. die gespeicherte Version und die Änderungen des Benutzers, bei dem ein Update gescheitert 
ist, anzeigen (bzw. nur die Unterschiede) und den Benutzer entscheiden lassen, welche Änderungen übernommen
werden sollen. Das ganze kommt ohne Table- bzw. Row-Locks aus. 
Ähnlich funktioniert das Versionssystem. Gibt es Konflikte, müssen diese aufgelöst werden (merge), bevor man 
comit ausführt.
Bei der Ticketreservierung könnte man es so machen, dass es noch eine extra Tabelle für reservierte Plätze und
welche in Bearbeitung gibt. Dazu evtl. noch einen Batchjob, der "Plätze in Bearbeitung" nach gewisser Zeit wieder 
freigibt, falls ein Client abstürzt. Ich stelle es mir wie folgt vor.

1) Es gibt eine Benutzeroberfläche, auf der man bestimmte Plätze anklicken kann. Bereits reservierte Plätze
sind farblich anders dargestellt. Plätze, die gerade bearbeitet werden ebenfalls.
2) Klickt der Benutzer auf einen bestimmten Platz, geht es zum Server und es wird ein Eintrag "Platz so und so 
in Bearbeitung" erstellt. Dabei wird auch eine Message geschickt, so dass alle anderen Clients dies mitkriegen,
sei es nur, um die Benutzeroberfläche aktuell zu halten.
2a) Scheitert das "Auschecken" eines Platzes, da bereits so ein Datensatz existiert (ein anderer war schneller dran),
eine Info-Message ausgeben (oder einfach ein beep) und GUI aktualisieren. Wobei das zweite zu diesem Zeitpunkt
bereits geschehen sein sollte (denk an die Messages)
3) Wird die Bearbeitung eines Platzes abgeschlossen (Reservieren oder Freigeben), wird der Datensatz "Platz so 
und so in Bearbeitung" in einen "Platz so und so reserviert" geändert bzw. entfernt, falls man den Vorgang abbricht 
und eine passende Message wird in die Runde geschickt.


----------

