Fixes #3299 - Having more active Agents using the Chat decreases performance linearly.

This commit is contained in:
Thorsten Eckel 2020-11-27 14:08:30 +01:00
parent aaa30b2b90
commit dccd4aa012
5 changed files with 166 additions and 8 deletions

View file

@ -20,10 +20,16 @@ class Chat::Agent < ApplicationModel
return chat_agent.active return chat_agent.active
end end
# ATTENTION: setter return value indicates whether `active` state has changed
if chat_agent if chat_agent
chat_agent.active = state chat_agent.active = state
# always update `updated_at` to inform other Agent sessions
# that this Agent session is still active
chat_agent.updated_at = Time.zone.now chat_agent.updated_at = Time.zone.now
chat_agent.save chat_agent.save
chat_agent.active_previously_changed?
else else
Chat::Agent.create( Chat::Agent.create(
active: state, active: state,

View file

@ -23,14 +23,7 @@ return is sent as message back to peer
# check if user has permissions # check if user has permissions
return if !permission_check('chat.agent', 'chat') return if !permission_check('chat.agent', 'chat')
chat_user = User.lookup(id: @session['id']) update_state
Chat::Agent.state(@session['id'], @payload['data']['active'])
chat_ids = Chat.agent_active_chat_ids(chat_user)
# broadcast new state to agents
Chat.broadcast_agent_state_update(chat_ids, @session['id'])
{ {
event: 'chat_agent_state', event: 'chat_agent_state',
@ -41,4 +34,17 @@ return is sent as message back to peer
} }
end end
private
def update_state
chat_user = User.lookup(id: @session['id'])
return if !Chat::Agent.state(@session['id'], @payload['data']['active'])
chat_ids = Chat.agent_active_chat_ids(chat_user)
# broadcast new state to agents
Chat.broadcast_agent_state_update(chat_ids, @session['id'])
end
end end

View file

@ -1,5 +1,6 @@
FactoryBot.define do FactoryBot.define do
factory :'chat/agent' do factory :'chat/agent' do
active { true }
created_by_id { 1 } created_by_id { 1 }
updated_by_id { 1 } updated_by_id { 1 }
end end

View file

@ -0,0 +1,63 @@
require 'rails_helper'
RSpec.describe Sessions::Event::ChatAgentState do
let(:client_id) { rand(123_456_789) }
let(:chat) { Chat.first }
let(:user) do
create(:agent, preferences: {
chat: {
active: {
chat.id.to_s => 'on'
}
}
})
end
let!(:instance) do
Sessions.create(client_id, { 'id' => user.id }, {})
Sessions.queue(client_id)
described_class.new(
payload: {
'data' => {
'active' => active
},
},
user_id: user.id,
client_id: client_id,
clients: {},
session: {
'id' => user.id
},
)
end
let(:record) { create(:'chat/agent', updated_by: user) }
before do
Setting.set('chat', true)
end
context 'when state changes' do
let(:active) { !record.active }
it 'broadcasts agent state update' do
allow(Chat).to receive(:broadcast_agent_state_update)
instance.run
expect(Chat).to have_received(:broadcast_agent_state_update)
end
end
context "when state doesn't change" do
let(:active) { record.active }
it "doesn't broadcasts agent state update" do
allow(Chat).to receive(:broadcast_agent_state_update)
instance.run
expect(Chat).not_to have_received(:broadcast_agent_state_update)
end
end
end

View file

@ -0,0 +1,82 @@
require 'rails_helper'
RSpec.describe Chat::Agent, type: :model do
describe '.state' do
let(:user) { create(:agent) }
context 'when no record exists for User' do
it 'returns false' do
expect(described_class.state(1337)).to be(false)
end
end
context 'when active flag is set to true' do
before do
create(:'chat/agent', active: true, updated_by: user)
end
it 'returns true' do
expect(described_class.state(user.id)).to be(true)
end
end
context 'when active flag is set to false' do
before do
create(:'chat/agent', active: false, updated_by: user)
end
it 'returns false' do
expect(described_class.state(user.id)).to be(false)
end
end
context 'when setting state for not existing record' do
it 'creates a record' do
expect { described_class.state(user.id, true) }.to change { described_class.exists?(updated_by: user) }.from(false).to(true)
end
end
context 'when setting same state for record' do
let(:record) { create(:'chat/agent', active: true, updated_by: user) }
before do
# avoid race condition with same updated_at time
record
travel_to 5.minutes.from_now
end
it 'updates updated_at timestamp' do
expect { described_class.state(record.updated_by_id, record.active) }.to change { record.reload.updated_at }
end
it 'returns false' do
expect(described_class.state(record.updated_by_id, record.active)).to eq(false)
end
end
context 'when setting different state for record' do
let(:record) { create(:'chat/agent', active: true, updated_by: user) }
before do
# avoid race condition with same updated_at time
record
travel_to 5.minutes.from_now
end
it 'updates updated_at timestamp' do
expect { described_class.state(record.updated_by_id, !record.active) }.to change { record.reload.updated_at }
end
it 'returns true' do
expect(described_class.state(record.updated_by_id, !record.active)).to eq(true)
end
end
end
end