Skip to content

Commit 171a28c

Browse files
authored
PERF: Optimize reviewable queries by eager loading (#36618)
Improved query performance by eagerly loading user_stat, primary_email, and topic/category associations when fetching the target. This prevents N+1 queries when accessing these commonly-needed associations in the reviewables list. In addition, running count is breaking eager_loading and causing N+1. Using goldiload to prevent it. Before <img width="846" height="169" alt="Screenshot 2025-12-11 at 11 30 09 am" src="https://github.com/user-attachments/assets/6053ca8f-edd6-4488-8db2-7ec196fe1196" /> After <img width="843" height="170" alt="Screenshot 2025-12-11 at 2 21 02 pm" src="https://github.com/user-attachments/assets/1d0f414c-8872-4c9f-9f32-080fd8a7b67c" />
1 parent 5a8a035 commit 171a28c

File tree

4 files changed

+39
-11
lines changed

4 files changed

+39
-11
lines changed

app/models/reviewable.rb

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -462,8 +462,16 @@ def self.viewable_by(user, order: nil, preload: true)
462462
.includes(
463463
{ created_by: :user_stat },
464464
:topic,
465-
:target,
466-
:target_created_by,
465+
{
466+
target: [
467+
:user_stat,
468+
:primary_email,
469+
{ topic: :category },
470+
:user_histories,
471+
:user_custom_fields,
472+
],
473+
},
474+
{ target_created_by: [:user_custom_fields] },
467475
:reviewable_histories,
468476
)
469477
.includes(reviewable_scores: { user: :user_stat, meta_topic: :posts })

app/models/user.rb

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ class User < ActiveRecord::Base
9292
has_many :do_not_disturb_timings, dependent: :delete_all
9393
has_many :reviewable_histories, foreign_key: :created_by_id, dependent: :delete_all
9494
has_many :sidebar_sections, dependent: :destroy
95+
has_many :user_histories, foreign_key: :target_user_id
9596
has_one :user_status, dependent: :destroy
9697

9798
# dependent deleting handled via before_destroy (special cases)
@@ -1329,7 +1330,7 @@ def silenced?
13291330
end
13301331

13311332
def silenced_record
1332-
UserHistory.for(self, :silence_user).order("id DESC").first
1333+
user_histories.where(action: UserHistory.actions[:silence_user]).order("id DESC").first
13331334
end
13341335

13351336
def silence_reason
@@ -1345,7 +1346,7 @@ def silenced_forever?
13451346
end
13461347

13471348
def suspend_record
1348-
UserHistory.for(self, :suspend_user).order("id DESC").first
1349+
user_histories.where(action: UserHistory.actions[:suspend_user]).order("id DESC").first
13491350
end
13501351

13511352
def full_suspend_reason
@@ -1638,7 +1639,12 @@ def number_of_flagged_posts
16381639
end
16391640

16401641
def number_of_rejected_posts
1641-
ReviewableQueuedPost.rejected.where(target_created_by_id: self.id).count
1642+
goldiload do |ids|
1643+
ReviewableQueuedPost
1644+
.where(status: "rejected", target_created_by_id: ids)
1645+
.group(:target_created_by_id)
1646+
.count
1647+
end
16421648
end
16431649

16441650
def number_of_flags_given
@@ -1650,11 +1656,21 @@ def number_of_flags_given
16501656
end
16511657

16521658
def number_of_silencings
1653-
UserHistory.for(self, :silence_user).count
1659+
goldiload do |ids|
1660+
UserHistory
1661+
.where(target_user_id: ids, action: UserHistory.actions[:silence_user])
1662+
.group(:target_user_id)
1663+
.count
1664+
end
16541665
end
16551666

16561667
def number_of_suspensions
1657-
UserHistory.for(self, :suspend_user).count
1668+
goldiload do |ids|
1669+
UserHistory
1670+
.where(target_user_id: ids, action: UserHistory.actions[:suspend_user])
1671+
.group(:target_user_id)
1672+
.count
1673+
end
16581674
end
16591675

16601676
def create_user_profile

app/serializers/flagged_user_serializer.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,15 @@ def flags_ignored
4444
end
4545

4646
def silenced_count
47-
object.number_of_silencings
47+
object.number_of_silencings.to_i
4848
end
4949

5050
def suspended_count
51-
object.number_of_suspensions
51+
object.number_of_suspensions.to_i
5252
end
5353

5454
def rejected_posts_count
55-
object.number_of_rejected_posts
55+
object.number_of_rejected_posts.to_i
5656
end
5757

5858
def custom_fields

spec/models/user_spec.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2134,7 +2134,7 @@ def hash(password, salt, algorithm = UserPassword::TARGET_PASSWORD_ALGORITHM)
21342134
status: Reviewable.statuses[:approved],
21352135
)
21362136

2137-
expect(user.number_of_rejected_posts).to eq(0)
2137+
expect(user.number_of_rejected_posts.to_i).to eq(0)
21382138
end
21392139
end
21402140

@@ -2158,6 +2158,10 @@ def hash(password, salt, algorithm = UserPassword::TARGET_PASSWORD_ALGORITHM)
21582158
Fabricate(:user_history, action: UserHistory.actions[:silence_user], target_user: user)
21592159
end
21602160
expect(user.number_of_silencings).to eq(3)
2161+
2162+
# ignores other users' history
2163+
Fabricate(:user_history, action: UserHistory.actions[:silence_user])
2164+
expect(user.reload.number_of_silencings).to eq(3)
21612165
end
21622166
end
21632167
end

0 commit comments

Comments
 (0)