Skip to content

perf(crud): eliminate N+1 queries by eagerly loading relations - #724

Merged
ImMohammad20000 merged 11 commits into
devfrom
tuned-queries
Jul 27, 2026
Merged

perf(crud): eliminate N+1 queries by eagerly loading relations#724
ImMohammad20000 merged 11 commits into
devfrom
tuned-queries

Conversation

@x0sina

@x0sina x0sina commented Jul 26, 2026

Copy link
Copy Markdown
Collaborator
  • Add selectinload for Admin.users, Admin.usage_logs, and Admin.role in get_admins to prevent sequential queries when not in compact mode
  • Add selectinload for Group.users and Group.inbounds in get_group to eagerly load relations
  • Add selectinload for Node.usage_logs in get_nodes, get_limited_nodes, and get_nodes_to_reset_usage to avoid loading attributes sequentially
  • Replace individual db.refresh() calls with single batch re-fetch queries in bulk_reset_node_usage and update_users_proxy_settings
  • Use .unique() on query results when eager loading relations to handle duplicates from joins
  • Remove sequential load_admin_attrs, load_group_attrs, and load_node_attrs calls where relations are now eagerly loaded
  • Use selectinload in _build_user_select_stmt to eagerly load all user relations (admin, next_plan, usage_logs, groups)
  • Improves query performance by reducing database round trips and eliminating N+1 query patterns across admin, group, node, and user CRUD operations

Summary

Type of change

  • Bug fix
  • New feature
  • Breaking change
  • Refactor / cleanup
  • Documentation
  • Tests / CI

Summary by CodeRabbit

  • Performance Improvements
    • Improved data loading across administrators, groups, nodes, and users by using eager-loading and reducing per-record database work.
    • Made bulk proxy-setting updates and node usage resets more efficient by re-fetching updated records in batches.
    • Reduced repeated database dialect lookups during usage recording.
  • Bug Fixes
    • Admin usage-limit warning reminders are now deduplicated and idempotent, with correct behavior when sending notifications fails for some administrators.
  • Database
    • Enforced uniqueness for admin notification reminders using a dedicated constraint and normalized threshold values (including non-null defaults with SQLite compatibility).
  • Tests
    • Added unit tests for reminder idempotency and notification failure handling.

- Add selectinload for Admin.users, Admin.usage_logs, and Admin.role in get_admins to prevent sequential queries when not in compact mode
- Add selectinload for Group.users and Group.inbounds in get_group to eagerly load relations
- Add selectinload for Node.usage_logs in get_nodes, get_limited_nodes, and get_nodes_to_reset_usage to avoid loading attributes sequentially
- Replace individual db.refresh() calls with single batch re-fetch queries in bulk_reset_node_usage and update_users_proxy_settings
- Use .unique() on query results when eager loading relations to handle duplicates from joins
- Remove sequential load_admin_attrs, load_group_attrs, and load_node_attrs calls where relations are now eagerly loaded
- Use selectinload in _build_user_select_stmt to eagerly load all user relations (admin, next_plan, usage_logs, groups)
- Improves query performance by reducing database round trips and eliminating N+1 query patterns across admin, group, node, and user CRUD operations
@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

CRUD queries now use eager loading and batched re-fetches. The usage-recording job caches the database dialect. Admin warning notifications now use locked, deduplicated bulk reminder creation with uniqueness enforcement, SQLite-compatible migration support, and idempotency/failure tests.

Changes

ORM and job efficiency

Layer / File(s) Summary
Eager-loaded CRUD reads
app/db/crud/admin.py, app/db/crud/group.py, app/db/crud/node.py, app/db/crud/user.py
Admin, group, node, and user queries eagerly load relationships and remove per-record attribute-loading loops.
Batched entity refreshes
app/db/crud/bulk.py, app/db/crud/node.py
Updated users and reset nodes are re-fetched in batch queries while preserving result ordering.
Cached database dialect
app/jobs/record_usages.py
The database dialect is memoized after its first lookup.
Reminder persistence contract
app/db/models.py, app/db/migrations/versions/*
Reminder thresholds become non-null with a default, duplicate records are normalized during migration, uniqueness is enforced, and SQLite-compatible primary-key handling is added.
Bulk admin reminder workflow
app/db/crud/admin.py, app/jobs/review_admins.py, tests/test_review_admins_unit.py
Reminder creation and warning notifications use deduplication, admin row locks, bulk persistence, failure handling, and idempotency tests.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ReviewAdmins
  participant AdminReminderStore
  participant NotificationService
  ReviewAdmins->>AdminReminderStore: Lock admins and query reminder records
  AdminReminderStore-->>ReviewAdmins: Return eligible admins
  ReviewAdmins->>NotificationService: Send warning notifications
  NotificationService-->>ReviewAdmins: Report success or failure
  ReviewAdmins->>AdminReminderStore: Insert successful reminders in bulk
Loading

Possibly related PRs

  • PasarGuard/panel#507: Introduces the admin reminder model and usage-limit notification workflow used by these changes.

Suggested labels: refactor

Poem

A rabbit sorts reminders, one by one,
Locks the rows beneath the morning sun.
Failed notes stay out, successes remain,
While eager hops make queries plain.
“Batched carrots!” the bunny cries—“again!”

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: CRUD queries were refactored to eagerly load relations and remove N+1 patterns.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch tuned-queries

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

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

@x0sina

x0sina commented Jul 26, 2026

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

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.

@PasarGuard PasarGuard deleted a comment from coderabbitai Bot Jul 26, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/db/crud/bulk.py`:
- Around line 448-453: Preserve input order in the refresh-and-return flow by
mapping the queried users by ID, then rebuilding the result according to
updated_user_ids before returning. Update the logic after load_user_attrs in the
relevant bulk update function, while retaining attribute loading and the
existing count return value.
- Around line 448-451: Update the refreshed-user query in the bulk proxy-setting
path to include the required relationship eager-load options, matching the
dependencies currently loaded by load_user_attrs with load_admin_role=True.
Remove the per-user load_user_attrs loop and return/use the batched, fully
populated User results instead.

In `@app/jobs/review_admins.py`:
- Around line 86-90: Move reminder persistence out of the pre-notification path
in the review-admin flow: send each notification first, then persist only the
corresponding successfully sent reminders through
bulk_create_admin_notification_reminders. Ensure failures or interruptions do
not commit reminders for unsent notifications, following the helper’s documented
post-send contract.
- Around line 73-84: The reminder creation flow around the threshold-admin loop
and its bulk helper must remain idempotent: atomically insert only missing
data-usage reminders for each admin and threshold, even with a non-unique index.
Return or track which reminders were newly inserted, and populate
notifications_to_send only for those new records so recurring job runs do not
resend notifications.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b257731f-5609-4b2d-89c6-8aa7494edbb9

📥 Commits

Reviewing files that changed from the base of the PR and between 25a928d and 8b7ea6f.

📒 Files selected for processing (7)
  • app/db/crud/admin.py
  • app/db/crud/bulk.py
  • app/db/crud/group.py
  • app/db/crud/node.py
  • app/db/crud/user.py
  • app/jobs/record_usages.py
  • app/jobs/review_admins.py

Comment thread app/db/crud/bulk.py Outdated
Comment thread app/db/crud/bulk.py Outdated
Comment thread app/jobs/review_admins.py Outdated
Comment thread app/jobs/review_admins.py Outdated
@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
Not Found - https://docs.github.com/rest/issues/comments#update-an-issue-comment

@ImMohammad20000

Copy link
Copy Markdown
Contributor

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

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.

@coderabbitai coderabbitai Bot added the enhancement New feature or request label Jul 26, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
app/db/crud/admin.py (1)

533-579: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Consider a DB-level unique constraint in addition to the application lock.

The new with_for_update() + check-then-insert pattern correctly serializes concurrent calls to this function for the same admins, closing the race a prior review flagged. However, idempotency here still rests entirely on every future reminder-creation path acquiring the same Admin row lock first — the admin_notification_reminders table itself still only has a non-unique index on (admin_id, type) (not even including threshold). A real UNIQUE constraint on (admin_id, type, threshold) would give a durable, schema-level guarantee independent of call-site discipline.

♻️ Suggested migration addition
     __table_args__ = (Index("ix_admin_notification_reminders_admin_id_type", "admin_id", "type"),)
+    __table_args__ = (
+        UniqueConstraint("admin_id", "type", "threshold", name="uq_admin_notification_reminders"),
+    )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/db/crud/admin.py` around lines 533 - 579, Add a database-level unique
constraint on admin_notification_reminders covering admin_id, type, and
threshold, and include the corresponding schema migration. Keep
bulk_create_admin_notification_reminders’ application locking and
duplicate-check logic intact, while ensuring the migration handles any existing
duplicate rows before creating the constraint.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@app/db/crud/admin.py`:
- Around line 533-579: Add a database-level unique constraint on
admin_notification_reminders covering admin_id, type, and threshold, and include
the corresponding schema migration. Keep
bulk_create_admin_notification_reminders’ application locking and
duplicate-check logic intact, while ensuring the migration handles any existing
duplicate rows before creating the constraint.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b66fe4d7-81cc-4e3d-a9f2-4c521d7a6b0f

📥 Commits

Reviewing files that changed from the base of the PR and between 8b7ea6f and 228a452.

📒 Files selected for processing (5)
  • app/db/crud/admin.py
  • app/db/crud/bulk.py
  • app/db/migrations/versions/bb4a32b7f5ce_add_admin_notification_reminders.py
  • app/jobs/review_admins.py
  • tests/test_review_admins_unit.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/db/crud/bulk.py

@ImMohammad20000

Copy link
Copy Markdown
Contributor

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/db/models.py`:
- Around line 752-755: The nullable AdminNotificationReminder.threshold does not
match the uniqueness expected by create_admin_notification_reminder_if_absent().
Update app/db/models.py lines 752-755 and
app/db/migrations/versions/fb32155473c1_add_unique_constraint_to_admin_.py lines
35-38 consistently: either backfill/remove NULL reminders and make threshold
non-null before applying the unique constraint, or implement backend-specific
nullable-unique behavior so repeated NULL thresholds are handled correctly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 7e165dca-a1c2-4b9c-89cc-d70bed2316b0

📥 Commits

Reviewing files that changed from the base of the PR and between 228a452 and ed56b29.

📒 Files selected for processing (3)
  • app/db/migrations/versions/fb32155473c1_add_unique_constraint_to_admin_.py
  • app/db/models.py
  • tests/test_review_admins_unit.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/test_review_admins_unit.py

Comment thread app/db/models.py
@ImMohammad20000

Copy link
Copy Markdown
Contributor

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

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.

@coderabbitai coderabbitai Bot removed the enhancement New feature or request label Jul 27, 2026
@ImMohammad20000
ImMohammad20000 merged commit 615c4d3 into dev Jul 27, 2026
20 checks passed
@ImMohammad20000
ImMohammad20000 deleted the tuned-queries branch July 27, 2026 10:31
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.

2 participants