CodamAIDocs
Themafertig

Einen Mandanten anlegen und bereitstellen

Absicht speichern, Datenbank anlegen und migrieren, Ergebnis speichern. Was in SINGLE, MULTI und bei einem eigenständigen CIAS passiert.

Ausprägungen
MULTI: Datenbank anlegenSINGLE: nichtseigenständiges CIAS: nichtsFehlschlag → FAILED, wiederholbarhängender Rollout → nach 30 min FAILED

Worum es geht

Einen Mandanten anlegen heißt zweierlei: einen Datensatz in CIAS schreiben und die Datenbank des Mandanten einrichten. Das eine liegt in der Datenbank von CIAS, das andere in der Persistenz von CDMS. Eine gemeinsame Transaktion über beide gibt es nicht. Das zweite kann also scheitern, nachdem das erste gelungen ist.

CIAS löst das, indem es jeden Schritt einzeln festhält. Scheitert etwas, bleibt ein Mandant zurück, der erkennbar kaputt ist und repariert werden kann, statt einer, der als angelegt gilt und nichts bedient.

Wer anlegen darf

Es gibt drei Wege, und alle enden in derselben Methode von CIAS:

WegWerPrüfung
Verwaltungs-API POST /cias/admin/tenantsein Plattform-AdministratorDie Rolle wird bei jedem Aufruf geprüft, auch beim Lesen. Ein Mandanten-Administrator darf keine Mandanten anlegen
Selbstregistrierung einer Firmaniemand ist angemeldetDie Registrierung ruft die Methode ohne Rollenprüfung auf. Diese Variante hat keinen HTTP-Endpunkt
Erster Mandant beim Startdie Installation selbstSteht ein Mandant in der Bootstrap-Konfiguration und gibt es ihn noch nicht, legt CIAS ihn beim Start an

Warum ein Mandanten-Administrator nicht darf: Mandanten anlegen, sperren oder schließen ist genau das, was Kunden voneinander trennt. Dürfte ein Kunde das, könnte er die anderen erreichen.

Der Ablauf

sequenceDiagram
    participant A as Admin
    participant C as CIAS
    participant DB as CIAS-Datenbank
    participant P as Persistenz (CDMS)
    A->>C: POST /cias/admin/tenants {key: nordbau, type: DYNAMIC}
    C->>DB: Transaktion 1: Schlüssel frei? speichern als PENDING / IN_PROGRESS
    C->>P: einrichten (außerhalb jeder Transaktion)
    P->>P: Datenbank nordbau anlegen, Schema migrieren
    P-->>C: fertig
    C->>DB: Transaktion 2: PROVISIONED, ACTIVE
    C-->>A: 200 mit dem Mandanten

Drei Gründe für diese Form:

  1. Absicht zuerst speichern. Stürzt der Prozess danach ab, ist wenigstens etwas da, das man wiedererkennt: ein Mandant in IN_PROGRESS mit Startzeit.
  2. Einrichten ohne offene Transaktion. Eine Datenbank anzulegen und zu migrieren kann dauern. Liefe das in einer Transaktion, hielte CIAS so lange eine Datenbankverbindung und ihre Sperren fest.
  3. Ergebnis danach speichern, Erfolg wie Misserfolg, jeweils in einer eigenen kurzen Transaktion.

Was „einrichten“ heißt

Was bei der Einrichtung passiert, hängt davon ab, was im selben Prozess läuft:

Die Einrichtung je Aufbau

Wann: CDMS-Persistenz im selben Prozess, CODAMAI_PERSISTENCE_TENANT_MODE=MULTI

  1. 1
    CIAS→CDMS
    bittet um Einrichtung von nordbau
  2. 2
    CDMS→Mandanten-DB
    fehlt die Datenbank und ist Anlegen freigegeben → CREATE DATABASE nordbau
  3. 3
    CDMS→Mandanten-DB
    migriert das Schema auf den Stand der Modelle

Ergebnis: Die Datenbank ist fertig, bevor der erste Kunde kommt. Scheitert etwas, merkt es der Betrieb beim Anlegen und nicht der Kunde bei seiner ersten Anfrage.

Wann: CODAMAI_PERSISTENCE_TENANT_MODE=SINGLE

Es gibt nur die eine Datenbank. Die Einrichtung hat nichts zu tun und meldet sofort Erfolg.

Ergebnis: Der Mandant ist sofort ACTIVE. In SINGLE spielt er für Anfragen allerdings keine Rolle, siehe In SINGLE zählt der Mandant im Token nicht.

Wann: CIAS läuft als eigener Dienst, ohne CDMS-Persistenz im Prozess.

CIAS hat keine Datenbanken der Mandanten, also richtet es nichts ein und meldet sofort Erfolg. Beim Start steht einmal im Log, dass diese Installation nichts bereitstellt. Die Datenbank legt CDMS bei der ersten Anfrage für den Mandanten selbst an, sofern Anlegen dort freigegeben ist.

Ergebnis: Der Mandant ist sofort ACTIVE. Die erste Anfrage des Kunden dauert länger. Siehe Datenbanken, Pools, Migration.

Die Einrichtung darf beliebig oft laufen: Eine vorhandene Datenbank wird nicht noch einmal angelegt, und die Migration spielt nur fehlende Änderungen ein. Ein zweiter Versuch nach einem halben Fehlschlag wiederholt also Arbeit, statt etwas doppelt anzulegen.

Was dabei schiefgehen kann

Fehlschlag, Neuversuch, hängender Rollout

Wann: Es gibt schon einen Mandanten mit diesem Schlüssel, in welchem Zustand auch immer.

CIAS legt nichts an.

Ergebnis: 409 cias.tenancy.key-already-used

Wann: Der Schlüssel verletzt die Regeln, oder type fehlt.

CIAS legt nichts an. Die Meldung beschreibt die eigene Eingabe, etwa welche Zeichen erlaubt sind.

Ergebnis: 400 cias.tenancy.invalid-request, siehe Der Mandantenschlüssel

Wann: Die Datenbank lässt sich nicht anlegen oder migrieren.

  1. 1
    CIAS→CDMS
    einrichten → Fehler
  2. 2
    CIAS
    speichert den Mandanten als FAILED, Stellung bleibt PENDING, Event ProvisioningFailed
  3. 3
    CIAS→Admin
    502 cias.tenancy.provisioning-failed: angelegt, aber nicht eingerichtet

Ergebnis: Der Mandant existiert und wird nicht bedient. Denselben Aufruf noch einmal zu schicken hilft nicht, der Schlüssel ist jetzt vergeben. Richtig ist der Neuversuch.

Wann: POST /cias/admin/tenants/{id}/retry-provisioning für einen Mandanten in FAILED

  1. 1
    CIAS
    setzt den Rollout wieder auf IN_PROGRESS, mit neuer Startzeit
  2. 2
    CIAS→CDMS
    richtet erneut ein
  3. 3
    CIAS
    gelingt es → PROVISIONED, und ACTIVE, wenn die Stellung noch PENDING ist

Ergebnis: Ist der Rollout nicht FAILED oder der Mandant geschlossen, lehnt CIAS ab, bevor etwas eingerichtet wird: 409 cias.tenancy.illegal-transition.retry-provisioning. Einen inzwischen gesperrten Mandanten richtet der Neuversuch ein, lässt ihn aber gesperrt, siehe Der Lebenslauf eines Mandanten.

Wann: Der Prozess stirbt zwischen den beiden Transaktionen. Der Mandant bleibt in IN_PROGRESS, und der Neuversuch nimmt nur FAILED an.

  1. 1
    CIAS
    Abgleich läuft alle 15 Minuten: Welche Rollouts sind seit mehr als 30 Minuten IN_PROGRESS?
  2. 2
    CIAS
    liest jeden Treffer neu, prüft noch einmal und setzt ihn auf FAILED, Event ProvisioningFailed mit dem Vermerk „aufgegeben“

Ergebnis: Danach geht der normale Neuversuch. Der Abgleich ist abschaltbar, beide Zeiten sind einstellbar.

Der Abgleich ist ein Zeitgeber, der liegengebliebene Rollouts findet. Eingestellt wird er über codamai.cias.tenancy.reconciliation.enabled, .interval (Standard 15 Minuten) und .deadline (Standard 30 Minuten). Im eigenständigen CIAS ist er eingeschaltet.

Die Frist ist mit Absicht großzügig. Wäre sie kürzer als die längste echte Einrichtung, würde der Abgleich einen noch laufenden Rollout aufgeben. Dieser Rollout könnte dann seinen Erfolg nicht mehr eintragen, denn CIAS nimmt „fertig“ nur für einen Rollout in IN_PROGRESS an. Zurück bliebe eine fertige Datenbank hinter einem Mandanten in FAILED. Das behebt ein Neuversuch, aber es ist unnötige Arbeit.

Was bei einem dynamischen Mandanten mit der Organisation passiert

WegOrganisation in Keycloak
Selbstregistrierung (CREATE_NEW)Die Registrierung legt zuerst die Organisation an, Alias = Schlüssel, und dann den Mandanten mit deren ID. Danach wird die Person Mitglied
Verwaltungs-APICIAS legt keine Organisation an. Das Feld externalOrganizationId nennt eine Organisation, die es in Keycloak schon gibt. Ein dynamischer Mandant ohne dieses Feld wird trotzdem angenommen
Erster Mandant beim Startwie bei der Verwaltungs-API, aus der Konfiguration

Einen Mandanten zu speichern und später mit seiner Organisation zu verbinden, ist so möglich. Ob ein Token den Mandanten nennt, hängt am Alias der Organisation, nicht an diesem Feld, siehe Den Mandanten einer Anfrage bestimmen.

Anfrage und Antwort

Anfrage
POST /cias/admin/tenants
Authorization: Bearer <Token eines Plattform-Administrators>
{
  "key": "nordbau",
  "type": "DYNAMIC",
  "displayName": "Nordbau GmbH",
  "externalOrganizationId": "b7e0…"
}
Antwort
HTTP 200
{
  "id": "0f6c…",
  "key": "nordbau",
  "displayName": "Nordbau GmbH",
  "type": "DYNAMIC",
  "status": "ACTIVE",
  "provisioningState": "PROVISIONED",
  "externalOrganizationId": "b7e0…",
  "validFrom": null,
  "validUntil": null,
  "createdAt": "2026-09-22T09:14:03.120Z",
  "updatedAt": "2026-09-22T09:14:04.480Z"
}

displayName darf fehlen, dann steht dort der Schlüssel. Ein Gültigkeitsfenster gehört nicht zum Anlegen, es wird danach gesetzt, siehe Mandanten sperren, schließen, Gültigkeit.

Fallen

Weiter

Quellen im Code und in der Wissensdatenbank
  • CIAS/cias-tenancy – TenantService (createTenant, retryProvisioning, rollOut), TenantAdministrationService (requireAdministrator), TenantAdminController (POST /cias/admin/tenants, /{id}/retry-provisioning), TenantRestDtos.CreateTenantRequest, TenantExceptionHandler
  • CIAS/cias-tenancy – Tenant (markProvisioningStarted, completeRollout)
  • CIAS/cias-tenancy – TenantReconciliationService, Tenant.isProvisioningStale; CIAS/cias-spring-boot-starter – TenantReconciliationScheduler, CiasReconciliationAutoConfiguration, CiasProperties.Tenancy.Reconciliation (interval PT15M, deadline PT30M)
  • commons-persistence – TenantProvisioningPort, DatabaseTenantProvisioningAdapter, NoOpTenantProvisioningAdapter
  • CIAS/cias-spring-boot-starter – CiasTenantProvisioningAutoConfiguration, CiasBootstrap.createFirstTenant
  • CIAS/cias-registration – RegistrationService.assignTenant
  • CIAS/cias-tenancy/docs/adr – ADR-016, ADR-020, ADR-045
Suchen