Skip to content

Namespaces and data isolation

Chronicle has been designed with the concept of namespaces from the ground up. Namespaces provide a way to provide segregation of data in the same event store. This can typically be used for multi-tenant scenarios were you have multiple tenants of your system and you want to provide data segregation between them.

When not specified, Chronicle will use the Default namespace. For how namespaces fit into tenancy, including choosing where each tenant’s database lives, see Tenancy.

Note: A tenant is typically an organization or an organizational unit that uses a system. In cloud terminology it is often linked to Software as a Service offerings were companies can sign up to use a system. The system being the same for all these companies, or tenants, but all their data segregated from each other.

For client usage and namespace removal, see Manage namespaces.

The explicit EnsureNamespace operation returns once healthy active projections and started events captures are subscribed in that namespace. Broken artifacts are logged and retried by the next subscription trigger; they do not prevent namespace creation, and retired observers are skipped. A very large projection set can exceed the call timeout; retrying joins the running subscription pass. A namespace created implicitly by its first append is reconciled on its event log’s next observer watchdog tick, even if the namespace-added notification was missed. Subscription does not mean catch-up is complete: wait for observer completion before querying a read model that must include an append.

With Chronicle, all namespace specific data sits in its own database in the underlying data storage. This helps us avoid data leakage between namespaces, or tenants. With this you also get a better utilization of resources with mechanisms like indexing that happens on a database level.

One event store

Default

tenant-a

tenant-b

database: Default

database: tenant-a

database: tenant-b

The databases are named from the event store name and the namespace. For an event store named Ada on MongoDB:

HoldsNamespace DefaultNamespace tenant-a
Event store wide stateAda+esAda+es
Event sequencesAda+es+DefaultAda+es+tenant-a
Read modelsAdaAda+tenant-a

Note the one asymmetry: read models of the default namespace are materialized into the bare event store name, without a namespace suffix, while event sequences suffix every namespace including the default. Anything reading read models outside Chronicle — for instance an injected IMongoCollection<TReadModel> bound to a database name composed by hand — has to reproduce that rule. Composing <eventStore>+<namespace> unconditionally resolves Ada+Default, a database that does not exist, and reads then come back empty rather than failing.

Since the namespace becomes part of a database name, it has to be a legal one. MongoDB rejects the characters / \ . " $ * < > : | ?, the space, and the null character, and limits the whole name to 63 bytes. This bites most often when the namespace is resolved from a tenant id — a display-style tenant id such as Hive Consulting composes a name MongoDB will not accept. Chronicle checks the composed name and throws InvalidDatabaseName, naming the event store, the namespace, and the offending character, rather than letting the driver fail later with a bare Invalid namespace specified.