perf: load widget request context in fewer queries
Every widget request loaded the web widget, its inbox, the account, the contact inbox and the contact with one query each. The widget endpoints run about 230M times a week in production, and widget/contacts#show runs these five queries and nothing else. The web widget, inbox and account now come from one joined query, and the contact inbox and contact from another. The inbox members view also called find_by on account_users for each member, which queries every time. Production averaged 8.5 of those per request. The controller now preloads account_users and the view picks the row in Ruby. A replay of one average production minute of widget GET traffic, 21,138 requests, went from 141,129 queries to 67,995 locally, with identical responses.
This commit is contained in:
@@ -2,6 +2,6 @@ class Api::V1::Widget::InboxMembersController < Api::V1::Widget::BaseController
|
||||
skip_before_action :set_contact
|
||||
|
||||
def index
|
||||
@inbox_members = @web_widget.inbox.inbox_members.includes(user: { avatar_attachment: :blob })
|
||||
@inbox_members = @web_widget.inbox.inbox_members.includes(user: [:account_users, { avatar_attachment: :blob }])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,14 +4,14 @@ module WebsiteTokenHelper
|
||||
end
|
||||
|
||||
def set_web_widget
|
||||
@web_widget = ::Channel::WebWidget.find_by!(website_token: permitted_params[:website_token])
|
||||
@web_widget = ::Channel::WebWidget.eager_load(inbox: :account).find_by!(website_token: permitted_params[:website_token])
|
||||
@current_account = @web_widget.inbox.account
|
||||
|
||||
render json: { error: 'Account is suspended' }, status: :unauthorized unless @current_account.active?
|
||||
end
|
||||
|
||||
def set_contact
|
||||
@contact_inbox = @web_widget.inbox.contact_inboxes.find_by(
|
||||
@contact_inbox = @web_widget.inbox.contact_inboxes.eager_load(:contact).find_by(
|
||||
source_id: auth_token_params[:source_id]
|
||||
)
|
||||
@contact = @contact_inbox&.contact
|
||||
|
||||
@@ -5,6 +5,8 @@ json.payload do
|
||||
json.id inbox_member.user.id
|
||||
json.name inbox_member.user.available_name
|
||||
json.avatar_url inbox_member.user.avatar_url
|
||||
json.availability_status inbox_member.user.account_users.find_by(account_id: @current_account.id)&.availability_status
|
||||
# account_users is preloaded; find_by would query once per member
|
||||
account_user = inbox_member.user.account_users.find { |record| record.account_id == @current_account.id }
|
||||
json.availability_status account_user&.availability_status
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user