Skip to content

Multi-Tenancy (Fluid Clusters)

DistributedMulti-TenancySecurity
Beta Works · surface still evolving · Tenant routing, isolation, quotas, snapshots and restore are proven end to end. The tenant of a connection is set by the embedding application; a client-side setting for it is not in this release.
Premium feature Available only with a license key that grants multi_tenancy. Flags and environment variables cannot enable it. To license this feature, contact the AetheriusDB team at aetheriusdb.com or aetheriuslabs.com. Set on the orchestrator and on every worker. See Licensing → Premium features.

A cluster can serve many tenants at once. Each connection executes for exactly one tenant, every statement it sends is routed for that tenant, and on the worker each tenant’s rows live in a directory of their own. A tenant never sees, counts or deletes another tenant’s rows — not through a query, not through an index, not through a backup.

Tenant 0 is the system tenant. It maps to the worker’s root data directory, so a single-tenant deployment needs no configuration at all.

Multi-tenancy is off by default. Set the flag on the orchestrator and on every worker:

AETHERIUS_ENABLE_MULTI_TENANCY=1

With the flag off, any statement or request for a tenant other than 0 is refused before anything is routed or read: the client gets a TenancyViolation error (SQLSTATE 08P01), and a worker that receives such a request directly aborts it. Both ends check independently, so a worker never relies on the orchestrator having done so. Every refusal is counted.

The tenant is an attribute of the connection’s session, set by the application that embeds the orchestrator. It is read once per statement and travels with the statement to the worker. Nothing in the SQL text names the tenant, so a query cannot be rewritten to reach another tenant’s data.

On a worker, tenant 7 reads and writes under tenant_7/ inside the data directory; the system tenant uses the data directory itself. An INSERT for a tenant lands in that tenant’s directory as a new container, and a SELECT only ever opens the blocks in the connection’s own directory. Two tenants inserting at the same time never touch the same file.

One tenant cannot occupy the cluster. Each tenant may have a bounded number of statements in flight at the orchestrator:

AETHERIUS_MAX_CONCURRENT_QUERIES_PER_TENANT=50 # the default

A statement over the limit is refused immediately with a RESOURCE_EXHAUSTED error and never reaches a worker; other tenants are unaffected. The slot is released when the statement finishes, fails, or the connection drops, so a crashed client cannot pin a tenant’s quota.

A cluster table’s schema can grow without rewriting a single file:

  • Add a column with a default — every existing row reads the default until the column is physically written for that tenant. SELECT and WHERE use it like any other column.
  • Add a nullable column — existing rows read NULL.
  • Per-tenant overlays — a column that exists for one tenant only, invisible to every other tenant of the same table.
  • Materialise — once a tenant’s new data carries the column physically, mark it materialised for that tenant; other tenants keep the default.

These are declared through the orchestrator’s schema API (aetherius_orchestrator::catalog::TableSchema, installed with catalog::install), not through SQL in this release.

A tenant snapshot is taken on the worker in one request and is complete in the time it takes to link the tenant’s files — it does not copy bytes, so a terabyte tenant snapshots in milliseconds. Every file the tenant owns is included: its containers and all of their sidecars (deleted-row marks, indexes and string data). A snapshot is immutable; asking for the same snapshot id twice is refused.

A restore is a routing change, not a copy: from the next statement on, every read and write for that tenant is served from the snapshot. Restoring to snapshot 0 returns the tenant to its live data. Nothing is moved and nothing waits.

Both are orchestrator API calls in this release (aetherius_orchestrator::topology::snapshot_tenant and restore_tenant_snapshot).

Refused statements and refused worker requests (tenancy), admitted and rejected statements per tenant (quota), snapshots requested, and the number of frames shipped for non-system tenants. See Observability.

  • The tenant id is a session attribute set by the embedding application; the wire protocols do not yet carry a client-side “use tenant N” setting.
  • Schema changes, snapshots and restores are API calls, not SQL statements.
  • A worker discovers each tenant’s directory lazily on first use; a directory created by hand is picked up on the next statement for that tenant.