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:
Shivam Mishra
2026-10-06 16:00:35 +05:30
parent ed7b122d27
commit b8b1d9184b
3 changed files with 6 additions and 4 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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