Skip to content

perf(schemas): index organization_role_user_relations on (tenant_id, organization_id, user_id)#8819

Merged
simeng-li merged 1 commit into
masterfrom
simeng-log-13473-perf-b-add-index-on-organization_role_user_relations
May 18, 2026
Merged

perf(schemas): index organization_role_user_relations on (tenant_id, organization_id, user_id)#8819
simeng-li merged 1 commit into
masterfrom
simeng-log-13473-perf-b-add-index-on-organization_role_user_relations

Conversation

@simeng-li
Copy link
Copy Markdown
Contributor

@simeng-li simeng-li commented May 15, 2026

Summary

Refs LOG-13473. Second migration in the Phase 0.5 performance milestone. Independent of LOG-13472 (separate table, no shared call sites); branch is cut from master, not stacked on #8818.

What changed

  • packages/schemas/alterations/next-1778500001-add-organization-role-user-relations-org-user-index.ts — new migration that adds create index concurrently if not exists organization_role_user_relations__tenant_id_org_id_user_id on organization_role_user_relations (tenant_id, organization_id, user_id). Built concurrently via the beforeUp hook so no table lock is held during the build. Reversible via beforeDown running drop index concurrently if exists.
  • packages/schemas/tables/organization_role_user_relations.sql — mirrors the new index inline so fresh installs get it without replaying the alteration.
  • .changeset/index-organization-role-user-relations-org-user.md@logto/schemas patch.

Expected result

Queries that filter by (organization_id, user_id) without specifying organization_role_id switch from a partial PK scan to an Index Scan on the new secondary index. Three hot call sites this fixes:

  • getUserScopes (packages/core/src/queries/organization/user-role-relations.ts:27-46) — every GET /organizations/:id/users/:userId/scopes.
  • The per-user role JOIN inside getUsersByOrganizationId (packages/core/src/queries/organization/user-relations.ts:121-141) — fan-out into this table on every paginated list-users request.
  • The DELETE clause in UserRoleRelationQueries.replace() — filter (organization_id, user_id).

No schema, API, or behavior change. Index is purely additive.

Reviewer notes

  • The PK column order (tenant_id, organization_id, organization_role_id, user_id) does not serve queries that skip organization_role_id. Postgres can use the PK efficiently only when all preceding columns are equality-matched; skipping organization_role_id forces a partial scan.
  • Effective WHERE clauses include tenant_id because every table in packages/schemas/tables/ carries the _after_each.sql RLS policy injecting tenant_id = (select id from tenants where db_user = current_user). The index column order matches.
  • Name abbreviation: the unabbreviated name (organization_role_user_relations__tenant_id_organization_id_user_id) is 67 chars and would be silently truncated by Postgres at the 63-char identifier limit. Abbreviated to org_id. Confirmed unique via grep.
  • Build runs via the beforeUp / beforeDown non-transactional hooks so CONCURRENTLY can be used without holding the write-blocking SHARE lock that a plain CREATE INDEX would hold for the duration of the build. This matches the pattern in 1.35.0-1765183934-add-logs-created-at-id-index.ts and 1.38.0-1772615848-add-oidc-model-instances-grant-id-partial-index.ts. if not exists / if exists keep both directions idempotent against transient failures.

Testing

Tested locally

Checklist

  • .changeset
  • unit tests
  • integration tests
  • necessary TSDoc comments

Copilot AI review requested due to automatic review settings May 15, 2026 06:54
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 15, 2026

COMPARE TO master

Total Size Diff 📈 +2.39 KB

Diff by File
Name Diff
.changeset/index-organization-role-user-relations-org-user.md 📈 +554 Bytes
packages/schemas/alterations/next-1778500001-add-organization-role-user-relations-org-user-index.ts 📈 +1.7 KB
packages/schemas/tables/organization_role_user_relations.sql 📈 +150 Bytes

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a secondary B-tree index on organization_role_user_relations (tenant_id, organization_id, user_id) to avoid partial PK scans for queries that filter without organization_role_id.

Changes:

  • New alteration next-1778500001-add-organization-role-user-relations-org-user-index.ts (reversible).
  • Mirror the index inline in tables/organization_role_user_relations.sql for fresh installs.
  • Changeset and a .gitignore entry for docs/superpowers/.

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated no comments.

File Description
packages/schemas/alterations/next-1778500001-add-organization-role-user-relations-org-user-index.ts New up/down migration creating/dropping the index.
packages/schemas/tables/organization_role_user_relations.sql Inline index definition for fresh installs.
.changeset/index-organization-role-user-relations-org-user.md Patch changeset for @logto/schemas.
.gitignore Ignore local docs/superpowers/ directory.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@simeng-li simeng-li force-pushed the simeng-log-13473-perf-b-add-index-on-organization_role_user_relations branch from fec6ec9 to 5abc9c6 Compare May 15, 2026 06:58
@github-actions github-actions Bot added size/s and removed size/s labels May 15, 2026
Copilot AI review requested due to automatic review settings May 15, 2026 09:34
@simeng-li simeng-li force-pushed the simeng-log-13473-perf-b-add-index-on-organization_role_user_relations branch from 5abc9c6 to c24fc13 Compare May 15, 2026 09:34
@github-actions github-actions Bot added size/s and removed size/s labels May 15, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 4 changed files in this pull request and generated no new comments.

@github-actions github-actions Bot added size/s and removed size/s labels May 15, 2026
…organization_id, user_id)

The primary key on organization_role_user_relations is
(tenant_id, organization_id, organization_role_id, user_id). Queries
that filter by (organization_id, user_id) without specifying
organization_role_id cannot use the PK efficiently and degrade to a
partial scan over all rows for the org.

Hot call sites:
- getUserScopes — invoked on every
  GET /organizations/:id/users/:userId/scopes.
- The per-user role join inside getUsersByOrganizationId — fan-out from
  organization_user_relations into this table on every paginated list.
- The DELETE filter in UserRoleRelationQueries.replace().

The new index is added to both the alteration script and the table
source file, matching the existing convention. The name
`organization_role_user_relations__tenant_id_org_id_user_id` abbreviates
`organization` to `org` because the unabbreviated form would exceed the
63-char Postgres identifier limit.

Refs LOG-13473
@simeng-li simeng-li force-pushed the simeng-log-13473-perf-b-add-index-on-organization_role_user_relations branch from c24fc13 to e5916dc Compare May 18, 2026 02:37
@github-actions github-actions Bot added size/s and removed size/s labels May 18, 2026
@simeng-li simeng-li enabled auto-merge (squash) May 18, 2026 02:41
@simeng-li simeng-li merged commit fafe81e into master May 18, 2026
45 of 46 checks passed
@simeng-li simeng-li deleted the simeng-log-13473-perf-b-add-index-on-organization_role_user_relations branch May 18, 2026 03:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Development

Successfully merging this pull request may close these issues.

4 participants