PERF: speed up about page render time and limit category mods (#8112)

* PERF: speed up about page render time and limit category mods

* Remove return

* Remove widgets

* Convert admins and mods lists

* Rename component

* Apply Joffrey's patch

Co-authored-by: Joffrey JAFFEUX <j.jaffeux@gmail.com>

* Make limit 100
This commit is contained in:
Osama Sayegh 2019-10-03 21:48:56 +03:00 committed by GitHub
parent d6b39dc01d
commit e27f332318
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 99 additions and 23 deletions

View File

@ -0,0 +1,24 @@
import { userPath } from "discourse/lib/url";
import { formatUsername, escapeExpression } from "discourse/lib/utilities";
import { normalize } from "discourse/components/user-info";
import { renderAvatar } from "discourse/helpers/user-avatar";
export default Ember.Component.extend({
usersTemplates: Ember.computed("users.[]", function() {
return (this.users || []).map(user => {
let name = "";
if (user.name && normalize(user.username) !== normalize(user.name)) {
name = user.name;
}
return {
username: user.username,
name,
userPath: userPath(user.username),
avatar: renderAvatar(user, { imageSize: "large" }),
title: escapeExpression(user.title || ""),
formatedUsername: formatUsername(user.username)
};
});
})
});

View File

@ -1,7 +1,7 @@
import computed from "ember-addons/ember-computed-decorators"; import computed from "ember-addons/ember-computed-decorators";
import { userPath } from "discourse/lib/url"; import { userPath } from "discourse/lib/url";
function normalize(name) { export function normalize(name) {
return name.replace(/[\-\_ \.]/g, "").toLowerCase(); return name.replace(/[\-\_ \.]/g, "").toLowerCase();
} }

View File

@ -28,9 +28,7 @@
<section class='about admins'> <section class='about admins'>
<h3>{{d-icon "users"}} {{i18n 'about.our_admins'}}</h3> <h3>{{d-icon "users"}} {{i18n 'about.our_admins'}}</h3>
{{#each model.admins as |a|}} {{about-page-users users=model.admins}}
{{user-info user=a}}
{{/each}}
<div class='clearfix'></div> <div class='clearfix'></div>
</section> </section>
@ -45,9 +43,7 @@
<h3>{{d-icon "users"}} {{i18n 'about.our_moderators'}}</h3> <h3>{{d-icon "users"}} {{i18n 'about.our_moderators'}}</h3>
<div class='users'> <div class='users'>
{{#each model.moderators as |m|}} {{about-page-users users=model.moderators}}
{{user-info user=m}}
{{/each}}
</div> </div>
<div class='clearfix'></div> <div class='clearfix'></div>
</section> </section>
@ -62,9 +58,7 @@
<section class='about category-moderators moderators-{{cm.category.slug}}'> <section class='about category-moderators moderators-{{cm.category.slug}}'>
<h3>{{category-link cm.category}}{{i18n "about.moderators"}}</h3> <h3>{{category-link cm.category}}{{i18n "about.moderators"}}</h3>
<div class='users'> <div class='users'>
{{#each cm.moderators as |m|}} {{about-page-users users=cm.moderators}}
{{user-info user=m}}
{{/each}}
</div> </div>
<div class='clearfix'></div> <div class='clearfix'></div>
</section> </section>

View File

@ -0,0 +1,22 @@
{{#each usersTemplates as |userTemplate|}}
<div data-username="{{userTemplate.username}}" class="user-info small">
<div class="user-image">
<div class="user-image-inner">
<a href="{{userTemplate.userPath}}" data-user-card="{{userTemplate.username}}">
{{{userTemplate.avatar}}}
</a>
</div>
</div>
<div class="user-detail">
<div class="name-line">
<span class="username">
<a href="{{userTemplate.userPath}}" data-user-card="{{userTemplate.username}}">
{{userTemplate.username}}
</a>
</span>
<span class="name">{{userTemplate.name}}</span>
</div>
<div class="title">{{userTemplate.title}}</div>
</div>
</div>
{{/each}}

View File

@ -81,18 +81,31 @@ class About
end end
def category_moderators def category_moderators
category_ids = Guardian.new(@user).allowed_category_ids allowed_cats = Guardian.new(@user).allowed_category_ids
return [] if allowed_cats.blank?
cats_with_mods = Category.where.not(reviewable_by_group_id: nil).pluck(:id)
category_ids = cats_with_mods & allowed_cats
return [] if category_ids.blank? return [] if category_ids.blank?
results = DB.query(<<~SQL, category_ids: category_ids)
SELECT c.id category_id, array_agg(gu.user_id) user_ids per_cat_limit = category_mods_limit / category_ids.size
per_cat_limit = 1 if per_cat_limit < 1
results = DB.query(<<~SQL, category_ids: category_ids, per_cat_limit: per_cat_limit)
SELECT c.id category_id, user_ids
FROM categories c FROM categories c
JOIN group_users gu CROSS JOIN LATERAL (
ON gu.group_id = reviewable_by_group_id SELECT ARRAY(
SELECT u.id
FROM users u
JOIN group_users gu
ON gu.group_id = c.reviewable_by_group_id AND gu.user_id = u.id
ORDER BY last_seen_at DESC
LIMIT :per_cat_limit
) AS user_ids
) user_ids
WHERE c.id IN (:category_ids) WHERE c.id IN (:category_ids)
GROUP BY c.id
SQL SQL
moderators = {} moderators = {}
User.where(id: results.map(&:user_ids).flatten).each do |user| User.where(id: results.map(&:user_ids).flatten.uniq).each do |user|
moderators[user.id] = user moderators[user.id] = user
end end
moderators moderators
@ -100,4 +113,12 @@ class About
CategoryMods.new(row.category_id, row.user_ids.map { |id| moderators[id] }) CategoryMods.new(row.category_id, row.user_ids.map { |id| moderators[id] })
end end
end end
def category_mods_limit
@category_mods_limit || 100
end
def category_mods_limit=(number)
@category_mods_limit = number
end
end end

View File

@ -10,14 +10,16 @@ describe About do
describe "#category_moderators" do describe "#category_moderators" do
let(:user) { Fabricate(:user) } let(:user) { Fabricate(:user) }
let(:public_cat_moderator) { Fabricate(:user) } let(:public_cat_moderator) { Fabricate(:user, last_seen_at: 1.month.ago) }
let(:private_cat_moderator) { Fabricate(:user) } let(:private_cat_moderator) { Fabricate(:user, last_seen_at: 2.month.ago) }
let(:common_moderator) { Fabricate(:user) } let(:common_moderator) { Fabricate(:user, last_seen_at: 3.month.ago) }
let(:common_moderator_2) { Fabricate(:user, last_seen_at: 4.month.ago) }
let(:public_group) do let(:public_group) do
group = Fabricate(:public_group) group = Fabricate(:public_group)
group.add(public_cat_moderator) group.add(public_cat_moderator)
group.add(common_moderator) group.add(common_moderator)
group.add(common_moderator_2)
group group
end end
@ -25,6 +27,7 @@ describe About do
group = Fabricate(:group) group = Fabricate(:group)
group.add(private_cat_moderator) group.add(private_cat_moderator)
group.add(common_moderator) group.add(common_moderator)
group.add(common_moderator_2)
group group
end end
@ -37,16 +40,28 @@ describe About do
expect(results.map(&:moderators).flatten.map(&:id).uniq).to contain_exactly( expect(results.map(&:moderators).flatten.map(&:id).uniq).to contain_exactly(
public_cat_moderator.id, public_cat_moderator.id,
common_moderator.id, common_moderator.id,
common_moderator_2.id,
private_cat_moderator.id private_cat_moderator.id
) )
[public_cat_moderator, user, nil].each do |u| [public_cat_moderator, user, nil].each do |u|
results = About.new(u).category_moderators results = About.new(u).category_moderators
expect(results.map(&:category_id)).to contain_exactly(public_cat.id) expect(results.map(&:category_id)).to contain_exactly(public_cat.id)
expect(results.map(&:moderators).flatten.map(&:id)).to contain_exactly( expect(results.map(&:moderators).flatten.map(&:id)).to eq([
public_cat_moderator.id, public_cat_moderator.id,
common_moderator.id common_moderator.id,
) common_moderator_2.id
])
end
end
it "limit category moderators when there are too many for perf reasons" do
about = About.new(private_cat_moderator)
about.category_mods_limit = 4
results = about.category_moderators
expect(results.size).to eq(2)
results.each do |res|
expect(res.moderators.size).to eq(2)
end end
end end
end end