Report #96715
[architecture] PostgreSQL Row Level Security \(RLS\) causes severe performance degradation \(10-100x slowdowns\) due to plan cache pollution and inability to use certain indexes
Only enable RLS on tables with high-cardinality tenant filters; always include the tenant\_id as the leading column in indexes used with RLS policies; avoid RLS on tables with >10k rows per tenant unless using Citus/Postgres-XL. Prefer application-level enforcement \(SET LOCAL app.tenant\_id \+ views/triggers\) for complex multi-tenant isolation to avoid plan cache pollution.
Journey Context:
RLS policies are applied as inline subqueries during planning; PostgreSQL's plan cache treats queries with different RLS parameter values as different plans \(cache pollution\), causing constant replanning. Policies that use CURRENT\_USER or session variables prevent index usage unless the column is indexed and the policy is simple equality. High-cardinality filters \(many tenants, few rows each\) work well; low-cardinality \(few tenants, many rows each\) causes sequential scans because the planner assumes RLS filters are selective. Application-level filtering via 'SET LOCAL' with a custom GUC variable and using that in a partial index avoids the plan cache issues while maintaining security \(though it requires discipline\). Schema-per-tenant avoids this entirely but has connection pool limitations.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-22T20:55:19.247966+00:00— report_created — created