Browse Source
Fix performance on instances list in admin UI (#15282)
Fix performance on instances list in admin UI (#15282)
- Reduce duplicate queries - Remove n+1 queries - Add accounts count to detailed view - Add separate action log entry for updating existing domain blocksfeature_cybrespace_locale

committed by
GitHub

No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 326 additions and 166 deletions
-
1Gemfile
-
4Gemfile.lock
-
5app/controllers/admin/domain_blocks_controller.rb
-
44app/controllers/admin/instances_controller.rb
-
2app/controllers/api/v1/instances/peers_controller.rb
-
6app/models/account.rb
-
13app/models/concerns/domain_materializable.rb
-
1app/models/domain_allow.rb
-
1app/models/domain_block.rb
-
63app/models/instance.rb
-
31app/models/instance_filter.rb
-
2app/models/unavailable_domain.rb
-
4app/policies/domain_block_policy.rb
-
2app/presenters/instance_presenter.rb
-
25app/views/admin/instances/_instance.html.haml
-
36app/views/admin/instances/index.html.haml
-
50app/views/admin/instances/show.html.haml
-
11app/workers/scheduler/instance_refresh_scheduler.rb
-
101config/brakeman.ignore
-
3config/locales/en.yml
-
3config/sidekiq.yml
-
9db/migrate/20201206004238_create_instances.rb
-
27db/schema.rb
-
17db/views/instances_v01.sql
-
4lib/mastodon/domains_cli.rb
-
6spec/controllers/admin/instances_controller_spec.rb
-
21spec/models/account_spec.rb
@ -0,0 +1,13 @@ |
|||
# frozen_string_literal: true |
|||
|
|||
module DomainMaterializable |
|||
extend ActiveSupport::Concern |
|||
|
|||
included do |
|||
after_create_commit :refresh_instances_view |
|||
end |
|||
|
|||
def refresh_instances_view |
|||
Instance.refresh unless domain.nil? || Instance.where(domain: domain).exists? |
|||
end |
|||
end |
@ -1,26 +1,63 @@ |
|||
# frozen_string_literal: true |
|||
# == Schema Information |
|||
# |
|||
# Table name: instances |
|||
# |
|||
# domain :string primary key |
|||
# accounts_count :bigint(8) |
|||
# |
|||
|
|||
class Instance |
|||
include ActiveModel::Model |
|||
class Instance < ApplicationRecord |
|||
self.primary_key = :domain |
|||
|
|||
attr_accessor :domain, :accounts_count, :domain_block |
|||
has_many :accounts, foreign_key: :domain, primary_key: :domain |
|||
|
|||
def initialize(resource) |
|||
@domain = resource.domain |
|||
@accounts_count = resource.respond_to?(:accounts_count) ? resource.accounts_count : nil |
|||
@domain_block = resource.is_a?(DomainBlock) ? resource : DomainBlock.rule_for(domain) |
|||
@domain_allow = resource.is_a?(DomainAllow) ? resource : DomainAllow.rule_for(domain) |
|||
belongs_to :domain_block, foreign_key: :domain, primary_key: :domain |
|||
belongs_to :domain_allow, foreign_key: :domain, primary_key: :domain |
|||
|
|||
scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) } |
|||
|
|||
def self.refresh |
|||
Scenic.database.refresh_materialized_view(table_name, concurrently: true, cascade: false) |
|||
end |
|||
|
|||
def countable? |
|||
@accounts_count.present? |
|||
def readonly? |
|||
true |
|||
end |
|||
|
|||
def to_param |
|||
domain |
|||
def delivery_failure_tracker |
|||
@delivery_failure_tracker ||= DeliveryFailureTracker.new(domain) |
|||
end |
|||
|
|||
def following_count |
|||
@following_count ||= Follow.where(account: accounts).count |
|||
end |
|||
|
|||
def followers_count |
|||
@followers_count ||= Follow.where(target_account: accounts).count |
|||
end |
|||
|
|||
def reports_count |
|||
@reports_count ||= Report.where(target_account: accounts).count |
|||
end |
|||
|
|||
def cache_key |
|||
def blocks_count |
|||
@blocks_count ||= Block.where(target_account: accounts).count |
|||
end |
|||
|
|||
def public_comment |
|||
domain_block&.public_comment |
|||
end |
|||
|
|||
def private_comment |
|||
domain_block&.private_comment |
|||
end |
|||
|
|||
def media_storage |
|||
@media_storage ||= MediaAttachment.where(account: accounts).sum(:file_file_size) |
|||
end |
|||
|
|||
def to_param |
|||
domain |
|||
end |
|||
end |
@ -0,0 +1,25 @@ |
|||
.directory__tag |
|||
= link_to admin_instance_path(instance) do |
|||
%h4 |
|||
= instance.domain |
|||
%small |
|||
- if instance.domain_block |
|||
- first_item = true |
|||
- if !instance.domain_block.noop? |
|||
= t("admin.domain_blocks.severity.#{instance.domain_block.severity}") |
|||
- first_item = false |
|||
- unless instance.domain_block.suspend? |
|||
- if instance.domain_block.reject_media? |
|||
- unless first_item |
|||
• |
|||
= t('admin.domain_blocks.rejecting_media') |
|||
- first_item = false |
|||
- if instance.domain_block.reject_reports? |
|||
- unless first_item |
|||
• |
|||
= t('admin.domain_blocks.rejecting_reports') |
|||
- elsif whitelist_mode? |
|||
= t('admin.accounts.whitelisted') |
|||
- else |
|||
= t('admin.accounts.no_limits_imposed') |
|||
.trends__item__current{ title: t('admin.instances.known_accounts', count: instance.accounts_count) }= number_to_human instance.accounts_count, strip_insignificant_zeros: true |
@ -0,0 +1,11 @@ |
|||
# frozen_string_literal: true |
|||
|
|||
class Scheduler::InstanceRefreshScheduler |
|||
include Sidekiq::Worker |
|||
|
|||
sidekiq_options lock: :until_executed, retry: 0 |
|||
|
|||
def perform |
|||
Instance.refresh |
|||
end |
|||
end |
@ -0,0 +1,9 @@ |
|||
class CreateInstances < ActiveRecord::Migration[5.2] |
|||
def change |
|||
create_view :instances, materialized: true |
|||
|
|||
# To be able to refresh the view concurrently, |
|||
# at least one unique index is required |
|||
safety_assured { add_index :instances, :domain, unique: true } |
|||
end |
|||
end |
@ -0,0 +1,17 @@ |
|||
WITH domain_counts(domain, accounts_count) |
|||
AS ( |
|||
SELECT domain, COUNT(*) as accounts_count |
|||
FROM accounts |
|||
WHERE domain IS NOT NULL |
|||
GROUP BY domain |
|||
) |
|||
SELECT domain, accounts_count |
|||
FROM domain_counts |
|||
UNION |
|||
SELECT domain_blocks.domain, COALESCE(domain_counts.accounts_count, 0) |
|||
FROM domain_blocks |
|||
LEFT OUTER JOIN domain_counts ON domain_counts.domain = domain_blocks.domain |
|||
UNION |
|||
SELECT domain_allows.domain, COALESCE(domain_counts.accounts_count, 0) |
|||
FROM domain_allows |
|||
LEFT OUTER JOIN domain_counts ON domain_counts.domain = domain_allows.domain |
Write
Preview
Loading…
Cancel
Save
Reference in new issue