Stellen Sie sich eine E-Commerce-Site vor, auf der Alice und Bob die Produktlisten bearbeiten. Alice verbessert die Beschreibungen, während Bob die Preise aktualisiert. Gleichzeitig bearbeiten sie das Acme Wonder Widget. Bob beendet zuerst und speichert das Produkt mit dem neuen Preis. Alice braucht etwas länger, um die Beschreibung zu aktualisieren, und wenn sie fertig ist, speichert sie das Produkt mit ihrer neuen Beschreibung. Leider überschreibt sie auch den Preis mit dem alten Preis, der nicht vorgesehen war.
Nach meiner Erfahrung treten diese Probleme in Webanwendungen häufig auf. Einige Softwareprodukte (z. B. Wiki-Software) sind dagegen geschützt. In der Regel schlägt der zweite Speichervorgang mit "Die Seite wurde während der Bearbeitung aktualisiert" fehl. Die meisten Websites verfügen jedoch nicht über diesen Schutz.
Es ist erwähnenswert, dass die Controller-Methoden für sich genommen thread-sicher sind. Normalerweise verwenden sie Datenbanktransaktionen, was sie in dem Sinne sicher macht, dass Alice und Bob, wenn sie im selben Moment versuchen zu speichern, keine Beschädigung verursachen. Die Rennbedingungen sind darauf zurückzuführen, dass Alice oder Bob veraltete Daten in ihrem Browser haben.
Wie können wir solche Rennbedingungen verhindern? Insbesondere würde ich gerne wissen:
- Welche Techniken können verwendet werden? zB Verfolgung des Zeitpunkts der letzten Änderung. Was sind die Vor- und Nachteile von jedem.
- Was ist eine hilfreiche Benutzererfahrung?
- In welchen Frameworks ist dieser Schutz eingebaut?
quelle
Antworten:
Sie müssen "Ihre Schreibvorgänge lesen", dh, bevor Sie eine Änderung aufzeichnen, müssen Sie den Datensatz erneut lesen und prüfen, ob seit dem letzten Lesen Änderungen daran vorgenommen wurden. Sie können dies Feld für Feld (feinkörnig) oder basierend auf einem Zeitstempel (grobkörnig) durchführen. Während Sie diese Prüfung durchführen, benötigen Sie eine exklusive Sperre für den Datensatz. Wenn keine Änderungen vorgenommen wurden, können Sie Ihre Änderungen notieren und die Sperre aufheben. Wenn sich der Datensatz in der Zwischenzeit geändert hat, brechen Sie die Transaktion ab, heben die Sperre auf und benachrichtigen den Benutzer.
quelle
Ich habe zwei Hauptwege gesehen:
Fügen Sie den Zeitstempel der letzten Aktualisierung der Seite, die Sie gerade bearbeiten, in eine ausgeblendete Eingabe ein. Wenn ein Commit ausgeführt wird, wird der Zeitstempel mit dem aktuellen verglichen und wenn er nicht übereinstimmt, wurde er von einer anderen Person aktualisiert und gibt einen Fehler zurück.
Pro: Mehrere Benutzer können verschiedene Teile der Seite bearbeiten. Die Fehlerseite kann zu einer Diff-Seite führen, auf der der zweite Benutzer seine Änderungen auf der neuen Seite zusammenführen kann.
con: Manchmal gehen große Teile des Aufwands bei großen gleichzeitigen Bearbeitungen verloren.
Wenn ein Benutzer anfängt, die Seitensperre für einen angemessenen Zeitraum zu bearbeiten, erhält ein anderer Benutzer beim Versuch, sie zu bearbeiten, eine Fehlerseite und muss warten, bis entweder die Sperre abläuft oder der erste Benutzer die Sperre bestätigt hat.
pro: edit-aufwände werden nicht verschwendet.
con: Ein skrupelloser Benutzer kann eine Seite auf unbestimmte Zeit sperren. Eine Seite mit einer abgelaufenen Sperre kann möglicherweise weiterhin ein Commit ausführen, sofern nicht anders verfahren wird (mithilfe von Technik 1).
quelle
Verwenden Sie Optimistic Concurrency Control .
Fügen Sie der betreffenden Tabelle eine versionNumber- oder versionTimestamp-Spalte hinzu (Ganzzahl ist am sichersten).
Benutzer 1 liest Datensatz:
Benutzer 2 liest Datensatz:
Benutzer 1 speichert den Datensatz, dies erhöht die Version:
Benutzer 2 versucht, den gelesenen Datensatz zu speichern:
Ruhezustand / JPA kann dies automatisch mit der
@Version
Annotation tunSie müssen den Status des gelesenen Datensatzes im Allgemeinen in einer Sitzung beibehalten (dies ist sicherer als in einer verborgenen Formularvariablen).
quelle
SQLAlchemy
verweist nicht auf optimistische Offline-Sperren, und ich kann ihn in den Dokumenten nicht finden. Hatten Sie einen hilfreicheren Link oder haben Sie nur allgemein auf SQLAlchemy hingewiesen?Einige ORM-Systeme (Object Relational Mapping) erkennen, welche Felder eines Objekts seit dem Laden aus der Datenbank geändert wurden, und erstellen die SQL-Aktualisierungsanweisung, um nur diese Werte festzulegen. ActiveRecord für Ruby on Rails ist ein solches ORM.
Der Nettoeffekt ist, dass Felder, die der Benutzer nicht geändert hat, nicht im UPDATE-Befehl enthalten sind, der an die Datenbank gesendet wird. Personen, die verschiedene Felder gleichzeitig aktualisieren, überschreiben die Änderungen nicht gegenseitig.
Suchen Sie je nach verwendeter Programmiersprache nach den verfügbaren ORMs und prüfen Sie, ob diese nur Spalten in der Datenbank aktualisieren, die in Ihrer Anwendung als "dirty" gekennzeichnet sind.
quelle