Glossary
Multi-tenancy
One system serves several customers on the same infrastructure, without the data of one appearing for another.
The choice is between a shared database with a marker per tenant, separate schemas, and separate databases. It determines operational effort, cost, and how hard it later becomes to migrate a single customer.
Multi-tenancy is not only about storage: background processing, caches, logs and metrics all have to know the tenant, otherwise things mix in unexpected places.
The shared table with a tenant column is the cheapest variant to operate and the most demanding one in code, because every single query has to carry the column. One forgotten condition serves data belonging to another customer, and neither the compiler nor the test suite notices, as long as there is only one tenant in the test database. The safeguard therefore belongs underneath the application code: row-level security in the database, or an access layer that no query can bypass. Relying on discipline is not a safeguard, it is a hope.
Separate schemas sit in the middle: the same database instance, but a namespace of its own per tenant. The advantage is that a wrong query can no longer reach across customers. The price is schema changes that have to be applied to hundreds of namespaces, and connection handling that knows the tenant.
Separate databases or entire environments are the most expensive variant and sometimes the only sellable one, for example when contracts make demands about where data is held, or when a single customer outweighs the load of all the others. They bring a second problem with them: without end-to-end automation, every new customer turns into a manual setup procedure, and after thirty customers that is a full-time job.
Whatever the variant, a multi-tenant system needs an answer to the noisy neighbour. A customer who starts a bulk import must not degrade the response times of everyone else. What works are quotas per tenant, separate queues for background work, and an upper limit for expensive queries. Without those limits the first complaint is only a question of customer size.
The last point, regularly missing from the design: at some point a customer will leave, or will want an environment of their own. If nobody can say how to export all the data of one tenant completely and delete it afterwards, that request turns into a project of several weeks, and the duty to delete is a legal one on top.
How you notice it
- Several customers use the same application.
- A customer asks whether their data is stored separately.
- A single customer is meant to move to a dedicated environment later.
- The test database contains only one tenant.
- A large customer degrades the response times of the others.
Not to be confused with
- Single tenancy
- One installation per customer. The simplest separation, the highest operational effort, and every release becomes a rollout across all environments.
- Tenant column
- A concrete way of implementing it, not the concept. It separates data only as well as the queries take it into account.
- Sharding
- Spreading data across several nodes for load reasons. It can follow the tenant, but it pursues a different goal than separation.
When it fits
- A product is sold to many customers who barely differ in functionality.
- Operational effort and cost per customer should stay low.
- Releases should reach all customers at the same time.
When it does not
- When contracts or regulation demand separate data storage.
- When individual customers need their own extensions to the data model.
- When a single customer outweighs the load of all the others together.
How to approach it
- Pick the degree of separation per requirementShared table, separate schema or separate database. The contract has a say in this, not only the technology.
- Enforce the tenant reference, do not recommend itRow-level security in the database, or an access layer that nothing gets past. Discipline is not a safeguard.
- Carry the tenant through every layerCaches, queues, background jobs, logs, metrics, file storage. Every layer that forgets it is the next place where data leaks.
- Test with several tenantsTest data with at least two customers and a test that checks that queries return no foreign rows. One tenant in the test database never finds the bug.
- Set limits against the noisy neighbourQuotas per tenant, separate queues, upper limits for expensive queries. Otherwise the largest customer determines the response time for everyone.
- Build export and deletion from the startHand out all the data of one tenant and delete it completely afterwards. That request will come, commercially and legally.
Frequently asked
How do you stop a query from forgetting the tenant?
By making it impossible rather than requiring it. Row-level security applies the condition on the database server; a mandatory access layer applies it in the code. On top of that, a test with two tenants that fails as soon as a query returns foreign rows. Agreements within the team alone do not survive the first deadline.
What is the noisy neighbour and what helps against it?
A customer whose load degrades the response times of everyone else, for example through a bulk import or a very large report. What works are quotas per tenant, dedicated queues for background work and upper limits for expensive queries. The escape route of moving that customer into a dedicated environment should be prepared before it is needed.
How do you pull a single customer out later?
Only with little pain if the per-tenant export has existed from the start and identifiers are unique per tenant rather than globally sequential. Building it afterwards means sorting records out of tables in which customers are interwoven through foreign keys, and that takes weeks.
