discourse/app/controllers/admin/config/flags_controller.rb
Alan Guo Xiang Tan 684569c30e
PERF: Avoid slow preloading in SiteSerializer (#33283)
`SiteSerializer#post_action_types` and `SiteSerializer#topic_flag_types`
both call the `Flag.used_flag_ids` method which executes queries that
becomes very slow on sites with alot of records in either the
`post_actions` or `reviewable_scores` table.

On the `post_actions` table, we execute `SELECT DISTINCT
post_action_type_id FROM post_actions`. On the `reviewable_scores`
table, we execute `SELECT DISTINCT reviewable_scope_type FROM
reviewable_scopes`. The problem with both queries is that it requires
the PG planner to scan through every single row in those tables.

For our use case, all we actually need is to check if a flag is being
referenced by a record in either the `post_actions` or
`reviewable_scores` tables. This commit updates the `Flag.used_flag_ids`
to accept a `flag_ids`
argument and use the argument to check whether the flag ids are
referenced in the `post_action_type_id` or `reviewable_scope_type`
foreign keys.
2025-06-25 15:03:34 +08:00

92 lines
3.2 KiB
Ruby

# frozen_string_literal: true
class Admin::Config::FlagsController < Admin::AdminController
def toggle
Flags::ToggleFlag.call(service_params) do
on_success do
Discourse.request_refresh!
render(json: success_json)
end
on_failure { render(json: failed_json, status: 422) }
on_model_not_found(:message) { raise Discourse::NotFound }
on_failed_policy(:invalid_access) { raise Discourse::InvalidAccess }
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end
end
def index
end
def new
end
def edit
end
def create
Flags::CreateFlag.call(service_params) do
on_success do |flag:|
Discourse.request_refresh!
render json: flag, serializer: FlagSerializer
end
on_failure { render(json: failed_json, status: 422) }
on_failed_policy(:invalid_access) { raise Discourse::InvalidAccess }
on_failed_policy(:unique_name) { render_json_error(I18n.t("flags.errors.unique_name")) }
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end
end
def update
Flags::UpdateFlag.call(service_params) do
on_success do |flag:|
Discourse.request_refresh!
render json: flag, serializer: FlagSerializer
end
on_failure { render(json: failed_json, status: 422) }
on_model_not_found(:message) { raise Discourse::NotFound }
on_failed_policy(:not_system) { render_json_error(I18n.t("flags.errors.system")) }
on_failed_policy(:not_used) { render_json_error(I18n.t("flags.errors.used")) }
on_failed_policy(:invalid_access) { raise Discourse::InvalidAccess }
on_failed_policy(:unique_name) { render_json_error(I18n.t("flags.errors.unique_name")) }
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end
end
def reorder
Flags::ReorderFlag.call(service_params) do
on_success do
Discourse.request_refresh!
render(json: success_json)
end
on_failure { render(json: failed_json, status: 422) }
on_model_not_found(:message) { raise Discourse::NotFound }
on_failed_policy(:invalid_access) { raise Discourse::InvalidAccess }
on_failed_policy(:invalid_move) { render_json_error(I18n.t("flags.errors.wrong_move")) }
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end
end
def destroy
Flags::DestroyFlag.call(service_params) do
on_success do
Discourse.request_refresh!
render(json: success_json)
end
on_failure { render(json: failed_json, status: 422) }
on_failed_policy(:not_system) { render_json_error(I18n.t("flags.errors.system")) }
on_failed_policy(:not_used) { render_json_error(I18n.t("flags.errors.used")) }
on_failed_policy(:invalid_access) { raise Discourse::InvalidAccess }
on_failed_contract do |contract|
render(json: failed_json.merge(errors: contract.errors.full_messages), status: 400)
end
end
end
end