From ff3f40a675c1dcbf17e121b0d09ce65ea441d8a5 Mon Sep 17 00:00:00 2001 From: Claire Date: Thu, 1 Jun 2023 02:41:51 +0200 Subject: [PATCH] Fix multiple N+1s in ConversationsController (#25134) --- .../api/v1/conversations_controller.rb | 17 +++++++- app/models/account_conversation.rb | 41 +++++++++++++------ 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/app/controllers/api/v1/conversations_controller.rb b/app/controllers/api/v1/conversations_controller.rb index 6c7583403..afc115786 100644 --- a/app/controllers/api/v1/conversations_controller.rb +++ b/app/controllers/api/v1/conversations_controller.rb @@ -11,7 +11,7 @@ class Api::V1::ConversationsController < Api::BaseController def index @conversations = paginated_conversations - render json: @conversations, each_serializer: REST::ConversationSerializer + render json: @conversations, each_serializer: REST::ConversationSerializer, relationships: StatusRelationshipsPresenter.new(@conversations.map(&:last_status), current_user&.account_id) end def read @@ -32,7 +32,20 @@ class Api::V1::ConversationsController < Api::BaseController def paginated_conversations AccountConversation.where(account: current_account) - .to_a_paginated_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id)) + .includes( + account: :account_stat, + last_status: [ + :media_attachments, + :preview_cards, + :status_stat, + :tags, + { + active_mentions: [account: :account_stat], + account: :account_stat, + }, + ] + ) + .to_a_paginated_by_id(limit_param(LIMIT), **params_slice(:max_id, :since_id, :min_id)) end def insert_pagination_headers diff --git a/app/models/account_conversation.rb b/app/models/account_conversation.rb index 45e74bbeb..6b1d24604 100644 --- a/app/models/account_conversation.rb +++ b/app/models/account_conversation.rb @@ -16,34 +16,51 @@ class AccountConversation < ApplicationRecord include Redisable + attr_writer :participant_accounts + + before_validation :set_last_status after_commit :push_to_streaming_api belongs_to :account belongs_to :conversation belongs_to :last_status, class_name: 'Status' - before_validation :set_last_status - def participant_account_ids=(arr) self[:participant_account_ids] = arr.sort + @participant_accounts = nil end def participant_accounts - if participant_account_ids.empty? - [account] - else - participants = Account.where(id: participant_account_ids) - participants.empty? ? [account] : participants + @participant_accounts ||= begin + if participant_account_ids.empty? + [account] + else + participants = Account.where(id: participant_account_ids).to_a + participants.empty? ? [account] : participants + end end end class << self - def to_a_paginated_by_id(limit, options = {}) - if options[:min_id] - paginate_by_min_id(limit, options[:min_id], options[:max_id]).reverse - else - paginate_by_max_id(limit, options[:max_id], options[:since_id]).to_a + def to_a_paginated_by_id(limit, min_id: nil, max_id: nil, since_id: nil, preload_participants: true) + array = begin + if min_id + paginate_by_min_id(limit, min_id, max_id).reverse + else + paginate_by_max_id(limit, max_id, since_id).to_a + end end + + if preload_participants + participant_ids = array.flat_map(&:participant_account_ids) + accounts_by_id = Account.where(id: participant_ids).index_by(&:id) + + array.each do |conversation| + conversation.participant_accounts = conversation.participant_account_ids.filter_map { |id| accounts_by_id[id] } + end + end + + array end def paginate_by_min_id(limit, min_id = nil, max_id = nil)