Skip to content

Conversation

@gurusainath
Copy link
Member

@gurusainath gurusainath commented Sep 1, 2025

Description

Added compound and single-field database indexes to optimize performance filtering.
Indexes have been added for the following table fields:

  1. Notification table
    1. entity_name
    2. entity_identifier
    3. read_at
    4. receiver, read_at
  2. FileAsset Table
    1. entity_type
    2. entity_identifier
    3. entity_type, entity_identifier
  3. UserFavorite Table
    1. entity_type
    2. entity_identifier
    3. entity_type, entity_identifier
  4. PageLog Table
    1. entity_type
    2. entity_name
    3. entity_identifier
    4. entity_type, entity_identifier
    5. entity_name, entity_identifier

These indexes will significantly improve performance for notification filters, especially in inbox views, pagination, and unread count computations.

Type of Change

  • Improvement (change that would cause existing functionality to not work as expected)
  • Performance improvements

References

[WEB-4823]

Summary by CodeRabbit

  • Chores
    • Improved performance for Notifications, File Attachments, Favorites, and Page Activity logs via database optimizations, yielding faster loading and filtering—especially in large workspaces.
    • Unread notification checks and read-state updates are noticeably quicker.
    • Linking and retrieving assets and favorites is more responsive.
    • Migration runs in the background with no expected downtime or user action required.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 1, 2025

Walkthrough

Adds explicit database indexes to Notification, FileAsset, UserFavorite, and PageLog models via Meta.indexes and introduces a non-atomic migration creating these indexes concurrently across the four tables.

Changes

Cohort / File(s) Summary
Model index additions
apps/api/plane/db/models/notification.py, apps/api/plane/db/models/asset.py, apps/api/plane/db/models/favorite.py, apps/api/plane/db/models/page.py
Define new Meta.indexes for query fields: Notification (entity_identifier, entity_name, read_at, receiver+read_at), FileAsset (entity_type, entity_identifier, entity_type+entity_identifier), UserFavorite (entity_type, entity_identifier, entity_type+entity_identifier), PageLog (entity_type, entity_identifier, entity_name, entity_type+entity_identifier, entity_name+entity_identifier). No field semantics changed.
Migration for concurrent index creation
apps/api/plane/db/migrations/0103_fileasset_asset_entity_type_idx_and_more.py
Non-atomic migration with 15 AddIndexConcurrently operations creating the above indexes for fileasset, notification, pagelog, and userfavorite. Depends on 0102.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

I hop through schemas, light and spry,
Planting indexes as I fly.
Fields align, the queries hum,
B-trees bloom—oh, outcome yum!
With concurrent steps I tread,
No locks to fill the ops with dread.
Carrot cached, performance fed.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch indexing-notification-fields

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@makeplane
Copy link

makeplane bot commented Sep 1, 2025

Pull Request Linked with Plane Work Items

Comment Automatically Generated by Plane

@dheeru0198
Copy link
Member

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 1, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@dheeru0198 dheeru0198 requested a review from Copilot September 1, 2025 12:14

This comment was marked as outdated.

@gurusainath gurusainath marked this pull request as ready for review September 1, 2025 12:25
@dheeru0198 dheeru0198 requested a review from Copilot September 1, 2025 12:36

This comment was marked as outdated.

Copy link
Contributor

@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

🧹 Nitpick comments (3)
apps/api/plane/db/models/notification.py (3)

17-18: Good call adding indexes; consider trigram for entity_name if using icontains/ILIKE

  • entity_identifier and entity_name indexes look right.
  • If entity_name is filtered with icontains/ILIKE/prefix, add a GIN trigram index; B-Tree won’t help those queries.

Add this (Postgres-only):

 class Meta:
   ...
   indexes = [
+    # Fast icontains/ILIKE on entity_name
+    GinIndex(fields=["entity_name"], name="notifications_entity_name_trgm", opclasses=["gin_trgm_ops"]),
   ]

Also add the import elsewhere in this file:

from django.contrib.postgres.indexes import GinIndex

And ensure pg_trgm is enabled via a migration:

from django.contrib.postgres.operations import TrigramExtension

33-33: Unread-focused index can be smaller and faster

Indexing read_at is useful; for unread counts/views, a partial index WHERE read_at IS NULL on (receiver, workspace, -created_at) typically outperforms a full (… , read_at) index and helps pagination.

Example (Meta.indexes):

 from django.db.models import Q
 ...
 indexes = [
+  models.Index(
+    fields=["receiver", "workspace", "-created_at"],
+    name="notifications_unread_rx_ws_createdat_idx",
+    condition=Q(read_at__isnull=True),
+  ),
 ]

42-51: Drop redundant indexes and optimize composite for pagination

  • Remove receiver_workspace_idx and receiver_read_at_idx—the existing (receiver, workspace, read_at) index covers both.
  • Extend your composite index to support ORDER BY snoozed_till, -created_at (e.g. fields=["receiver","workspace","read_at","snoozed_till","-created_at"]) for efficient LIMIT pagination.
  • Prefix names with notifications_ for cross-table uniqueness.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 0358e9b and ca48340.

📒 Files selected for processing (2)
  • apps/api/plane/db/migrations/0103_alter_notification_entity_identifier_and_more.py (1 hunks)
  • apps/api/plane/db/models/notification.py (3 hunks)
🔇 Additional comments (1)
apps/api/plane/db/migrations/0103_alter_notification_entity_identifier_and_more.py (1)

28-39: Use concurrent index creation (AddIndexConcurrently) and mark the migration non-atomic

Replace migrations.AddIndex(...) with AddIndexConcurrently(...) (from django.contrib.postgres.operations) and set atomic = False because CREATE INDEX CONCURRENTLY cannot run inside a transaction. (docs.djangoproject.com, dokk.org)

Apply the original suggested diff (unchanged):

-from django.db import migrations, models
+from django.db import migrations, models
+from django.contrib.postgres.operations import AddIndexConcurrently
@@
 class Migration(migrations.Migration):
 
+    # Required for CONCURRENTLY
+    atomic = False
@@
-        migrations.AddIndex(
+        AddIndexConcurrently(
             model_name='notification',
             index=models.Index(fields=['receiver', 'workspace'], name='receiver_workspace_idx'),
         ),
-        migrations.AddIndex(
+        AddIndexConcurrently(
             model_name='notification',
             index=models.Index(fields=['receiver', 'read_at'], name='receiver_read_at_idx'),
         ),
-        migrations.AddIndex(
+        AddIndexConcurrently(
             model_name='notification',
             index=models.Index(fields=['receiver', 'workspace', 'read_at'], name='receiver_workspace_read_at_idx'),
         ),

Optional: add an unread-optimized partial index (Postgres-only) — descending ordering with '-' and partial index via condition are supported by Django Index. (docs.djangoproject.com)

Verification notes: rg shows the three index names only in apps/api/plane/db/migrations/0103_alter_notification_entity_identifier_and_more.py and no existing AddIndexConcurrently usages; repository did not expose a pinned Django version — confirm the project uses PostgreSQL and Django >= 3.0 before applying this change. (dokk.org, docs.djangoproject.com)

Copy link
Contributor

@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: 2

♻️ Duplicate comments (1)
apps/api/plane/db/migrations/0103_alter_notification_entity_identifier_and_more.py (1)

13-27: Avoid long locks: create these single-field indexes concurrently and make the migration non-atomic.

AlterField(db_index=True) builds indexes inside the transaction and can take ACCESS EXCLUSIVE locks on a large notification table. Switch to concurrent index creation via SeparateDatabaseAndState + AddIndexConcurrently and set atomic = False.

Apply:

@@
-from django.db import migrations, models
+from django.db import migrations, models
+from django.db.models import Q
+from django.contrib.postgres.operations import AddIndexConcurrently
@@
-class Migration(migrations.Migration):
+class Migration(migrations.Migration):
+    # Required for *Concurrently operations
+    atomic = False
@@
-    operations = [
-        migrations.AlterField(
-            model_name='notification',
-            name='entity_identifier',
-            field=models.UUIDField(db_index=True, null=True),
-        ),
-        migrations.AlterField(
-            model_name='notification',
-            name='entity_name',
-            field=models.CharField(db_index=True, max_length=255),
-        ),
-        migrations.AlterField(
-            model_name='notification',
-            name='read_at',
-            field=models.DateTimeField(db_index=True, null=True),
-        ),
-        migrations.AddIndex(
-            model_name='notification',
-            index=models.Index(fields=['receiver', 'read_at'], name='receiver_read_at_idx'),
-        ),
-    ]
+    operations = [
+        migrations.SeparateDatabaseAndState(
+            state_operations=[
+                migrations.AlterField(
+                    model_name='notification',
+                    name='entity_identifier',
+                    field=models.UUIDField(db_index=True, null=True),
+                ),
+                migrations.AlterField(
+                    model_name='notification',
+                    name='entity_name',
+                    field=models.CharField(db_index=True, max_length=255),
+                ),
+                migrations.AlterField(
+                    model_name='notification',
+                    name='read_at',
+                    field=models.DateTimeField(db_index=True, null=True),
+                ),
+            ],
+            database_operations=[
+                AddIndexConcurrently(
+                    model_name='notification',
+                    index=models.Index(fields=['entity_identifier'], name='notification_entity_identifier_idx'),
+                ),
+                AddIndexConcurrently(
+                    model_name='notification',
+                    index=models.Index(fields=['entity_name'], name='notification_entity_name_idx'),
+                ),
+                AddIndexConcurrently(
+                    model_name='notification',
+                    index=models.Index(fields=['read_at'], name='notification_read_at_idx'),
+                ),
+                AddIndexConcurrently(
+                    model_name='notification',
+                    index=models.Index(fields=['receiver', 'read_at'], name='receiver_read_at_idx'),
+                ),
+            ],
+        ),
+    ]
🧹 Nitpick comments (4)
apps/api/plane/db/migrations/0103_alter_notification_entity_identifier_and_more.py (4)

25-26: Speed up unread counts: add a partial index on unread rows.

If unread = read_at IS NULL is common, a partial index is smaller and faster.

@@
             database_operations=[
                 ...
+                AddIndexConcurrently(
+                    model_name='notification',
+                    index=models.Index(
+                        fields=['receiver', 'workspace'],
+                        name='receiver_workspace_unread_idx',
+                        condition=Q(read_at__isnull=True),
+                    ),
+                ),

21-21: Validate entity_name access patterns; consider pattern-ops or trigram index if prefix/ILIKE searches are used.

If queries use ILIKE 'prefix%', a btree with varchar_pattern_ops (or pg_trgm for general substring) is more effective than a plain btree. Optional and Postgres-specific.

I can propose an alternative index once you confirm query patterns and Postgres usage in prod.


30-30: Index naming nit: consider app/table prefix to avoid cross-table name collisions.

E.g., db_notification_receiver_read_at_idx instead of receiver_read_at_idx. Optional.


12-32: Audit and refine Notification indexes

  • Queries observed only filter by workspace__slug+receiver_id, by workspace__slug+receiver_id+read_at__isnull, then by entity_name on that result set
  • The composite index on (receiver,read_at) covers the unread‐filter but cannot help with workspace and the standalone read_at index has no standalone use—drop it
  • No filters on entity_identifier found—its index appears unused
  • entity_name is always filtered alongside workspace+receiver—consider a composite index on (workspace_id,receiver_id,entity_name) instead of its single-field index
    Verify these query shapes against production workloads and adjust your index set accordingly.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between ca48340 and 9638ed5.

📒 Files selected for processing (2)
  • apps/api/plane/db/migrations/0103_alter_notification_entity_identifier_and_more.py (1 hunks)
  • apps/api/plane/db/models/notification.py (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/api/plane/db/models/notification.py

Copy link
Contributor

@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: 2

🧹 Nitpick comments (8)
apps/api/plane/db/models/favorite.py (2)

18-19: Avoid redundant single-column index on entity_type.

Since a composite index on (entity_type, entity_identifier) is added below, the standalone index on entity_type is likely redundant. Keep a single-column index on entity_identifier only if you do queries by identifier alone.

Apply this diff:

-    entity_type = models.CharField(max_length=100, db_index=True)
+    entity_type = models.CharField(max_length=100)

44-49: Tune index for common access pattern: include user and make it partial on active rows.

Favorites are typically fetched per user and active (deleted_at IS NULL). Indexing only (entity_type, entity_identifier) may not help those queries.

Apply this diff:

-        indexes = [
-            models.Index(
-                fields=["entity_type", "entity_identifier"],
-                name="user_favorite_entity_idx",
-            ),
-        ]
+        indexes = [
+            models.Index(
+                fields=["user", "entity_type", "entity_identifier"],
+                name="user_favorite_user_entity_idx",
+                condition=models.Q(deleted_at__isnull=True),
+            ),
+        ]

To verify, EXPLAIN your most frequent SELECTs on user_favorites and confirm this index is chosen.

apps/api/plane/db/models/asset.py (2)

64-67: Trim redundant index and validate identifier type.

  • Composite index below covers the leftmost column; drop the standalone index on entity_type to reduce write overhead.
  • entity_identifier is CharField while other models use UUIDField; if values are UUIDs, consider UUIDField for storage/validation benefits.

Apply this diff to remove the redundant single-column index:

-    entity_type = models.CharField(max_length=255, null=True, blank=True, db_index=True)
+    entity_type = models.CharField(max_length=255, null=True, blank=True)

Confirm whether FileAsset.entity_identifier stores mixed formats; if always UUIDs, we can plan a safe data migration to UUIDField.


81-86: Confirm column order matches filter/sort pattern.

If queries often filter by entity_identifier (and optionally entity_type), indexing (entity_identifier, entity_type) yields better selectivity. Otherwise current order is fine since you also have a single-column index on entity_identifier.

Optional reorder:

-            models.Index(
-                fields=["entity_type", "entity_identifier"],
-                name="file_asset_entity_idx",
-            ),
+            models.Index(
+                fields=["entity_identifier", "entity_type"],
+                name="file_asset_entity_idx",
+            ),

Also consider a partial index excluding soft-deleted/archived rows if most queries do so (condition=Q(is_deleted=False, is_archived=False)).

apps/api/plane/db/models/page.py (2)

102-108: Drop low-selectivity single-column indexes now covered by composites.

entity_name/entity_type typically have low cardinality. Since composite indexes below start with these columns, standalone db_index=True on them is unnecessary. Keep the single-column index on entity_identifier if you filter by it alone.

Apply this diff:

-    entity_identifier = models.UUIDField(null=True, blank=True, db_index=True)
-    entity_name = models.CharField(
-        max_length=30, verbose_name="Transaction Type", db_index=True
-    )
-    entity_type = models.CharField(
-        max_length=30, verbose_name="Entity Type", null=True, blank=True, db_index=True
-    )
+    entity_identifier = models.UUIDField(null=True, blank=True, db_index=True)
+    entity_name = models.CharField(
+        max_length=30, verbose_name="Transaction Type"
+    )
+    entity_type = models.CharField(
+        max_length=30, verbose_name="Entity Type", null=True, blank=True
+    )

119-128: Validate composite index order against real queries.

If the common filter is by entity_identifier (optionally plus type/name), reversing order to start with entity_identifier can help. Otherwise current design + single-column entity_identifier index is acceptable.

Optional swap example:

-            models.Index(
-                fields=["entity_type", "entity_identifier"],
-                name="page_log_entity_idx",
-            ),
+            models.Index(
+                fields=["entity_identifier", "entity_type"],
+                name="page_log_entity_idx",
+            ),
-            models.Index(
-                fields=["entity_name", "entity_identifier"],
-                name="page_log_entity_identifier_idx",
-            ),
+            models.Index(
+                fields=["entity_identifier", "entity_name"],
+                name="page_log_entity_identifier_idx",
+            ),

Run EXPLAIN on your hot PageLog queries to confirm.

apps/api/plane/db/migrations/0103_alter_fileasset_entity_identifier_and_more.py (2)

28-37: Reassess single-column indexes on notification.

Indexing entity_name and read_at individually may be unnecessary if queries always include receiver. Prefer (receiver, read_at) plus the partial unread index suggested above; drop the standalone read_at index to save space.

If you agree, remove db_index=True from Notification.read_at in the model and replace with the partial index via a follow-up migration.


12-23: Heads-up: AlterField with db_index=True also creates indexes non-concurrently.

Where feasible, avoid AlterField solely to add indexes on large tables. Instead, keep the model field without db_index and add explicit concurrent indexes via operations. This prevents write locks.

I can push a revised pair of migrations: (1) add concurrent indexes, (2) then update model fields without triggering new indexes.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9638ed5 and 3b4e387.

📒 Files selected for processing (5)
  • apps/api/plane/db/migrations/0103_alter_fileasset_entity_identifier_and_more.py (1 hunks)
  • apps/api/plane/db/models/asset.py (2 hunks)
  • apps/api/plane/db/models/favorite.py (2 hunks)
  • apps/api/plane/db/models/notification.py (3 hunks)
  • apps/api/plane/db/models/page.py (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/api/plane/db/models/notification.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Analyze (javascript)

@dheeru0198 dheeru0198 requested a review from Copilot September 1, 2025 13:42
Copy link
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

This PR adds database indexes to optimize query performance for notification-related operations and entity lookups across multiple models. The changes focus on improving performance for notification filtering, entity identification, and user preference queries.

  • Added single-field indexes (db_index=True) to commonly queried fields across models
  • Implemented compound indexes for frequently used field combinations
  • Included corresponding Django migration to apply these database schema changes

Reviewed Changes

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

Show a summary per file
File Description
apps/api/plane/db/models/notification.py Added indexes for entity fields and read status to optimize notification queries
apps/api/plane/db/models/page.py Added indexes for entity tracking fields in page logs
apps/api/plane/db/models/favorite.py Added indexes for entity type and identifier lookups in user favorites
apps/api/plane/db/models/asset.py Added indexes for entity fields in file assets
apps/api/plane/db/migrations/0103_alter_fileasset_entity_identifier_and_more.py Django migration implementing all index changes

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Copy link
Contributor

@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: 0

🧹 Nitpick comments (5)
apps/api/plane/db/models/page.py (1)

117-127: Trim redundant single-column indexes; keep composites that cover them.

Postgres can satisfy queries on the leftmost column from a composite index. If you rarely filter by entity_type or entity_name alone, consider dropping those single-column indexes to cut bloat and write-amplification. Keep entity_identifier (useful standalone) and the two composites.

Example diff to de-dup:

         indexes = [
-            models.Index(fields=["entity_type"], name="pagelog_entity_type_idx"),
             models.Index(fields=["entity_identifier"], name="pagelog_entity_id_idx"),
-            models.Index(fields=["entity_name"], name="pagelog_entity_name_idx"),
             models.Index(
                 fields=["entity_type", "entity_identifier"], name="pagelog_type_id_idx"
             ),
             models.Index(
                 fields=["entity_name", "entity_identifier"], name="pagelog_name_id_idx"
             ),
         ]
apps/api/plane/db/models/asset.py (1)

79-87: Avoid overlapping indexes unless query patterns demand them.

(entity_type, entity_identifier) covers filters on entity_type alone. If standalone entity_type lookups aren’t common, drop that single-column index; keep entity_identifier and the composite.

         indexes = [
-            models.Index(fields=["entity_type"], name="asset_entity_type_idx"),
             models.Index(
                 fields=["entity_identifier"], name="asset_entity_identifier_idx"
             ),
             models.Index(
                 fields=["entity_type", "entity_identifier"], name="asset_entity_idx"
             ),
         ]
apps/api/plane/db/models/favorite.py (1)

44-52: Reduce index overlap on favorites.

Same rationale: if you don’t filter by entity_type alone frequently, the composite can serve leftmost scans. Consider removing the single-column entity_type index.

         indexes = [
-            models.Index(fields=["entity_type"], name="fav_entity_type_idx"),
             models.Index(
                 fields=["entity_identifier"], name="fav_entity_identifier_idx"
             ),
             models.Index(
                 fields=["entity_type", "entity_identifier"], name="fav_entity_idx"
             ),
         ]
apps/api/plane/db/models/notification.py (1)

42-49: Refine Notification model indexes
Align indexes with filters (read_at__isnull) and ordering (snoozed_till, -created_at) in apps/api/plane/app/views/notification/base.py:

  • Drop standalone read_at index (no global scans by read_at).
  • Replace (receiver, read_at) index with composite on (receiver, snoozed_till, -created_at) to avoid filesort.
  • Add partial index on receiver for unread queries.
     class Meta:
         indexes = [
             models.Index(fields=["entity_identifier"], name="notif_entity_identifier_idx"),
             models.Index(fields=["entity_name"],     name="notif_entity_name_idx"),
-            models.Index(fields=["read_at"],         name="notif_read_at_idx"),
-            models.Index(fields=["receiver","read_at"], name="notif_entity_idx"),
+            # drop if unused globally
+            # models.Index(fields=["read_at"], name="notif_read_at_idx"),
+            # support ORDER BY snoozed_till, -created_at per-view
+            models.Index(
+                fields=["receiver", "snoozed_till", "-created_at"],
+                name="notif_receiver_snoozed_created_idx",
+            ),
+            # speed up unread counts/filters by receiver
+            models.Index(
+                fields=["receiver"],
+                name="notif_unread_receiver_idx",
+                condition=models.Q(read_at__isnull=True),
+            ),
         ]
apps/api/plane/db/migrations/0103_fileasset_asset_entity_type_idx_and_more.py (1)

14-75: Keep migrations in sync, rename indexes for clarity and drop redundant ops; manually verify no naming collisions.

  • Confirm model Meta now exactly defines these indexes to prevent automatic “AddIndex” migrations—this matches the current migration.
  • Rename notif_entity_idx to notif_receiver_read_at_idx; if adopting the 3-column or partial indexes below, update this migration to match.
  • Remove any AddIndexConcurrently for single-column indexes you drop from the models (e.g., the standalone notif_read_at_idx).

Example diffs (if adopting notification enhancements):

-        AddIndexConcurrently(
-            model_name='notification',
-            index=models.Index(fields=['receiver', 'read_at'], name='notif_entity_idx'),
-        ),
+        AddIndexConcurrently(
+            model_name='notification',
+            index=models.Index(fields=['receiver', 'read_at', '-created_at'], name='notif_receiver_read_at_created_idx'),
+        ),
+        AddIndexConcurrently(
+            model_name='notification',
+            index=models.Index(fields=['receiver'], name='notif_unread_receiver_idx', condition=models.Q(read_at__isnull=True)),
+        ),
-        AddIndexConcurrently(
-            model_name='notification',
-            index=models.Index(fields=['read_at'], name='notif_read_at_idx'),
-        ),

Manually confirm no existing DB indexes use these names: notif_receiver_read_at_created_idx, notif_unread_receiver_idx, notif_entity_idx, notif_read_at_idx.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3b4e387 and 37fc962.

📒 Files selected for processing (5)
  • apps/api/plane/db/migrations/0103_fileasset_asset_entity_type_idx_and_more.py (1 hunks)
  • apps/api/plane/db/models/asset.py (1 hunks)
  • apps/api/plane/db/models/favorite.py (1 hunks)
  • apps/api/plane/db/models/notification.py (1 hunks)
  • apps/api/plane/db/models/page.py (2 hunks)
🔇 Additional comments (2)
apps/api/plane/db/models/page.py (1)

104-106: No functional change in field; LGTM.

Formatting-only update; null/blank semantics unchanged.

apps/api/plane/db/migrations/0103_fileasset_asset_entity_type_idx_and_more.py (1)

8-8: atomic=False is correct for concurrent index creation.

Prevents “CREATE INDEX CONCURRENTLY” from running inside a transaction.

@sriramveeraghanta sriramveeraghanta merged commit 2d31b56 into preview Sep 2, 2025
6 of 8 checks passed
@sriramveeraghanta sriramveeraghanta deleted the indexing-notification-fields branch September 2, 2025 08:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants