From 3a77f5bb1c2f6eaddf4ae5a1f4e81580b2d3bfaa Mon Sep 17 00:00:00 2001 From: Miranda Limonczenko Date: Mon, 17 Aug 2026 16:29:34 -0700 Subject: [PATCH] Apply feedback - Remove benchmarks - Remove 'This guide' from beginning of topic --- .../row-level-security-performance.mdx | 20 ++----------------- .../database/postgres/row-level-security.mdx | 2 +- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/apps/docs/content/guides/database/postgres/row-level-security-performance.mdx b/apps/docs/content/guides/database/postgres/row-level-security-performance.mdx index 1e9e5526d23..af0a3d11c7e 100644 --- a/apps/docs/content/guides/database/postgres/row-level-security-performance.mdx +++ b/apps/docs/content/guides/database/postgres/row-level-security-performance.mdx @@ -5,11 +5,11 @@ description: 'Measure and tune Postgres Row Level Security policies.' subtitle: 'Measure and tune Postgres Row Level Security policies.' --- -This guide explains how to measure the cost of Row Level Security (RLS) and tune policies that are already correct. To learn how to write correct policies, see [Row Level Security](/docs/guides/database/postgres/row-level-security). +Measure the cost of Row Level Security (RLS) and tune policies that are already correct. To learn how to write correct policies, see [Row Level Security](/docs/guides/database/postgres/row-level-security). Postgres evaluates a policy expression against each candidate row, so the cost scales with the rows a query scans. This matters most for queries that scan every row in a table, like many `select` operations, including those using limit, offset, and ordering. -Three policy rules affect performance enough that they belong with the policy itself rather than here. The RLS guide covers each one, and the [benchmarks](#benchmarks) on this page measure them: +Three policy rules affect performance enough that they belong with the policy itself rather than here. Apply them first: - [Index the columns your policies filter on](/docs/guides/database/postgres/row-level-security#add-indexes) - [Call functions with `select`](/docs/guides/database/postgres/row-level-security#call-functions-with-select) @@ -148,22 +148,6 @@ If the list exceeds 1000 items, a different approach may be needed, or you may n -## Benchmarks - -These numbers come from a series of [tests](https://github.com/GaryAustin1/RLS-Performance) run against a 100,000-row table. Each row links to the test that produced it. - -| Test | Before (ms) | After (ms) | % Improvement | Change | -| --------------------------------------------------------------------------------------------------------------------------------- | ----------- | ---------- | ------------- | ----------------------------------------------------------------------------------- | -| [test1-indexed](https://github.com/GaryAustin1/RLS-Performance/tree/main/tests/test1-indexed) | 171 | < 0.1 | 99.94% | No index, then `user_id` indexed | -| [test2a-wrappedSQL-uid]() | 179 | 9 | 94.97% | `auth.uid() = user_id`, then `(select auth.uid()) = user_id` | -| [test2b-wrappedSQL-isadmin]() | 11,000 | 7 | 99.94% | `is_admin()` table join, then `(select is_admin())` table join | -| [test2c-wrappedSQL-two-functions](https://github.com/GaryAustin1/RLS-Performance/tree/main/tests/test2c-wrappedSQL-two-functions) | 11,000 | 10 | 99.91% | Two bare function calls, then both wrapped in `select` | -| [test2d-wrappedSQL-sd-fun](https://github.com/GaryAustin1/RLS-Performance/tree/main/tests/test2d-wrappedSQL-sd-fun) | 178,000 | 12 | 99.993% | `has_role() = role`, then `(select has_role()) = role` | -| [test2e-wrappedSQL-sd-fun-array](https://github.com/GaryAustin1/RLS-Performance/tree/main/tests/test2e-wrappedSQL-sd-fun-array) | 173,000 | 16 | 99.991% | `team_id = any(user_teams())`, then `team_id = any(array(select user_teams()))` | -| [test3-addfilter](https://github.com/GaryAustin1/RLS-Performance/tree/main/tests/test3-addfilter) | 171 | 9 | 94.74% | No client filter, then `.eq` or `where` on `user_id` | -| [test5-fixed-join](https://github.com/GaryAustin1/RLS-Performance/tree/main/tests/test5-fixed-join) | 9,000 | 20 | 99.78% | `auth.uid()` in a table join on a column, then the column in a join on `auth.uid()` | -| [test6-To-role](https://github.com/GaryAustin1/RLS-Performance/tree/main/tests/test6-To-role) | 170 | < 0.1 | 99.78% | No `to` clause, then `to authenticated` with `anon` accessing | - ## More resources - [Row Level Security](/docs/guides/database/postgres/row-level-security) diff --git a/apps/docs/content/guides/database/postgres/row-level-security.mdx b/apps/docs/content/guides/database/postgres/row-level-security.mdx index 1e192914137..05099d5a1be 100644 --- a/apps/docs/content/guides/database/postgres/row-level-security.mdx +++ b/apps/docs/content/guides/database/postgres/row-level-security.mdx @@ -639,7 +639,7 @@ using ( (select auth.uid()) = user_id ); This prevents the policy `( (select auth.uid()) = user_id )` from running for any `anon` users, since the execution stops at the `to authenticated` step. -These three rules keep policies correct as a table grows. For the measured impact and for tuning beyond them, see [Row Level Security performance](/docs/guides/database/postgres/row-level-security-performance). +These three rules keep policies correct as a table grows. To diagnose whether a policy is still the bottleneck, and to tune beyond these rules, see [Row Level Security performance](/docs/guides/database/postgres/row-level-security-performance). ### Use security definer functions