What this is about
A tenant is a customer whose data must stay separate from the data of other customers. How strictly CDMS separates them is set by the operating mode. There are exactly two:
SINGLE: one database for everything. There are no tenants.MULTI: one system database for data of the whole installation, plus a separate database per tenant.
The operating mode applies to the whole installation, so to the database and the file storage at the same time. It is set in the environment variable CODAMAI_PERSISTENCE_TENANT_MODE.
Two pictures
flowchart TB
subgraph S["SINGLE"]
direction TB
SA(["Request"]) --> SDB[("Database single<br/>system, tenant and user models")]
end
subgraph M["MULTI"]
direction TB
MA(["Request from tenant acme"]) --> SYS[("Database system<br/>system models")]
MA --> T1[("Database acme<br/>tenant and user models")]
T2[("Database globex<br/>tenant and user models")]
end
In MULTI the request from acme reaches only two databases: the system database that everyone shares, and its own. The database of globex is not on its path. Which models go where is explained in Model levels.
What differs
- one database for all models
- the tenant in the token is ignored
- no tenant switch, no check whether a tenant is served
- files without a tenant directory
- the owner filter of user models still applies
- for installations with exactly one customer, for development and testing
- system database plus one database per tenant
- every request of a person needs a tenant, otherwise 403
- CIAS checks on every request whether the tenant is served
- tenant switch by header for authorized persons
- files in one directory per tenant
- for installations with several customers
| Level of the model | Operating mode | Database |
|---|---|---|
| System | SINGLE | the one database |
| Tenant or user | SINGLE | the one database |
| System | MULTI | system database |
| Tenant or user | MULTI | database of the request's tenant |
The full decision, including the error cases, is in Which database? The persistence target.
What SINGLE does with the tenant in the token
A token can carry a tenant in SINGLE too, for example because the same Keycloak serves several applications. CDMS then does not evaluate it at all:
- CIAS does not determine a tenant and does not ask whether it is served.
- The list of allowed tenants stays empty. A
tenantheader therefore cannot switch anything. - All models end up in the one database.
Separation by person does not depend on the operating mode: the owner filter of user models works in SINGLE exactly as in MULTI.
Configuration
When: CODAMAI_PERSISTENCE_TENANT_MODE=SINGLE or =MULTI
-
1CDMSreads the operating mode at startup
-
2CDMSsets up database access, file storage and the CIAS tenant check accordingly
Result: The application starts.
When: The variable is not set.
-
1CDMSfinds no operating mode; there is deliberately no default
-
2CDMSstops the start with an error message that names the setting
Result: A default would silently switch off a separation someone expected, or invent one nobody asked for.
When: The old key CODAMAI_PERSISTENCE_DATABASE_MODE (formerly CDMS_DATABASE_MODE) is set as well.
-
1CDMScompares the old value with the new one
-
2CDMSequal → warning in the log, the old key should be removed
-
3CDMSdifferent → the start stops, the message names both values
Result: Neither value wins silently. A contradiction is a configuration error.
Pitfalls
Where to go next
- Where the tenant of a request comes from: Where the tenant of a request comes from
- Which database a request hits: Which database? The persistence target
- How the databases are created: Databases, pools, migration
- Both modules together: SINGLE and MULTI across both modules