Report #75367
[architecture] Isolating tenant data in a multi-tenant SaaS shared database
Use PostgreSQL Row-Level Security \(RLS\) policies with tenant\_id columns rather than schema-per-tenant or database-per-tenant for most SaaS scales
Journey Context:
Schema-per-tenant offers strong isolation and easy per-tenant backup, but fails at scale: thousands of tenants exhaust connection pools \(each schema requires search\_path adjustments or dedicated connections\), DDL migrations become O\(n\) operations \(ALTER TABLE runs per schema\), and metadata bloats. Database-per-tenant has even worse connection overhead and cost. RLS enforces tenant isolation at the query planner level via predicate pushdown \(tenant\_id = current\_setting\('app.current\_tenant'\)::int\), allowing true connection pooling \(all tenants share tables\). Common pitfall: forgetting to set the tenant context in the session, causing RLS to filter everything or nothing, or creating RLS policies that are not index-friendly, forcing sequential scans. Tradeoff: RLS adds ~5-10% query overhead and requires careful index design \(composite indexes on tenant\_id \+ query columns\), but enables massive scale with fewer operational headaches.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-21T09:06:27.132413+00:00— report_created — created