Gleichzeitige Zugriffe auf Daten auf dem Server

  • Themenstarter Themenstarter tuxedo
  • Beginndatum Beginndatum
T

tuxedo

Gast
Hallo zusammen,

ich bastle hier eine Client-Server-Anwendung die auf Serverseite Daten aus der DB holt und die Objekte dem Client anbietet. Der kann sie anzeigen, modifizieren, löschen, ...

Stehe nun vor dem Design-Problem: Wie geh mich mit gleichzeitigen Zugriffen um?

Szenario:

Client 1 holt sich Objekt A und fängt an es zu editieren. Vor dem abspeichern geht er in die Mittagspause.

Client 2 ist auf Diät und arbeitet weiter. Er holt sich ebenfalls Objekt A vom Server (welches beim Client 1 noch offen ist), macht größere Änderungen und speichert diese.

Client 1 kommt aus der Mittagspause zurück und klickt speichern.​

*Fupp* Änderungen von Client2 sind nun weg.

Gibt es da Patterns wie man das am besten angeht?

Ansatz 1:
Mein erster naiver Ansatz wäre gewesen, dass der Client Objekte, die er editieren will, beim Server mit einem "exclusive edit/write-lock" versehen kann. Dann könnte Client2 ihm während der Änderung nicht dazwischen pfuschen.

Problem dabei: Was ist wenn Client1 vergisst den lock zu entfernen (bzw. speichern zu drücken)? Dann müsste ein Timeout oder so her...

Ansatz 2:
Versionierung der Datensätze. Client1 holt sich das Objekt, welches Version 1 hat. Client2 macht nun seine Änderungen vor Client1 und inkrementiert die Versionsnummer auf 2. Wenn Client1 nun speichern will, wird erkannt dass sich die Version in der zwischenzeit geändert hat (Hibernate -> Rollbackexception oder so).

Problem: Client1 muss dann irgendwie die Daten mergen. Das kann UI technisch wohl recht aufwendig/doof werden.

Ansatz 3:
Pech gehabt: Wer zuletzt speichert gewinnt.

Bisher gefällt mir Ansatz #1 am besten. Aber ich hab das mit dem Timeout noch nicht ganz ausgetüftelt.

Was sagen die Experten hier dazu?! Gibts weitere Vorgehensweisen? Gibt's irgendwo Lesestoff dazu (mir ist das passende Suchwort noch nicht eingefallen)?

Gruß
Alex
 
Nimm doch Ansatz 2 und frag den Benutzer, ob er den Datensatz aus der DB überschreiben oder den lokalen Datensatz verwerfen möchte. Der Anwender muss ja selber wissen, wie aktuell bzw. korrekt seine Eingabe ist.
 
Für kleine Datensätze mag das noch "vertretbar" sein. Aber was ist mit einer recht großen Eingabemaske (die auf mehrere Tabs verteilt ist) die vielleicht mehr als nur ein Datenobjekt benötigt?

Beispielsweise sowas (ist nicht meins, nur ein beispiel):



Da wird's dann schwer zu entscheiden was aktuell ist, ohne einen visuell aufbereiteten Datenvergleich anzuzeigen.
 
Ich hab mal die Version 1 umgesetzt. Mit einem Lock der nur 10 Minuten gültig ist und der Client (so lange er offen ist) den lock alle 5 Minuten erneuert.. Beim lock stand der user name dabei so konnten die anderen User darüber informiert werden wer den da den Datensatz gerade sperrt...
 
CDO eignet sich dafür hervorragend
CDO Model Repository
Beim CDO Eclipse Editor werden Konflikte und Remote Updates auch direkt im Editor angezeigt (sobald der Konflikt auftritt, nicht erst beim Commit), diese Funktionalität müsstest du bei einer Swing GUI natürlich selbst implementieren, aber da CDO dich per Listener genau über alle Updates/Konflikte informieren kann sollte das nicht schwierig werden. Die EMF Reflection API macht es da Gott sei Dank sehr einfach.
 
Ansatz #3 entspricht auf einer DB dem Transaktionsisolationslevel "read commited"... wer zuletzt kommt gewinnt und das ist in der Regel ausreichend und sollte als (einfachster) Ansatz nicht aus den Augen verloren werden.

Sonst würde ich Ansatz #2,5 wählen: eine Versionsüberprüfung, aber ohne Merge... wer so #&%$§! ist und so eine Aktion längere Zeit stehen lässt, bekommt halt einen Fehler und muss noch einmal anfangen. Auch so etwas akzeptieren Benutzer häufiger, als es einem der Fachbereich versucht zu erzählen ;-)
Die Versionsüberprüfung geht einfach über ein extra Feld in der DB, das bei jedem Schreiben (komplette Daten) geändert wird bzw. vor dem Schreiben überprüft wird.
 
#2 entspricht ziemlich genau dem Wikipedia-Verhalten, lass den Merge weg und erzähle dem Nutzer einfach, dass die Daten in der Zwischenzeit bereits geändert wurde.
 
Danke für die Antworten.

Ansatz 1 finde ich jetzt nmicht so aufwendig zu implementieren. Und es geht weder etwas verloren, noch besteht die Gefahr dass jemand Daten überschreibt. Nimmt man das Timout allerdings mit hinzu, so ist man teilweise schon bei Ansatz #2

Ansatz 2 finde ich auch nicht soo verkehrt. Allerdings ist das Potential für Ärger etwas größer als beim Ansatz #1

Ansatz 2.5 scheint mir eine Art Kompromiss zwischen Ansatz 1 und 2 zu sein. Den neuen Datensatz kann man im UI ja nochmal zusätzlich anzeigen lassen. Dann kann der User selbst mergen (oder auch nicht). An sich auch nicht schlecht...

Ansatz 3 ist wirklich der einfachste, aber auch der mit dem meisten (zunehmend mit der Anzahl gleichzeitig arbeitender Clients) potential für überschriebene Daten

Werde nochmal die eine oder andere Nacht drüber schlafen bevor ich mich für irgendwas um Ansatz 1-2.5 entscheide. Ansatz 3 werde ich für meinen Fall wohl nicht weiter vertiefen.

Gruß
Alex
 
Werde nochmal die eine oder andere Nacht drüber schlafen bevor ich mich für irgendwas um Ansatz 1-2.5 entscheide. Ansatz 3 werde ich für meinen Fall wohl nicht weiter vertiefen


eine vernünftige db bietet von haus aus isolation resp. lock modi an, um die daten in einer multiuser-umgebung konsistent zu halten. für das problem "stehen lassen" gibt es timeouts. welche db verwendest du?
 
Zuletzt bearbeitet:
So als Suchbegriffe:
Pessemestic Lock, Optimistic Lock, dann noch die Offline variante: Pessimistic Offline Lock

Bei einem Pessemistic (Offline) Lock ist ein Timeout immer nötig.
Generell sind optimistische locks einfacher zu implementieren (JPA bringt schon Unterstützung dafür mit der @Version Annotation), haben aber den Nachteil, dass der User u.U. erst nach umfangreichen Änderungen feststellt dass er eine veraltete Version hatte und diese nicht mehr speichern kann.

Pessemistic Locking wird direkt in der DB umgesetzt (select for update) und ist meist DB spezifisch, die offline Variante ist DB unabhängig, hat aber den Nachteil, dass man mit direktem DB Zugriff Datensätze ändern kann welche die Anwendung als gelockt betrachtet.
 
@tuttle64

PostgreSQL. Und ja, ich weiß, da geht das. Aber ich will den Client bzw. dessen Verhalten nicht an die DB koppeln. Wenn dann muss/soll das einen Layer weiter oben passieren. JPA z.B.
Und da hab ich gerade gesehen dass man eine Entity "locken" kann: Lock Entity : LockJPAJava

Wie ich's nun genau umsetze werd' ich noch sehen. Entweder auf JPA Ebene, oder in einer eigenen Ebene. Aber ich denke JPA könnte hier schon alles notwendige mitbringen.

- Alex
 
Eine Transaktion für die Konsistenz der Daten zu verwenden ist prinzipiell der beste Ansatz, schließt sich aber bei deiner Anforderung (Daten werden geholt und liegen u.U. lange rum bevor sie wieder geschrieben werden) praktisch aus:
du hast eine lang laufende Transaktion am Haken (muss eine Usertransaktion sein), da die Transaktion ja holen und schreiben der Daten umschließen muss.

... oder eben Timeouts ...
 

Zurück
Oben