Softwarearchitektur verstehen und entscheiden

Aaron KreisÜber mich
Konsistenz und Transaktionen

Pessimistic Locking

Datensatz vor der Bearbeitung sperren

Die Idee und ihr Nutzen

Frau Berg und Herr Kurz arbeiten in der Schadenabteilung einer Versicherung und wollen beide Schaden 4711 bearbeiten. Frau Berg öffnet den Schaden zuerst, und die Schadenanwendung, mit der beide arbeiten, sperrt ihn für sie. Herr Kurz sieht beim Öffnen, dass der Schaden gesperrt ist. Er wartet, bis Frau Berg fertig ist, oder bearbeitet in der Zwischenzeit einen anderen Schaden. So kann keiner der beiden die Änderung des anderen überschreiben, und Herr Kurz beginnt keine Arbeit, die er nicht abschließen könnte. Dieses Vorgehen heißt Pessimistic Locking, auf Deutsch pessimistisches Sperren. Pessimistisch heißt das Vorgehen, weil es für Fälle gedacht ist, in denen Konflikte häufig sind. Ein Konflikt entsteht, wenn zwei Personen oder Programme denselben Schaden gleichzeitig ändern. Die Schadenanwendung verhindert einen solchen Konflikt schon beim Öffnen des Schadens.

Wie funktioniert es?

Frau Berg öffnet um 10:05 Uhr Schaden 4711 zum Ändern und verlangt eine Sperre. Die Schadenanwendung vermerkt, dass Schaden 4711 für Frau Berg gesperrt ist, und gibt der Sperre eine Ablaufzeit von 30 Minuten. Schließt Frau Berg den Browser oder stürzt ihr Rechner ab, verfällt die Sperre nach diesen 30 Minuten von selbst. Eine Bearbeitung dauert in der Schadenabteilung meist höchstens 20 Minuten; die Ablaufzeit lässt Frau Berg diese Zeit, und nach einem Absturz wartet niemand länger als eine halbe Stunde. Um 10:10 Uhr will Herr Kurz denselben Schaden ändern, um die Freigabe der Auszahlung vorzubereiten. Die Schadenanwendung weist Herrn Kurz mit der Meldung „Schaden 4711 wird von Frau Berg bearbeitet“ ab. Herr Kurz kann den Schaden ansehen, aber nicht ändern. Frau Berg ergänzt einen neuen Gutachterbericht und erhöht deshalb die Reserve, also den Betrag, den die Versicherung für diesen Schaden zurücklegt. Die Schadenanwendung speichert die Änderungen in der Datenbank. Um 10:25 Uhr schließt Frau Berg den Schaden, und die Schadenanwendung hebt die Sperre auf. Herr Kurz öffnet den Schaden erneut, erhält jetzt selbst die Sperre und bereitet die Freigabe auf dem neuen Stand mit dem Gutachterbericht und der erhöhten Reserve vor.

Für die Bearbeitung durch einen Menschen gibt es eine fachliche Sperre, für eine kurze Änderung durch ein Programm eine Sperre in der Datenbank. Die Sperre für Frau Berg ist eine fachliche Sperre: Die Schadenanwendung speichert sie als eigenen Eintrag „Schaden 4711 wird seit 10:05 Uhr von Frau Berg bearbeitet“. Öffnet Herr Kurz den Schaden, findet die Schadenanwendung diesen Eintrag und lässt Herrn Kurz den Schaden nur lesen. Beim endgültigen Speichern prüft die Schadenanwendung, ob Frau Berg die Sperre noch hält und ob sich der Schaden in der Zwischenzeit geändert hat. Beim Schließen löscht die Schadenanwendung den Eintrag.

Ein Programm, das die Reserve neu berechnet, braucht dafür nur Bruchteile einer Sekunde und verwendet eine Sperre der Datenbank. Der Datenbankbefehl lautet sinngemäß: „Lies Schaden 4711 und sperre ihn für Änderungen durch andere.“ Solange die Sperre besteht, muss jedes andere Programm, das Schaden 4711 ebenfalls ändern will, warten. Das Programm, das die Sperre hält, berechnet die Reserve neu und speichert den geänderten Schaden. Die Sperre gilt innerhalb einer einzigen Datenbanktransaktion, also einer Folge von Datenbankbefehlen, die gemeinsam gelingen oder gemeinsam scheitern. Mit dem Ende der Transaktion hebt die Datenbank die Sperre auf, und das nächste wartende Programm kommt an die Reihe. Datenbanken unterscheiden dabei Sperren zum Lesen, die mehrere Programme gleichzeitig halten dürfen, und Sperren zum Schreiben, die immer nur ein Programm auf einmal halten darf. Für die Bearbeitung durch Frau Berg eignet sich eine Datenbanksperre nicht. Frau Berg braucht für ihre Änderungen 20 Minuten, und die Schadenanwendung ist eine Webanwendung, die zwischen dem Öffnen eines Schadens im Browser und dem Speichern nicht ständig mit der Datenbank verbunden bleibt. Eine Datenbanktransaktion so lange offen zu halten, würde die ganze Zeit eine Verbindung zur Datenbank belegen und andere Programme wie die Neuberechnung der Reserve blockieren.

Halten zwei Programme jeweils eine Sperre, die das andere braucht, warten beide endlos aufeinander. Diese Lage heißt Verklemmung (Deadlock). Viele Datenbanken erkennen Verklemmungen und brechen eines der beiden Programme ab, damit das andere weiterarbeiten kann. Die fachliche Sperre schützt außerdem nur vor Programmen, die sie beachten. Ein anderes Programm, das unmittelbar in die Datenbank schreibt, kennt die Sperreinträge der Schadenanwendung nicht. Ob die Änderung von Frau Berg fachlich richtig ist, prüft die Sperre ebenfalls nicht.

Pessimistisches Sperren: Herr Kurz wartet, bis Frau Berg fertig ist

Beteiligte: Frau Berg: Bearbeiterin A; Herr Kurz: Bearbeiter B; Schadenanwendung: vergibt fachliche Sperren mit Ablaufzeit; Datenbank: speichert Schaden 4711. Ablauf: 1. Frau Berg öffnet um 10:05 Uhr Schaden 4711 zum Ändern und verlangt eine Sperre. 2. Die Schadenanwendung vermerkt, dass Schaden 4711 für Frau Berg gesperrt ist, und gibt der Sperre eine Ablaufzeit von 30 Minuten. 3. Herr Kurz will um 10:10 Uhr denselben Schaden ändern. 4. Die Schadenanwendung weist Herrn Kurz mit „wird von Frau Berg bearbeitet“ ab. Herr Kurz kann den Schaden ansehen, aber nicht ändern. 5. Frau Berg ergänzt den Gutachterbericht und erhöht die Reserve. 6. Die Schadenanwendung speichert die Änderungen in der Datenbank. 7. Um 10:25 Uhr schließt Frau Berg den Schaden, und die Schadenanwendung hebt die Sperre auf. 8. Herr Kurz öffnet Schaden 4711 erneut. 9. Herr Kurz erhält die Sperre und bereitet die Freigabe auf dem neuen Stand mit dem Gutachterbericht und der erhöhten Reserve vor.

Wann ist es sinnvoll und wann nicht?

Pessimistic Locking lohnt sich

  • Konflikte sind häufig. Ein Beispiel sind mehrere Sachbearbeiter, die nach einem Sturm gleichzeitig denselben Großschaden bearbeiten. Ohne Sperre scheitern viele Speicherversuche, oder eine Änderung überschreibt die andere. Mit der Sperre ändert immer nur eine Person den Schaden, und kein Speicherversuch scheitert. Dasselbe gilt, wenn schon ein einzelner Konflikt schwere Folgen hätte, etwa bei der Freigabe hoher Beträge.
  • Zwei Personen wollen denselben Vorgang zur selben Zeit bearbeiten, und die Arbeit der einen hängt vom Ergebnis der anderen ab. Ohne Schutz würde Herr Kurz die Freigabe auf dem alten Stand ohne den neuen Gutachterbericht vorbereiten, und einer der beiden würde die Änderung des anderen überschreiben. Mit der Sperre bereitet Herr Kurz die Freigabe erst vor, wenn Frau Berg fertig ist, und zwar auf dem neuen Stand.
  • Menschen bearbeiten einen Vorgang über viele Minuten. Ein Verfahren, das Konflikte erst beim Speichern erkennt, würde den Speicherversuch von Herrn Kurz zwar ablehnen. Herr Kurz hätte dann aber schon zwanzig Minuten in eine Freigabe gesteckt, die er so nicht abschließen kann. Mit der fachlichen Sperre erfährt er schon um 10:10 Uhr beim Öffnen, dass Frau Berg den Schaden bearbeitet.
  • Eine kurze Änderung läuft innerhalb einer einzigen Datenbanktransaktion ab. Ohne Sperre könnten zwei Programme, die gleichzeitig die Reserve von Schaden 4711 neu berechnen, ihre Ergebnisse gegenseitig überschreiben. Mit Pessimistic Locking setzt das Programm eine Datenbanksperre und braucht keine fachliche Sperre mit eigenen Einträgen und Ablaufzeit.

Pessimistic Locking ist nicht das Richtige

  • Konflikte sind selten, und ein abgelehnter Speicherversuch ist schnell wiederholt. Ein Beispiel ist das Ändern einer Telefonnummer. Eine Sperre würde Herrn Kurz trotzdem warten lassen, solange Frau Berg denselben Schaden geöffnet hat. Stattdessen sperrt die Schadenanwendung nichts vorab, sondern arbeitet mit Optimistic Locking: Jeder Schaden trägt eine Versionsnummer, und die Datenbank lehnt das Speichern ab, wenn sich die Versionsnummer seit dem Lesen geändert hat. Niemand wartet auf eine Sperre, und die Schadenanwendung braucht keine Sperreinträge mit Ablaufzeit. Speichert Herr Kurz nach Frau Berg, trägt er die Telefonnummer in den neuen Stand ein und speichert noch einmal.
  • Programme auf verschiedenen Servern müssen denselben Vorgang schützen, ohne dieselbe Datenbank zu nutzen. Eine Datenbanksperre gilt nur innerhalb ihrer Datenbank, und den Sperreintrag der Schadenanwendung beachtet nur die Schadenanwendung. Ohne gemeinsame Sperre müsste jedes Programm seine Sperre in der Datenbank eines anderen Programms setzen. Dann verwenden die Programme einen Distributed Lock, also eine Sperre, die ein eigener Dienst verwaltet und die alle Programme auf allen Servern vor einer Änderung anfordern. Keines der Programme muss dafür auf die Datenbank eines anderen Programms zugreifen.

Nachteile und typische Fehler

Wer auf eine Sperre wartet, verliert Zeit. Wenn viele Programme oder Menschen denselben Datensatz brauchen, bilden sich Warteschlangen. Für die fachliche Sperre braucht die Schadenanwendung zusätzlichen Code, der Sperreinträge anlegt, prüft und löscht und abgelaufene Einträge erkennt. Die Ablaufzeit muss zur üblichen Bearbeitungsdauer passen. Ist sie zu kurz, verfällt die Sperre, während Frau Berg noch arbeitet. Ist sie zu lang, wartet Herr Kurz nach einem Absturz unnötig. Außerdem müssen alle Programme, die Schäden ändern, die fachliche Sperre beachten, auch jedes neu hinzukommende Programm.

Typische Fehler und woran man sie erkennt:

  • Sperre ohne Ablaufzeit: Die fachliche Sperre hat keine Ablaufzeit. Der Rechner von Frau Berg stürzt ab, bevor sie Schaden 4711 schließt, und die Schadenanwendung löscht den Sperreintrag nie. Herr Kurz und alle anderen können Schaden 4711 dann dauerhaft nur lesen. Erkennbar ist das an Sperreinträgen, die Stunden oder Tage alt sind.
  • Verklemmung: Das Programm, das die Reserve neu berechnet, sperrt Schaden 4711 und will danach Schaden 4712 sperren. Gleichzeitig sperrt ein zweites Programm Schaden 4712 und will danach Schaden 4711 sperren. Jedes der beiden Programme wartet auf die Sperre des anderen. Die Datenbank erkennt die Verklemmung und bricht eines der beiden Programme ab, und das abgebrochene Programm muss die Transaktion wiederholen. Erkennbar ist das an Meldungen über Verklemmungen in den Protokollen der Datenbank und an Transaktionen, die wiederholt werden müssen.
  • Datenbanksperre über die Bearbeitungszeit: Die Schadenanwendung beginnt beim Öffnen von Schaden 4711 eine Datenbanktransaktion mit Sperre und hält sie offen, bis Frau Berg speichert. Das Programm, das die Reserve neu berechnet, wartet dann bis zu 20 Minuten auf Schaden 4711 statt Bruchteile einer Sekunde. Erkennbar ist das an langen offenen Transaktionen und an Zeitüberschreitungen bei Programmen wie der Neuberechnung der Reserve.
  • Zu grobe Sperre: Das Programm sperrt die ganze Schadentabelle statt des einen Schadens. Alle anderen Zugriffe auf diese Tabelle müssen warten. Erkennbar ist das daran, dass bei einer einzigen Bearbeitung auch Sachbearbeiter warten, die ganz andere Schäden ändern wollen.
  • Abgelaufene Sperre nicht geprüft: Frau Berg arbeitet an Schaden 4711. Nach 30 Minuten verfällt ihre Sperre. Herr Kurz erhält die Sperre und ändert den Schaden. Nach 40 Minuten speichert Frau Berg. Prüft die Schadenanwendung dabei nicht, ob Frau Berg die Sperre noch hält, überschreiben Frau Bergs Daten die Änderung von Herrn Kurz. Erkennbar ist das an verlorenen Änderungen bei Bearbeitungen, die länger als die Ablaufzeit dauerten.
  • Sperre umgangen: Ein Importprogramm schreibt unmittelbar in die Datenbank und beachtet die fachliche Sperre nicht. Erkennbar ist das an Änderungen an gesperrten Schäden, die keiner der beiden Sachbearbeiter vorgenommen hat.

Weiterführend

Verwandte Ansätze

  • Unit of Work: sammelt mehrere Änderungen und speichert sie in einer gemeinsamen Transaktion. Sperren, die dabei angefordert werden, bleiben in der Regel bis zum Ende dieser Transaktion bestehen.

Werkzeuge und Umsetzungen

  • PostgreSQL: Datenbank, die einzelne Zeilen mit dem Befehl SELECT ... FOR UPDATE sperrt und Verklemmungen erkennt.
  • MariaDB: Datenbank, die einzelne Zeilen für Schreibvorgänge innerhalb von Transaktionen sperrt.
  • Hibernate ORM: Java-Bibliothek für Datenbankzugriffe, die mit der Einstellung LockModeType.PESSIMISTIC_WRITE eine Schreibsperre in der Datenbank anfordert.
  • Spring Data: Java-Bibliothek, mit der Entwickler eine Datenbankabfrage mit einer Sperranforderung kennzeichnen.

Quellen

  • David Rice: Pessimistic Offline Lock: Muster aus Martin Fowlers Buch Patterns of Enterprise Application Architecture für Sperren über eine lange fachliche Bearbeitung, die mehrere Datenbanktransaktionen umfasst.
  • PostgreSQL: Explicit Locking: Projektdokumentation zu Zeilensperren mit SELECT ... FOR UPDATE, gemeinsamen und ausschließlichen Sperren, Sperren bis zum Ende der Transaktion und zur Erkennung von Verklemmungen.