Skip to content
Permalink
Browse files

Performance: Observing every Overview of each Agent Session doesn't s…

…cale well on larger systems (60 Overviews per Agent). With this change only the last 5 used Overviews are checked on every iteration. A full check is still performed ever 60 seconds. This reduces the overall load.
  • Loading branch information
zammad-sync authored and thorsteneckel committed Nov 28, 2019
1 parent f41aa09 commit 4e8cf209cab5e2b679086400c8ecfb8b840687e1
@@ -936,6 +936,10 @@ class Navbar extends App.Controller
if item.link is @view
@title item.name, true

# send first view info
if !@view && data && data[0] && data[0].link
App.WebSocket.send(event:'ticket_overview_select', data: { view: data[0].link })

# redirect to first view
if @activeState && !@view && !@vertical
view = data[0].link
@@ -1037,6 +1041,8 @@ class Table extends App.Controller
@view_mode = App.LocalStorage.get("mode:#{@view}", @Session.get('id')) || 's'
console.log 'notice', 'view:', @view, @view_mode

App.WebSocket.send(event:'ticket_overview_select', data: { view: @view })

# get ticket list
ticketListShow = []
for ticket in tickets
@@ -894,6 +894,10 @@ class App.TicketZoom extends App.Controller
if macro && macro.ux_flow_next_up
taskAction = macro.ux_flow_next_up

nextTicket = undefined
if taskAction is 'closeNextInOverview' || taskAction is 'next_from_overview'
nextTicket = @getNextTicketInOverview()

# submit changes
@ajax(
id: "ticket_update_#{ticket.id}"
@@ -916,8 +920,8 @@ class App.TicketZoom extends App.Controller
@sidebarWidget.commit()

if taskAction is 'closeNextInOverview' || taskAction is 'next_from_overview'
@openTicketInOverview(nextTicket)
App.Event.trigger('overview:fetch')
@taskOpenNextTicketInOverview()
return

if taskAction is 'closeTab' || taskAction is 'next_task'
@@ -22,22 +22,40 @@ App.TicketNavigable =
show: true
)

getNextTicketInOverview: ->
return if !@ticket
return if !@overview_id

App.Overview.find(@overview_id).nextTicket(@ticket)

openTicketInOverview: (nextTicket) ->
if nextTicket
@taskCloseTicket()
@taskLoadTicket(nextTicket.id)
return

@taskCloseTicket(true)

taskOpenNextTicketInOverview: ->
if !(@overview_id? && @ticket?)
@taskCloseTicket(true)
return
next_ticket = App.Overview.find(@overview_id).nextTicket(@ticket)
if next_ticket

nextTicket = @getNextTicketInOverview()
if nextTicket
@taskCloseTicket()
@taskLoadTicket(next_ticket.id)
@taskLoadTicket(nextTicket.id)
return

@taskCloseTicket(true)

taskCloseTicket: (openNext = false) ->
App.TaskManager.remove(@taskKey)
return if !openNext

nextTaskUrl = App.TaskManager.nextTaskUrl()
if nextTaskUrl
@navigate nextTaskUrl
return

@navigate '#'
@@ -7,6 +7,10 @@ module Ticket::Overviews
result = Ticket::Overviews.all(current_user: User.find(3))
certain overviews by user
result = Ticket::Overviews.all(current_user: User.find(3), links: ['all_unassigned', 'my_assigned'])
returns
result = [overview1, overview2]
@@ -15,6 +19,7 @@ module Ticket::Overviews

def self.all(data)
current_user = data[:current_user]
links = data[:links]

# get customer overviews
role_ids = User.joins(:roles).where(users: { id: current_user.id, active: true }, roles: { active: true }).pluck('roles.id')
@@ -23,6 +28,9 @@ def self.all(data)
if current_user.organization_id && current_user.organization.shared
overview_filter.delete(:organization_shared)
end
if links.present?
overview_filter[:link] = links
end
overviews = Overview.joins(:roles).left_joins(:users).where(overviews_roles: { role_id: role_ids }, overviews_users: { user_id: nil }, overviews: overview_filter).or(Overview.joins(:roles).left_joins(:users).where(overviews_roles: { role_id: role_ids }, overviews_users: { user_id: current_user.id }, overviews: overview_filter)).distinct('overview.id').order(:prio, :name)
return overviews
end
@@ -35,12 +43,21 @@ def self.all(data)
if User.where('out_of_office = ? AND out_of_office_start_at <= ? AND out_of_office_end_at >= ? AND out_of_office_replacement_id = ? AND active = ?', true, Time.zone.today, Time.zone.today, current_user.id, true).count.positive?
overview_filter_not = {}
end
if links.present?
overview_filter[:link] = links
end
Overview.joins(:roles).left_joins(:users).where(overviews_roles: { role_id: role_ids }, overviews_users: { user_id: nil }, overviews: overview_filter).or(Overview.joins(:roles).left_joins(:users).where(overviews_roles: { role_id: role_ids }, overviews_users: { user_id: current_user.id }, overviews: overview_filter)).where.not(overview_filter_not).distinct('overview.id').order(:prio, :name)
end

=begin
result = Ticket::Overviews.index(User.find(123))
index of all overviews by user
result = Ticket::Overviews.index(User.find(3))
index of certain overviews by user
result = Ticket::Overviews.index(User.find(3), ['all_unassigned', 'my_assigned'])
returns
@@ -75,9 +92,10 @@ def self.all(data)
=end

def self.index(user)
def self.index(user, links = nil)
overviews = Ticket::Overviews.all(
current_user: user,
links: links,
)
return [] if overviews.blank?

@@ -12,21 +12,17 @@
# Show full error reports.
config.consider_all_requests_local = true

# Commented out to ensure using file cache store as described in config/application.rb
# Enable/disable caching. By default caching is disabled.
# Run rails dev:cache to toggle caching.
# if Rails.root.join('tmp', 'caching-dev.txt').exist?
# config.action_controller.perform_caching = true
#
# config.cache_store = :memory_store
# config.public_file_server.headers = {
# 'Cache-Control' => "public, max-age=#{2.days.to_i}"
# }
# else
# config.action_controller.perform_caching = false
#
# config.cache_store = :null_store
# end
if Rails.root.join('tmp', 'caching-dev.txt').exist?
config.action_controller.perform_caching = true

config.public_file_server.headers = {
'Cache-Control' => "public, max-age=#{2.days.to_i}"
}
else
config.action_controller.perform_caching = false
end

# Store uploaded files on the local file system (see config/storage.yml for options)
# config.active_storage.service = :local
@@ -4,38 +4,95 @@ def self.reset(user_id)
Cache.write("TicketOverviewPull::#{user_id}", { needed: true })
end

def initialize(user, asset_lookup, client = nil, client_id = nil, ttl = 8)
def initialize(user, asset_lookup, client = nil, client_id = nil, ttl = 7)
@user = user
@client = client
@client_id = client_id
@ttl = ttl
@asset_lookup = asset_lookup
@last_change = nil
@last_index_lists = nil
@last_overview = {}
@last_overview_change = nil
@last_ticket_change = nil
@last_full_fetch = nil
end

def self.overview_history_append(overview, user_id)
key = "TicketOverviewHistory::#{user_id}"
history = Cache.get(key) || []

history.prepend overview
history.uniq!
if history.count > 4
history.pop
end

Cache.write(key, history)
end

def self.overview_history_get(user_id)
Cache.get("TicketOverviewHistory::#{user_id}")
end

def load

# get whole collection
index_and_lists = Ticket::Overviews.index(@user)
index_and_lists = nil
local_overview_changed = overview_changed?
if !@last_index_lists || !@last_full_fetch || @last_full_fetch < (Time.zone.now.to_i - 60) || local_overview_changed

# check if min one ticket has changed
return if !ticket_changed?(true) && !local_overview_changed

index_and_lists = Ticket::Overviews.index(@user)
@last_full_fetch = Time.zone.now.to_i
else

# check if min one ticket has changed
return if !ticket_changed? && !local_overview_changed

index_and_lists_local = Ticket::Overviews.index(@user, Sessions::Backend::TicketOverviewList.overview_history_get(@user.id))

# compare index_and_lists_local to index_and_lists_local
# return if no changes

index_and_lists = []
@last_index_lists.each do |last_index|
found_in_particular_index = false
index_and_lists_local.each do |local_index|
next if local_index[:overview][:id] != last_index[:overview][:id]

index_and_lists.push local_index
found_in_particular_index = true
break
end
next if found_in_particular_index == true

index_and_lists.push last_index
end
end

# no data exists
return if index_and_lists.blank?

# no change exists
return if @last_change == index_and_lists
return if @last_index_lists == index_and_lists

# remember last state
@last_change = index_and_lists
@last_index_lists = index_and_lists

index_and_lists
end

def local_to_run?
return false if !@time_now

return true if pull_overview?

false
end

def pull_overview?
result = Cache.get("TicketOverviewPull::#{@user.id}")
Cache.delete("TicketOverviewPull::#{@user.id}") if result
return true if result
@@ -48,14 +105,6 @@ def push

@time_now = Time.zone.now.to_i

# check if min one ticket or overview has changed
last_overview_change = Overview.latest_change
last_ticket_change = Ticket.latest_change
return if last_ticket_change == @last_ticket_change && last_overview_change == @last_overview_change

@last_overview_change = last_overview_change
@last_ticket_change = last_ticket_change

# load current data
index_and_lists = load
return if !index_and_lists
@@ -134,4 +183,26 @@ def push
nil
end

def overview_changed?

# check if min one overview has changed
last_overview_change = Overview.latest_change
return false if last_overview_change == @last_overview_change

@last_overview_change = last_overview_change

true
end

def ticket_changed?(reset = false)

# check if min one ticket has changed
last_ticket_change = Ticket.latest_change
return false if last_ticket_change == @last_ticket_change

@last_ticket_change = last_ticket_change if reset

true
end

end
@@ -0,0 +1,23 @@
class Sessions::Event::TicketOverviewSelect < Sessions::Event::Base

=begin
Event module to serve spool messages and send them to new client connection.
To execute this manually, just paste the following into the browser console
App.WebSocket.send({event:'spool'})
=end

def run
return if @payload['data'].blank?
return if @payload['data']['view'].blank?
return if @session['id'].blank?

Sessions::Backend::TicketOverviewList.overview_history_append(@payload['data']['view'], @session['id'])

nil
end

end

0 comments on commit 4e8cf20

Please sign in to comment.
You can’t perform that action at this time.