Skip to content

perf: add missing indexes on all foreign key columns#2461

Merged
fallenbagel merged 1 commit intodevelopfrom
fallenbagel/perf/add-fk-indexes
Feb 16, 2026
Merged

perf: add missing indexes on all foreign key columns#2461
fallenbagel merged 1 commit intodevelopfrom
fallenbagel/perf/add-fk-indexes

Conversation

@fallenbagel
Copy link
Collaborator

@fallenbagel fallenbagel commented Feb 16, 2026

Description

Adds Indexes to all @ManyToOne relations that were missing FK indexes. Most impactful is season.mediaId which caused 10+ second load times on the discover page for users with large libraries (~50k media) (reported by @mmozeiko). Fixes a full table scan on every eager-loaded join.

How Has This Been Tested?

Screenshots / Logs (if applicable)

Checklist:

  • I have read and followed the contribution guidelines.
  • Disclosed any use of AI (see our policy)
  • I have updated the documentation accordingly.
  • All new and existing tests passed.
  • Successful build pnpm build
  • Translation keys pnpm i18n:extract
  • Database migration (if required)

Summary by CodeRabbit

  • Chores
    • Optimized database performance by indexing key relationship fields across multiple entities to improve query speed and efficiency without affecting existing functionality.

@fallenbagel fallenbagel requested a review from a team as a code owner February 16, 2026 15:57
@coderabbitai
Copy link

coderabbitai bot commented Feb 16, 2026

📝 Walkthrough

Walkthrough

Database indexes are added to multiple entity relations across eight TypeORM entity files using the @Index() decorator. Corresponding migrations for PostgreSQL and SQLite databases create these indexes and manage foreign key constraints at the database level.

Changes

Cohort / File(s) Summary
Entity Index Decorators
server/entity/Blocklist.ts, server/entity/Issue.ts, server/entity/IssueComment.ts, server/entity/MediaRequest.ts, server/entity/Season.ts, server/entity/SeasonRequest.ts, server/entity/UserPushSubscription.ts, server/entity/Watchlist.ts
Added @Index() decorators to various foreign key relations (user, media, issueType, createdBy, modifiedBy, requestedBy, issue, request) with corresponding Index imports from TypeORM. Total 16 index decorators added across 8 entities with no changes to control flow or business logic.
Database Migrations
server/migration/postgres/1771259406751-AddForeignKeyIndexes.ts, server/migration/sqlite/1771259394105-AddForeignKeyIndexes.ts
Implemented TypeORM migrations for PostgreSQL and SQLite that create the declared indexes, manage foreign key constraints (including NO ACTION and CASCADE rules), handle sequence defaults, and preserve data through temporary table operations during schema modifications. Up/down methods fully support rollback.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~30 minutes

Poem

🐰✨ Indexes are hopping into place,

Foreign keys dancing with grace,

Tables indexed, migrations run,

Database queries now swift as fun!

Whiskers twitch at optimization's delight! 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely describes the main change: adding indexes to foreign key columns for performance improvement.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into develop

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@server/migration/postgres/1771257215823-AddForeignKeyIndexes.ts`:
- Around line 131-132: In the migration AddForeignKeyIndexes
(server/migration/postgres/1771257215823-AddForeignKeyIndexes.ts) the
down/rollback uses nextval('blacklist_id_seq') but the up path creates
blocklist_id_seq; update the down migration statement that alters
"blocklist"."id" to use nextval('blocklist_id_seq') (or otherwise make the
sequence name consistent between up and down) so rollback won't fail due to a
missing sequence.
🧹 Nitpick comments (3)
server/migration/postgres/1771257215823-AddForeignKeyIndexes.ts (1)

26-69: Plan for index-creation impact on large tables.

Multiple new indexes on large tables can lock writes; consider running this migration during a maintenance window or other low-traffic period to minimize contention.

server/migration/sqlite/1771257189964-AddForeignKeyIndexes.ts (2)

23-41: Remove duplicate user_push_subscription rebuild in up.

The table is recreated twice back-to-back with identical schema, which doubles copy work and lengthens the migration without a schema change.

♻️ Suggested simplification
-    await queryRunner.query(
-      `CREATE TABLE "temporary_user_push_subscription" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "endpoint" varchar NOT NULL, "p256dh" varchar NOT NULL, "auth" varchar NOT NULL, "userId" integer, "userAgent" varchar, "createdAt" datetime DEFAULT (CURRENT_TIMESTAMP), CONSTRAINT "UQ_6427d07d9a171a3a1ab87480005" UNIQUE ("endpoint", "userId"), CONSTRAINT "UQ_f90ab5a4ed54905a4bb51a7148b" UNIQUE ("auth"), CONSTRAINT "FK_03f7958328e311761b0de675fbe" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`
-    );
-    await queryRunner.query(
-      `INSERT INTO "temporary_user_push_subscription"("id", "endpoint", "p256dh", "auth", "userId", "userAgent", "createdAt") SELECT "id", "endpoint", "p256dh", "auth", "userId", "userAgent", "createdAt" FROM "user_push_subscription"`
-    );
-    await queryRunner.query(`DROP TABLE "user_push_subscription"`);
-    await queryRunner.query(
-      `ALTER TABLE "temporary_user_push_subscription" RENAME TO "user_push_subscription"`
-    );

162-180: Likewise, collapse the duplicate rebuild in down.

The rollback repeats the same user_push_subscription rebuild twice, which adds unnecessary work during downgrade.

♻️ Suggested simplification
-    await queryRunner.query(
-      `ALTER TABLE "user_push_subscription" RENAME TO "temporary_user_push_subscription"`
-    );
-    await queryRunner.query(
-      `CREATE TABLE "user_push_subscription" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "endpoint" varchar NOT NULL, "p256dh" varchar NOT NULL, "auth" varchar NOT NULL, "userId" integer, "userAgent" varchar, "createdAt" datetime DEFAULT (CURRENT_TIMESTAMP), CONSTRAINT "UQ_6427d07d9a171a3a1ab87480005" UNIQUE ("endpoint", "userId"), CONSTRAINT "UQ_f90ab5a4ed54905a4bb51a7148b" UNIQUE ("auth"), CONSTRAINT "FK_03f7958328e311761b0de675fbe" FOREIGN KEY ("userId") REFERENCES "user" ("id") ON DELETE CASCADE ON UPDATE NO ACTION)`
-    );
-    await queryRunner.query(
-      `INSERT INTO "user_push_subscription"("id", "endpoint", "p256dh", "auth", "userId", "userAgent", "createdAt") SELECT "id", "endpoint", "p256dh", "auth", "userId", "userAgent", "createdAt" FROM "temporary_user_push_subscription"`
-    );
-    await queryRunner.query(`DROP TABLE "temporary_user_push_subscription"`);

@danshilm
Copy link
Contributor

@fallenbagel add an index on the issue type too on the Issue entity
While logging requests to the database I saw a lot of queries that would benefit from it, and if someone has a lot of issues this might be slow for them.

@fallenbagel
Copy link
Collaborator Author

@fallenbagel add an index on the issue type too on the Issue entity
While logging requests to the database I saw a lot of queries that would benefit from it, and if someone has a lot of issues this might be slow for them.

Let me update it. Thanks!

Adds indexes to all @manytoone Fk columns to prevent full table scans on joins
@fallenbagel fallenbagel force-pushed the fallenbagel/perf/add-fk-indexes branch from ff08937 to b41330a Compare February 16, 2026 16:31
Copy link
Contributor

@danshilm danshilm left a comment

Choose a reason for hiding this comment

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

Looks good!
Thanks for picking that up

Copy link
Member

@gauthier-th gauthier-th left a comment

Choose a reason for hiding this comment

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

LGTM

@fallenbagel fallenbagel enabled auto-merge (squash) February 16, 2026 16:33
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@server/migration/postgres/1771259406751-AddForeignKeyIndexes.ts`:
- Around line 17-24: Remove the erroneous DROP of the id default and fix the
sequence name typo: in the migration where you create and set the sequence for
"blocklist" (references to "blocklist_id_seq" and ALTER TABLE "blocklist" ALTER
COLUMN "id" SET DEFAULT nextval('"blocklist_id_seq"')), delete the subsequent
`ALTER TABLE "blocklist" ALTER COLUMN "id" DROP DEFAULT` so the auto-increment
default remains; then in the down migration replace the incorrect
`'blacklist_id_seq'` reference with `'blocklist_id_seq'` so rollback references
the same sequence created in up.
🧹 Nitpick comments (1)
server/migration/sqlite/1771259394105-AddForeignKeyIndexes.ts (1)

7-118: Consider consolidating repeated table rebuilds and index create/drop cycles.
The up path rebuilds blocklist and user_push_subscription twice and creates/drops the same indexes in short succession, which can significantly slow migrations on large datasets. If feasible, consolidate into a single rebuild per table and create indexes only once at the end.

@fallenbagel fallenbagel merged commit c6bcfe0 into develop Feb 16, 2026
11 checks passed
@fallenbagel fallenbagel deleted the fallenbagel/perf/add-fk-indexes branch February 16, 2026 16:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

Comments