CodamAIDocs
Themafertig

Welche Datenbank? Das Persistenzziel

Die Entscheidung, ob ein Zugriff in der System-DB oder in der Mandanten-DB landet, als vollständige Entscheidungstabelle. Ohne Rückfall auf die System-DB.

Ausprägungen
System-ModellMandanten-Modell mit Mandantohne Kontext → 500ohne Mandant → 400Mandant nicht erlaubt → 403Betriebsart SINGLEDatenbank fehlt oder ist nicht erreichbar

Worum es geht

Jedes Mal, wenn CDMS ein Objekt liest oder schreibt, muss es wissen, in welche Datenbank der Zugriff geht. Diese Datenbank heißt Persistenzziel. Es gibt nur zwei Arten von Zielen:

  • die System-Datenbank mit dem Schlüssel system,
  • die Datenbank eines Mandanten mit dem Mandantenschlüssel, etwa acme.

Die Entscheidung fällt für jedes Modell einzeln, nicht einmal pro Anfrage. Zwei Dinge bestimmen sie: die Ebene des Modells und der Mandant im RequestContext. Ein Header, ein Feld in den Daten oder ein Parameter der URL spielen keine Rolle.

Der Ablauf

flowchart TD
    A(["Zugriff auf ein Modell"]) --> S{"System-Modell?"}
    S -- ja --> SYS[("System-Datenbank")]
    S -- nein --> K{"RequestContext vorhanden?"}
    K -- nein --> E500["500 CDMS_PERSISTENCE_CONTEXT_MISSING"]
    K -- ja --> T{"Mandant gesetzt?"}
    T -- nein --> M1{"Betriebsart?"}
    M1 -- SINGLE --> ONE[("die eine Datenbank")]
    M1 -- MULTI --> E400["400 CDMS_TENANT_REQUIRED"]
    T -- ja --> M2{"Betriebsart?"}
    M2 -- SINGLE --> ONE
    M2 -- MULTI --> R{"reservierter Schlüssel?<br/>system, single"}
    R -- ja --> E403R["403 CDMS_TENANT_KEY_RESERVED"]
    R -- nein --> L{"Mandant und Wechselziel<br/>in der Liste der erlaubten Mandanten?"}
    L -- nein --> E403["403 CDMS_TENANT_SWITCH_NOT_AUTHORIZED"]
    L -- ja --> TDB[("Datenbank des Mandanten")]

Die Reihenfolge ist Absicht: Die Frage „System-Modell?“ kommt vor jeder Prüfung des Kontexts. Ein fehlender oder falscher RequestContext kann ein System-Modell deshalb weder in eine andere Datenbank umleiten noch blockieren.

Die Entscheidungstabelle

Wohin ein Zugriff geht
Ebene des ModellsRequestContextMandantBetriebsartin der Liste der erlaubten MandantenPersistenzziel
System––––System-Datenbank
Mandant / Benutzerfehlt–––500 CDMS_PERSISTENCE_CONTEXT_MISSING
Mandant / BenutzerdafehltSINGLE–die eine Datenbank
Mandant / BenutzerdafehltMULTI–400 CDMS_TENANT_REQUIRED
Mandant / BenutzerdaacmeSINGLE–die eine Datenbank; der Mandant zählt nicht
Mandant / BenutzerdaacmeMULTIjaDatenbank acme
Mandant / BenutzerdaacmeMULTInein403 CDMS_TENANT_SWITCH_NOT_AUTHORIZED
Mandant / Benutzerdasystem oder singleMULTI–403 CDMS_TENANT_KEY_RESERVED, auch wenn der Schlüssel in der Liste steht

Die Ebene eines Modells legst du im Hub fest, siehe Modell-Ebenen. Der Generator macht daraus die Basisklasse der Entity: AbstractSystemModel, AbstractTenantModel oder AbstractUserModel. Aus ihr liest CDMS zur Laufzeit ab, ob ein Modell ein System-Modell ist.

Die Fehlerfälle

Wann kein Persistenzziel gefunden wird

Wann: Code greift auf ein Mandanten-Modell zu, ohne dass ein RequestContext existiert.

  1. 1
    CDMS
    sucht den RequestContext des aktuellen Threads und findet keinen
  2. 2
    CDMS
    500 CDMS_PERSISTENCE_CONTEXT_MISSING

Ergebnis: Das passiert nicht bei normalen Anfragen, sondern in eigenem Code: ein eigener Thread, ein zeitgesteuerter Job, ein Listener. Solcher Code muss sich den Kontext selbst holen, siehe Arbeiten für einen Mandanten ohne Anfrage.

Wann: MULTI, der RequestContext enthält keinen Mandanten.

  1. 1
    CDMS
    will ein Mandanten-Modell lesen oder schreiben
  2. 2
    CDMS
    400 CDMS_TENANT_REQUIRED

Ergebnis: Normale Anfragen ohne Mandanten weist schon die Filterkette mit 403 ab, siehe Woher der Mandant einer Anfrage kommt. Diese Prüfung ist die zweite Sicherung dahinter.

Wann: MULTI, der Mandant oder das Ziel eines Wechselwunsches steht nicht in der Liste der erlaubten Mandanten.

  1. 1
    CDMS
    vergleicht Mandant und Wechselziel mit der Liste aus dem Token
  2. 2
    CDMS
    403 CDMS_TENANT_SWITCH_NOT_AUTHORIZED

Ergebnis: Ein abgelehnter Wechsel wird damit sichtbar, statt still im eigenen Mandanten weiterzulaufen. Siehe Mandantenwechsel per Header.

Vom Mandantenschlüssel zur Datenbank

Steht das Ziel fest, baut CDMS daraus die Verbindung. Die Datenbank-URL der Installation enthält den Platzhalter {tenant}:

CODAMAI_PERSISTENCE_DATABASE_URL=jdbc:mysql://db:3306/{tenant}
ZielDatenbank
System-Datenbank in MULTIjdbc:mysql://db:3306/system
Mandant acme in MULTIjdbc:mysql://db:3306/acme
alles in SINGLEjdbc:mysql://db:3306/single

Alle Datenbanken liegen also auf demselben Datenbankserver und nutzen dieselben Zugangsdaten. Getrennt sind sie durch den Datenbanknamen.

Gibt es die Datenbank noch nicht oder ist der Server nicht erreichbar, endet der Zugriff mit einem eigenen Fehler, siehe Datenbanken, Pools, Migration:

FehlerStatusBedeutung
CDMS_TENANT_DATASOURCE_NOT_FOUND500Die Datenbank des Mandanten fehlt, und automatisches Anlegen ist nicht freigegeben.
CDMS_TENANT_DATASOURCE_UNAVAILABLE503Der Datenbankserver antwortet nicht. CDMS versucht dann nicht, etwas anzulegen.
CDMS_ENTITY_MANAGER_CREATION_FAILED500Die Datenbank ist da, aber der Zugang dazu ließ sich nicht aufbauen.

Eine Anfrage, zwei Ziele

Eine Anfrage kann beide Arten von Zielen berühren, etwa wenn ein Hook an einem Mandanten-Modell zusätzlich ein System-Modell schreibt. CDMS öffnet dann für jedes Ziel eine eigene Verbindung und eine eigene Transaktion. Was das für Fehler bedeutet, steht unter Keine Atomarität über zwei Datenbanken.

Fallen

Wie es weitergeht

Quellen im Code und in der Wissensdatenbank
  • commons-persistence – DatabaseRequestContext (getEntityManager, resolveTenant, requireAllowedTenant, isTenantAllowed)
  • commons-persistence – TenantIdentifierResolver.DEFAULT_TENANT, TenantEntityManagerFactory (cacheKey), DataSourceManager (getConnection, {tenant} in der URL)
  • commons-persistence – PersistenceErrorCode (CDMS_PERSISTENCE_CONTEXT_MISSING 500, CDMS_TENANT_REQUIRED 400, CDMS_TENANT_SWITCH_NOT_AUTHORIZED 403, CDMS_TENANT_KEY_RESERVED 403, CDMS_TENANT_DATASOURCE_NOT_FOUND 500, CDMS_TENANT_DATASOURCE_UNAVAILABLE 503, CDMS_ENTITY_MANAGER_CREATION_FAILED 500)
  • CDMS/cdms-persistence-database – AbstractSystemModel, AbstractTenantModel, AbstractUserModel, EntityClassFilterService
  • documentation/30-daten-und-persistenz/01-mandantentrennung.md (Schritt 3)
Suchen