58 lines
1.5 KiB
Markdown
58 lines
1.5 KiB
Markdown
---
|
|
title: Optimize RLS Policies for Performance
|
|
impact: HIGH
|
|
impactDescription: 5-10x faster RLS queries with proper patterns
|
|
tags: rls, performance, security, optimization
|
|
---
|
|
|
|
## Optimize RLS Policies for Performance
|
|
|
|
Poorly written RLS policies can cause severe performance issues. Use subqueries and indexes strategically.
|
|
|
|
**Incorrect (function called for every row):**
|
|
|
|
```sql
|
|
create policy orders_policy on orders
|
|
using (auth.uid() = user_id); -- auth.uid() called per row!
|
|
|
|
-- With 1M rows, auth.uid() is called 1M times
|
|
```
|
|
|
|
**Correct (wrap functions in SELECT):**
|
|
|
|
```sql
|
|
create policy orders_policy on orders
|
|
using ((select auth.uid()) = user_id); -- Called once, cached
|
|
|
|
-- 100x+ faster on large tables
|
|
```
|
|
|
|
Use security definer functions for complex checks:
|
|
|
|
```sql
|
|
-- Create helper function (runs as definer, bypasses RLS)
|
|
create or replace function is_team_member(team_id bigint)
|
|
returns boolean
|
|
language sql
|
|
security definer
|
|
set search_path = ''
|
|
as $$
|
|
select exists (
|
|
select 1 from public.team_members
|
|
where team_id = $1 and user_id = (select auth.uid())
|
|
);
|
|
$$;
|
|
|
|
-- Use in policy (indexed lookup, not per-row check)
|
|
create policy team_orders_policy on orders
|
|
using ((select is_team_member(team_id)));
|
|
```
|
|
|
|
Always add indexes on columns used in RLS policies:
|
|
|
|
```sql
|
|
create index orders_user_id_idx on orders (user_id);
|
|
```
|
|
|
|
Reference: [RLS Performance](https://supabase.com/docs/guides/database/postgres/row-level-security#rls-performance-recommendations)
|