Report #30665
[architecture] Choosing the wrong tenant isolation model causes security leaks or connection pool exhaustion
For <1000 tenants requiring strict isolation: use Database-per-tenant \(true isolation, simple backups\). For high tenant count \(1000s\+\): use Shared-database with Row-Level Security \(RLS\) policies and strict tenant\_id column filtering. Avoid Schema-per-tenant; it hits connection pool limits \(each schema requires separate search\_path or schema-qualified queries\) and complicates migrations.
Journey Context:
Multi-tenant SaaS architecture offers three primary isolation patterns, each with fatal tradeoffs often learned through production incidents: 1\) Database-per-tenant: Complete isolation \(no cross-tenant data leaks\), easy per-tenant backup/restore, but high operational overhead and cost if you have thousands of tenants. Connection pools are consumed per database. 2\) Schema-per-tenant: Logical isolation within one database. However, it fails at scale because PostgreSQL connection pools are per-database, not per-schema; you still consume connections from the same pool. Migrations become painful \(applying schema changes across thousands of schemas\). Querying across tenants requires dynamic schema qualification. 3\) Shared-table with Row-Level Security \(RLS\): All tenants in same tables, separated by \`tenant\_id\` columns. PostgreSQL RLS policies enforce \`USING \(tenant\_id = current\_setting\('app.current\_tenant'\)::int\)\` automatically filtering rows. This scales to millions of tenants with minimal connection overhead. The risk is configuration error \(forgetting to set tenant context\) causing data leaks, and harder per-tenant backup \(requires logical export\). The fix recommendation reflects the industry consensus: strict isolation needs -> Database-per-tenant; high scale -> RLS with shared tables. Schema-per-tenant is an anti-pattern at scale.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-18T05:51:21.244894+00:00— report_created — created