From ef67b71353ea4815ef1e4fdbc67e0d48d791ee17 Mon Sep 17 00:00:00 2001
From: Jared Hancock <gravydish@gmail.com>
Date: Tue, 14 Aug 2018 03:51:32 +0000
Subject: [PATCH] queues: cache queue counts

If APCu is available, then the queue counts can be cached between requests.
They are automatically cleared and recalculated if the status of a ticket
changes or if a queue or saved search is edited. Otherwise, the queue counts
will expire after an hour and be recalculated anyway.
---
 include/class.queue.php  |  5 ++++-
 include/class.search.php | 28 ++++++++++++++++++++++++++--
 include/class.ticket.php |  3 +++
 3 files changed, 33 insertions(+), 3 deletions(-)

diff --git a/include/class.queue.php b/include/class.queue.php
index b35f33b85..e62300017 100644
--- a/include/class.queue.php
+++ b/include/class.queue.php
@@ -1299,8 +1299,11 @@ class CustomQueue extends VerySimpleModel {
         $nopath = !isset($this->path);
         $path_changed = isset($this->dirty['parent_id']);
 
-        if ($this->dirty)
+        if ($this->dirty) {
             $this->updated = SqlFunction::NOW();
+            // Refetch the queue counts
+            SavedQueue::clearCounts();
+        }
         if (!($rv = parent::save($refetch || $this->dirty)))
             return $rv;
 
diff --git a/include/class.search.php b/include/class.search.php
index eb720bcc2..6dad61416 100644
--- a/include/class.search.php
+++ b/include/class.search.php
@@ -854,7 +854,15 @@ class SavedQueue extends CustomQueue {
         if (!$agent instanceof Staff)
             return array();
 
-        $queues = SavedQueue::objects()
+        if (function_exists('apcu_store')) {
+            $key = "counts.queues.{$agent->getId()}.".SECRET_SALT;
+            $cached = false;
+            $counts = apcu_fetch($key, $cached);
+            if ($cached === true)
+                return $counts;
+        }
+
+        $queues = static::objects()
             ->filter(Q::any(array(
                 'flags__hasbit' => CustomQueue::FLAG_QUEUE,
                 'staff_id' => $agent->getId(),
@@ -875,7 +883,23 @@ class SavedQueue extends CustomQueue {
             ));
         }
 
-        return $query->values()->one();
+        $counts = $query->values()->one();
+
+        if (function_exists('apcu_store')) {
+            $key = "counts.queues.{$agent->getId()}.".SECRET_SALT;
+            apcu_store($key, $counts, 3600);
+        }
+
+        return $counts;
+    }
+
+    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);
+            }
+        }
     }
 
     static function lookup($criteria) {
diff --git a/include/class.ticket.php b/include/class.ticket.php
index 9db2bff77..37c80c73c 100644
--- a/include/class.ticket.php
+++ b/include/class.ticket.php
@@ -3200,6 +3200,9 @@ implements RestrictedAccess, Threadable, Searchable {
     function save($refetch=false) {
         if ($this->dirty) {
             $this->updated = SqlFunction::NOW();
+            if (isset($this->dirty['status_id']))
+                // Refetch the queue counts
+                SavedQueue::clearCounts();
         }
         return parent::save($this->dirty || $refetch);
     }
-- 
GitLab