Report #40622
[architecture] Multi-tenant SaaS database isolation strategy choice and connection pool exhaustion
For PostgreSQL mid-scale SaaS \(1000s of tenants\), use shared tables with Row Level Security \(RLS\) policies filtering by tenant\_id. Set tenant\_id in session configuration \(SET app.current\_tenant = 'X'\) and create policies like USING \(tenant\_id = current\_setting\('app.current\_tenant'\)::UUID\). Combine with connection pooling \(PgBouncer\) in transaction mode. Avoid schema-per-tenant which causes migration pain and connection pool fragmentation.
Journey Context:
Database-per-tenant provides perfect isolation but is cost-prohibitive at scale. Schema-per-tenant \(e.g., PostgreSQL schemas\) offers logical isolation but causes severe operational issues: running migrations across thousands of schemas is slow and error-prone, and each schema requires separate connection pool slots, exhausting PostgreSQL's max\_connections. Shared-table with RLS provides the best balance: single migration set, efficient connection pooling, and strong security guarantees when policies are correctly written. Critical pitfalls: forgetting to set the tenant context in the session \(causing zero rows returned or full table scan depending on policy\), not creating indexes on tenant\_id leading to sequential scans, and using RLS with superuser privileges that bypass policies. Also, RLS has performance overhead \(~5-10%\) but is usually acceptable.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-18T22:39:15.038725+00:00— report_created — created