From 5fd916193163b57bb4c0e8f0d2c1a11b47675ede Mon Sep 17 00:00:00 2001 From: Peter Rotich <peter@enhancesoft.com> Date: Tue, 21 Aug 2018 15:16:49 +0000 Subject: [PATCH] queues: Counts revisited Prefer agent's queue count instead of rough count when paginating the tickets. This will make the initial queue load expensive but has an added advantage of having queue counts available thereafter for drop downs. This commits also adds entry to auto-cron, to keep queue counts more up to date in the background.When APCu is not available SESSION is used to cache the counts. --- include/ajax.search.php | 2 +- include/class.search.php | 59 ++++++++++++++----- .../staff/templates/queue-tickets.tmpl.php | 2 +- scp/autocron.php | 5 ++ 4 files changed, 50 insertions(+), 18 deletions(-) diff --git a/include/ajax.search.php b/include/ajax.search.php index dbc0a291b..fabe2d810 100644 --- a/include/ajax.search.php +++ b/include/ajax.search.php @@ -395,7 +395,7 @@ class SearchAjaxAPI extends AjaxController { if ($ids && is_array($ids)) $criteria = array('id__in' => $ids); - $counts = SavedQueue::ticketsCount($thisstaff, $criteria, 'q'); + $counts = SavedQueue::counts($thisstaff, $criteria); Http::response(200, false, 'application/json'); return $this->encode($counts); } diff --git a/include/class.search.php b/include/class.search.php index aa2919910..29dd562fc 100644 --- a/include/class.search.php +++ b/include/class.search.php @@ -848,18 +848,40 @@ class SavedQueue extends CustomQueue { return (!$errors); } - static function ticketsCount($agent, $criteria=array(), - $prefix='') { + function getCount($agent, $cached=true) { + $criteria = $cached ? array() : array('id' => $this->getId()); + $counts = self::counts($agent, $criteria, $cached); + return $counts["q{$this->getId()}"] ?: 0; + } + + // Get ticket counts for queues the agent has acces to. + static function counts($agent, $criteria=array(), $cached=true) { if (!$agent instanceof Staff) return array(); - if (function_exists('apcu_store')) { - $key = "counts.queues.{$agent->getId()}.".SECRET_SALT; - $cached = false; - $counts = apcu_fetch($key, $cached); - if ($cached === true) - return $counts; + // Cache TLS in seconds + $ttl = 3600; + // Cache key based on agent and salt of the installation + $key = "counts.queues.{$agent->getId()}.".SECRET_SALT; + if ($criteria && is_array($criteria)) // Consider additional criteria. + $key .= '.'.md5(serialize($criteria)); + + // only consider cache if requesed + if ($cached) { + if (function_exists('apcu_store')) { + $found = false; + $counts = apcu_fetch($key, $found); + if ($found === true) + return $counts; + } elseif (isset($_SESSION[$key]) + && isset($_SESSION[$key]['qcount']) + && (time() - $_SESSION[$key]['time']) < $ttl) { + return $_SESSION[$key]['qcount']; + } else { + // Auto clear missed session cache (if any) + unset($_SESSION[$key]); + } } $queues = static::objects() @@ -868,7 +890,7 @@ class SavedQueue extends CustomQueue { 'staff_id' => $agent->getId(), ))); - if ($criteria) + if ($criteria && is_array($criteria)) $queues->filter($criteria); $query = Ticket::objects(); @@ -879,15 +901,18 @@ class SavedQueue extends CustomQueue { $Q = $queue->getBasicQuery(); $expr = SqlCase::N()->when(new SqlExpr(new Q($Q->constraints)), new SqlField('ticket_id')); $query->aggregate(array( - "$prefix{$queue->id}" => SqlAggregate::COUNT($expr, true) + "q{$queue->id}" => SqlAggregate::COUNT($expr, true) )); } $counts = $query->values()->one(); - + // Always cache the results if (function_exists('apcu_store')) { - $key = "counts.queues.{$agent->getId()}.".SECRET_SALT; - apcu_store($key, $counts, 3600); + apcu_store($key, $counts, $ttl); + } else { + // Poor man's cache + $_SESSION[$key]['qcount'] = $counts; + $_SESSION[$key]['time'] = time(); } return $counts; @@ -895,9 +920,11 @@ class SavedQueue extends CustomQueue { static function clearCounts() { if (function_exists('apcu_store')) { - $regex = '/^counts.queues.\d+.' . preg_quote(SECRET_SALT, '/') . '$/'; - foreach (new APCUIterator($regex, APC_ITER_KEY) as $key) { - apcu_delete($key); + if (class_exists('APCUIterator')) { + $regex = '/^counts.queues.\d+.' . preg_quote(SECRET_SALT, '/') . '$/'; + foreach (new APCUIterator($regex, APC_ITER_KEY) as $key) { + apcu_delete($key); + } } // Also clear rough counts apcu_delete("rough.counts.".SECRET_SALT); diff --git a/include/staff/templates/queue-tickets.tmpl.php b/include/staff/templates/queue-tickets.tmpl.php index 205ac43c3..10a4cffba 100644 --- a/include/staff/templates/queue-tickets.tmpl.php +++ b/include/staff/templates/queue-tickets.tmpl.php @@ -76,7 +76,7 @@ if (!$sorted && isset($sort['queuesort'])) { $page = ($_GET['p'] && is_numeric($_GET['p']))?$_GET['p']:1; $pageNav = new Pagenate(PHP_INT_MAX, $page, PAGE_LIMIT); $tickets = $pageNav->paginateSimple($tickets); -$count = $queue->getRoughCount(); +$count = $queue->getCount($thisstaff); $pageNav->setTotal($count, true); $pageNav->setURL('tickets.php', $args); ?> diff --git a/scp/autocron.php b/scp/autocron.php index 170ab3a8b..5f5da4fb2 100644 --- a/scp/autocron.php +++ b/scp/autocron.php @@ -45,6 +45,11 @@ if ($sec < 180 || !$ost || $ost->isUpgradePending()) require_once(INCLUDE_DIR.'class.cron.php'); +// Run tickets count every 3rd run or so... force new count by skipping cached +// results +if (mt_rand(1, 12) == 3) + SavedQueue::counts($thisstaff, array(), false); + // Clear staff obj to avoid false credit internal notes & auto-assignment $thisstaff = null; -- GitLab