Salesforce's published Force.com design keeps many customers' records in shared tables described by metadata, rather than giving each tenant its own schema. Weissman and Bobrowski's SIGMOD 2009 paper describes the platform supporting tens of thousands of organisations this way. What does that choice force about access control, and where would copying it be a mistake?
Show the full answer Hide the answer
The situation they were in
A platform where every customer can add fields, objects and validation rules, and where upgrades must apply to everyone at once. Per-tenant schemas make that impossible at scale: 55,000 organisations would mean 55,000 schema variants to migrate on every release, and database catalogues are not built for that many objects.
What they chose
Shared physical tables, with a metadata layer describing what each tenant's fields mean and where they live, and a runtime that constructs queries from that metadata. The paper is explicit that this is what makes a single upgrade serve every tenant.
What it forces about access control
Once rows from different tenants share a table, tenant isolation stops being a property of the schema and becomes a property of every query. There is no arrangement of grants that isolates a customer, because the grain of the grant is the table and the grain of the isolation requirement is the row.
That has three consequences that generalise well beyond this platform:
- The filter must be applied by something no application code can omit — the query-construction layer, or the database's own row-level policy. A convention that developers add a tenant predicate is not a control, because the failure is one forgotten
WHEREclause and the symptom is another customer's data on the screen. - The identity that carries the tenant must be established before the query is built, and must not be derivable from user-supplied input.
- Testing changes shape. The important test is not "does the query return the right rows" but "does any code path reach the table without the predicate", which is a static question about the architecture rather than a dynamic one about a result set.
What it cost them
Query plans over shared tables are harder for the optimiser, which is why such designs lean on custom indexing structures and pivot tables rather than ordinary per-column indexes. Per-tenant performance isolation becomes work the platform must do explicitly, because the storage no longer provides it. And the blast radius of a bug in the query-construction layer is every tenant at once — the price of having exactly one code path.
When copying it would be wrong
This design is justified by tenant count and by per-tenant extensibility. A business-to-business product with 40 enterprise customers, each wanting isolation they can point at in a contract, gets more from a database per tenant: isolation is structural, noisy neighbours are impossible, a restore affects one customer, and regional residency is a deployment decision rather than a query predicate.
The decision flips at the point where per-tenant operations stop fitting your maintenance window — where 55,000 schema migrations is obviously impossible and 40 is obviously fine, and the crossover is somewhere in the hundreds. Below it, shared tables buy efficiency you do not need and hand you an isolation problem you did not have. The common mistake is copying the architecture of a company with 100,000 tenants while having 30.