feat(perf): Cache labels on the conversation model (#8527)

This commit is contained in:
Pranav Raj S
2023-12-11 18:27:55 -08:00
committed by GitHub
parent 79412ba2c6
commit 890515edfd
9 changed files with 48 additions and 53 deletions

View File

@@ -14,7 +14,8 @@ class Api::V1::Accounts::Conversations::AssignmentsController < Api::V1::Account
def set_agent
@agent = Current.account.users.find_by(id: params[:assignee_id])
@conversation.update_assignee(@agent)
@conversation.assignee = @agent
@conversation.save!
render_agent
end

View File

@@ -110,8 +110,8 @@ class Api::V1::Accounts::ConversationsController < Api::V1::Accounts::BaseContro
end
def assign_conversation
@agent = Current.account.users.find(current_user.id)
@conversation.update_assignee(@agent)
@conversation.assignee = current_user
@conversation.save!
end
def conversation

View File

@@ -0,0 +1,16 @@
class Migration::ConversationCacheLabelJob < ApplicationJob
queue_as :async_database_migration
# To cache the label, we simply access it from the object and save it. Anytime the object is
# saved in the future, ActsAsTaggable will automatically recompute it. This process is done
# initially when the user has not performed any action.
# Reference: https://github.com/mbleigh/acts-as-taggable-on/wiki/Caching
def perform(account)
account.conversations.find_in_batches do |conversation_batch|
conversation_batch.each do |conversation|
conversation.label_list
conversation.save!
end
end
end
end

View File

@@ -6,6 +6,7 @@
# additional_attributes :jsonb
# agent_last_seen_at :datetime
# assignee_last_seen_at :datetime
# cached_label_list :string
# contact_last_seen_at :datetime
# custom_attributes :jsonb
# first_reply_created_at :datetime
@@ -144,10 +145,6 @@ class Conversation < ApplicationRecord
end
end
def update_assignee(agent = nil)
update!(assignee: agent)
end
def toggle_status
# FIXME: implement state machine with aasm
self.status = open? ? :resolved : :open
@@ -185,6 +182,10 @@ class Conversation < ApplicationRecord
Conversations::EventDataPresenter.new(self).push_data
end
def cached_label_list_array
(cached_label_list || '').split(',')
end
def notifiable_assignee_change?
return false unless saved_change_to_assignee_id?
return false if assignee_id.blank?

View File

@@ -1,5 +1,5 @@
json.meta do
json.labels @conversation.label_list
json.labels @conversation.cached_label_list_array
json.additional_attributes @conversation.additional_attributes
json.contact @conversation.contact.push_event_data
json.assignee @conversation.assignee.push_event_data if @conversation.assignee.present?

View File

@@ -35,7 +35,7 @@ json.can_reply conversation.can_reply?
json.contact_last_seen_at conversation.contact_last_seen_at.to_i
json.custom_attributes conversation.custom_attributes
json.inbox_id conversation.inbox_id
json.labels conversation.label_list
json.labels conversation.cached_label_list_array
json.muted conversation.muted?
json.snoozed_until conversation.snoozed_until
json.status conversation.status

View File

@@ -0,0 +1,19 @@
class AddCachedLabelsList < ActiveRecord::Migration[7.0]
def change
add_column :conversations, :cached_label_list, :string
Conversation.reset_column_information
ActsAsTaggableOn::Taggable::Cache.included(Conversation)
update_exisiting_conversations
end
private
def update_exisiting_conversations
::Account.find_in_batches do |account_batch|
account_batch.each do |account|
Migration::ConversationCacheLabelJob.perform_later(account)
end
end
end
end

View File

@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.0].define(version: 2023_12_01_014644) do
ActiveRecord::Schema[7.0].define(version: 2023_12_11_010807) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_stat_statements"
enable_extension "pg_trgm"
@@ -453,6 +453,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_12_01_014644) do
t.integer "priority"
t.bigint "sla_policy_id"
t.datetime "waiting_since"
t.string "cached_label_list"
t.index ["account_id", "display_id"], name: "index_conversations_on_account_id_and_display_id", unique: true
t.index ["account_id", "id"], name: "index_conversations_on_id_and_account_id"
t.index ["account_id", "inbox_id", "status", "assignee_id"], name: "conv_acid_inbid_stat_asgnid_idx"

View File

@@ -63,47 +63,4 @@ shared_examples_for 'assignment_handler' do
end
end
end
describe '#update_assignee' do
subject(:update_assignee) { conversation.update_assignee(agent) }
let(:conversation) { create(:conversation, assignee: nil) }
let(:agent) do
create(:user, email: 'agent@example.com', account: conversation.account, role: :agent)
end
let(:assignment_mailer) { instance_double(AgentNotifications::ConversationNotificationsMailer, deliver: true) }
before do
create(:inbox_member, user: agent, inbox: conversation.inbox)
end
it 'assigns the agent to conversation' do
expect(update_assignee).to be(true)
expect(conversation.reload.assignee).to eq(agent)
end
it 'dispaches assignee changed event' do
# TODO: FIX me
# expect(EventDispatcherJob).to(have_been_enqueued.at_least(:once).with('assignee.changed', anything, anything, anything, anything))
expect(EventDispatcherJob).to(have_been_enqueued.at_least(:once))
expect(update_assignee).to be(true)
end
it 'adds assignee to conversation participants' do
expect { update_assignee }.to change { conversation.conversation_participants.count }.by(1)
end
context 'when agent is current user' do
before do
Current.user = agent
end
it 'creates self-assigned message activity' do
expect(update_assignee).to be(true)
expect(Conversations::ActivityMessageJob).to(have_been_enqueued.at_least(:once)
.with(conversation, { account_id: conversation.account_id, inbox_id: conversation.inbox_id,
message_type: :activity, content: "#{agent.name} self-assigned this conversation" }))
end
end
end
end