CodamAIDocs
Themafertig

Keine Atomarität über zwei Datenbanken

Eine Änderung, die System-DB und Mandanten-DB berührt, ist nicht atomar. Hier steht, wann das vorkommt und was es bedeutet.

Ausprägungen
normale Anfrage: eine DatenbankHook oder eigener Code schreibt beide EbenenFehler vor dem Commit → beide zurückFehler beim Commit → Teilzustand möglichBetriebsart SINGLE

Worum es geht

Atomar heißt: ganz oder gar nicht. Innerhalb einer Datenbank ist eine Anfrage atomar, siehe Ein Request, eine Transaktion.

CDMS kennt aber zwei Arten von Datenbanken: die System-Datenbank für Modelle der Ebene System und die Datenbank des Mandanten für Modelle der Ebenen Mandant und Benutzer. Siehe Modell-Ebenen: System, Mandant, Benutzer. Jede Datenbank hat ihre eigene Transaktion. Eine Klammer, die beide zusammen festschreibt, gibt es nicht.

Wann eine Anfrage zwei Datenbanken berührt

Über die generierten Endpunkte kommt das nicht vor: Beziehungen gibt es nur zwischen Modellen derselben Datenbank, also bleibt auch verschachteltes Schreiben in einer Datenbank.

Zwei Datenbanken entstehen erst durch eigenen Code:

  • ein Hook an einem Mandanten-Modell, der zusätzlich ein System-Modell schreibt, etwa einen Zähler oder einen Protokolleintrag für die ganze Installation,
  • oder umgekehrt ein Hook an einem System-Modell, der Mandanten-Daten schreibt,
  • ein eigener Endpunkt, der beide Ebenen ändert.
flowchart LR
    C["Client"] --> D["CDMS: eine Anfrage"]
    D --> T1["Transaktion 1"] --> SYS[("System-Datenbank")]
    D --> T2["Transaktion 2"] --> MAND[("Datenbank Mandant A")]

Was bei Fehlern passiert

Eine Anfrage schreibt in beide Datenbanken
Wann tritt der Fehler auf?Ergebnis
gar nichtbeide Transaktionen werden festgeschrieben, 2xx
während der Verarbeitung: Rolle, Validierung, Hook, Datenbankregel beim flushbeide werden zurückgerollt, nichts bleibt
beim Commit der ersten Datenbanknichts festgeschrieben, Fehlerantwort
beim Commit der zweiten Datenbankdie erste ist schon festgeschrieben, die zweite nicht; Fehlerantwort

Der Teilzustand entsteht also nur in einem schmalen Fenster: Alles ist geprüft und geschrieben, und dann scheitert das Festschreiben der zweiten Datenbank, etwa weil sie gerade nicht erreichbar ist. Das ist selten, aber möglich, und die Reihenfolge der beiden Commits ist nicht festgelegt.

  1. 1
    CDMS
    Verarbeitung erfolgreich, beide Datenbanken haben offene Änderungen
  2. 2
    CDMS→Database
    Commit System-Datenbank gelingt
  3. 3
    CDMS→Database
    Commit Mandanten-Datenbank scheitert
  4. 4
    CDMS→Client
    Fehlerantwort; die Mandanten-Änderung ist zurückgerollt
    Ergebnis: Die System-Datenbank enthält die Änderung, die Mandanten-Datenbank nicht.

Betriebsart SINGLE

In der Betriebsart SINGLE liegt alles in einer Datenbank. Trotzdem schreibt CDMS System-Modelle und Mandanten-Modelle einer Anfrage in getrennten Transaktionen. Die Regeln oben gelten deshalb in beiden Betriebsarten. Siehe SINGLE und MULTI über beide Module.

Wie du damit umgehst

Drei Regeln für eigenen Code
Vermeiden
  • eine fachliche Operation schreibt eine Ebene
  • Daten, die zusammengehören, gehören auf dieselbe Ebene
Reihenfolge wählen
  • ist es unvermeidbar, teile es in zwei Anfragen
  • zuerst die Seite, deren Teilzustand harmlos ist
  • dann die zweite; scheitert sie, bleibt nur Harmloses
Erkennbar machen
  • ein Teilzustand muss sich finden lassen
  • z. B. über einen Status wie pending, der erst im zweiten Schritt auf done geht
  • oder einen Ausgleichsschritt, der ihn später aufräumt

Fallen

Wie es weitergeht

Quellen im Code und in der Wissensdatenbank
  • commons-persistence – DatabaseRequestContext (ein EntityManager und eine Transaktion je Ziel und Thread; resolveTenant; commitThreadTransactions; markRollbackOnly)
  • CDMS/cdms-rest-api – RequestTransactionCommitter, CdmsExceptionMapper
  • CDMS/cdms-persistence-database – docs/adr/ADR-001, ADR-005, ADR-009, ADR-010
  • documentation/30-daten-und-persistenz/02-transaktionen.md (Atomarität über Datenbankgrenzen)
Suchen