We have upgraded osTicket version to 1.16 today (from 1.12) with this we also needed to upgrade our php version to 8 (from 7.2). We have followed the upgrade guide step by step, ran the upgrade wizard after updating the files. But the performance is now absolutely terrible. Going to /scp page fires up a query which uses up all of the server's resources and ends up executing for around 5 minutes, earlier (before the upgrade) it used to be almost instant. What could I check/do to potentially fix this issue?
OS - Windows server 2016
Mysql - 10.4.8-MariaDB
osTicket version - 1.16
php version - 8.0

And this is the query that it runs forever
SELECT COUNT(DISTINCT CASE WHEN A2.state= 'open' AND A2.state= 'open' AND A2.state= 'open' AND A1.dept_id= 1 AND A1.team_id= 0 AND A1.staff_id= 0 AND A3.object_type= 'T' THEN A1.ticket_idEND) ASq15, COUNT(DISTINCT CASE WHEN (A1.team_idIN (2) OR A1.staff_idIN (4)) AND A2.state= 'open' AND (A1.team_idIN (2) OR A1.staff_idIN (4)) AND A2.state= 'open' AND (A1.team_idIN (2) OR A1.staff_idIN (4)) AND A2.state= 'open' AND B4.priorityREGEXP '\"?1(\"|,|$)' AND A3.object_type= 'T' THEN A1.ticket_idEND) ASq18, COUNT(DISTINCT CASE WHEN (A1.team_idIN (2) OR A1.staff_idIN (4)) AND A2.state= 'open' AND (A1.team_idIN (2) OR A1.staff_idIN (4)) AND A2.state= 'open' AND (A1.team_idIN (2) OR A1.staff_idIN (4)) AND A2.state= 'open' AND NOT B4.priorityREGEXP '\"?1(\"|,|$)' AND A3.object_type= 'T' THEN A1.ticket_idEND) ASq19, COUNT(DISTINCT CASE WHEN A2.state= 'open' AND A1.isanswered= '0' AND A2.state= 'open' AND A1.isanswered= '0' AND A2.state= 'open' AND A1.isanswered= '0' AND A1.staff_idIN (4, 2) AND NOT B4.priorityREGEXP '\"?1(\"|,|$)' AND A3.object_type= 'T' THEN A1.ticket_idEND) ASq27, COUNT(DISTINCT CASE WHEN A2.state= 'open' AND A2.state= 'open' AND A2.state= 'open' AND A2.idIN (8) AND A3.object_type= 'T' THEN A1.ticket_idEND) ASq30, COUNT(DISTINCT CASE WHEN A2.state= 'open' AND A2.state= 'open' AND A2.state= 'open' AND A1.dept_id= 3 AND A1.team_id= 0 AND A1.staff_id= 0 AND A3.object_type= 'T' THEN A1.ticket_idEND) ASq32, COUNT(DISTINCT CASE WHEN A2.state= 'open' AND A2.state= 'open' AND A2.state= 'open' AND A1.staff_id= 45 AND NOT A2.idIN (10) AND A3.object_type= 'T' THEN A1.ticket_idEND) ASq33, COUNT(DISTINCT CASE WHEN A2.state= 'open' AND A2.state= 'open' AND A2.state= 'open' AND A1.staff_idIN (48) AND A3.object_type= 'T' THEN A1.ticket_idEND) ASq34, COUNT(DISTINCT CASE WHEN A2.state= 'open' AND A2.state= 'open' AND A2.state= 'open' AND A1.staff_idIN (2) AND A3.object_type= 'T' THEN A1.ticket_idEND) ASq35, COUNT(DISTINCT CASE WHEN A2.state= 'open' AND A2.state= 'open' AND A2.state= 'open' AND A1.staff_idIN (4) AND A3.object_type= 'T' THEN A1.ticket_idEND) ASq36, COUNT(DISTINCT CASE WHEN A2.state= 'open' AND A2.state= 'open' AND A2.state= 'open' AND A2.idIN (10) AND A1.staff_id= 45 AND A3.object_type= 'T' THEN A1.ticket_idEND) ASq38, COUNT(DISTINCT CASE WHEN A2.state= 'open' AND A3.object_type= 'T' THEN A1.ticket_idEND) ASq1, COUNT(DISTINCT CASE WHEN A2.state= 'open' AND A2.state= 'open' AND A1.isanswered= '0' AND A3.object_type= 'T' THEN A1.ticket_idEND) ASq2, COUNT(DISTINCT CASE WHEN (A1.team_idIN (2) OR A1.staff_idIN (4)) AND A2.state= 'open' AND (A1.team_idIN (2) OR A1.staff_idIN (4)) AND A2.state= 'open' AND A1.staff_idIN (4) AND A3.object_type= 'T' THEN A1.ticket_idEND) ASq6, COUNT(DISTINCT CASE WHEN A2.state= 'closed' AND A2.state= 'closed' AND A1.closedBETWEEN '2022-08-01 00:00:00' AND '2022-08-01 23:59:59' AND A3.object_type= 'T' THEN A1.ticket_idEND) ASq9, COUNT(DISTINCT CASE WHEN A2.state= 'open' AND A2.state= 'open' AND A1.isanswered= '1' AND A3.object_type= 'T' THEN A1.ticket_idEND) ASq3, COUNT(DISTINCT CASE WHEN (A1.team_idIN (2) OR A1.staff_idIN (4)) AND A2.state= 'open' AND (A1.team_idIN (2) OR A1.staff_idIN (4)) AND A2.state= 'open' AND NOT A1.staff_idIN (4) AND A3.object_type= 'T' THEN A1.ticket_idEND) ASq7, COUNT(DISTINCT CASE WHEN A2.state= 'closed' AND A2.state= 'closed' AND A1.closedBETWEEN '2022-07-31 00:00:00' AND '2022-07-31 23:59:59' AND A3.object_type= 'T' THEN A1.ticket_idEND) ASq10, COUNT(DISTINCT CASE WHEN A2.state= 'open' AND A2.state= 'open' AND A1.isoverdue= '1' AND A3.object_type= 'T' THEN A1.ticket_idEND) ASq4, COUNT(DISTINCT CASE WHEN (A1.team_idIN (2) OR A1.staff_idIN (4)) AND A2.state= 'open' AND A3.object_type= 'T' THEN A1.ticket_idEND) ASq5, COUNT(DISTINCT CASE WHEN A2.state= 'closed' AND A2.state= 'closed' AND A1.closedBETWEEN '2022-08-01 00:00:00' AND '2022-08-07 23:59:59' AND A3.object_type= 'T' THEN A1.ticket_idEND) ASq11, COUNT(DISTINCT CASE WHEN A2.state= 'closed' AND A3.object_type= 'T' THEN A1.ticket_idEND) ASq8, COUNT(DISTINCT CASE WHEN A2.state= 'closed' AND A2.state= 'closed' AND A1.closedBETWEEN '2022-08-01 00:00:00' AND '2022-08-31 23:59:59' AND A3.object_type= 'T' THEN A1.ticket_idEND) ASq12, COUNT(DISTINCT CASE WHEN A2.state= 'closed' AND A2.state= 'closed' AND A1.closedBETWEEN '2022-07-01 00:00:00' AND '2022-09-30 23:59:59' AND A3.object_type= 'T' THEN A1.ticket_idEND) ASq13, COUNT(DISTINCT CASE WHEN A2.state= 'closed' AND A2.state= 'closed' AND A1.closedBETWEEN '2022-01-01 00:00:00' AND '2022-12-31 23:59:59' AND A3.object_type= 'T' THEN A1.ticket_idEND) ASq14FROMost_ticketA1 JOINost_ticket_statusA2 ON (A1.status_id= A2.id) LEFT JOINost_threadA3 ON (A3.object_type= 'T' AND A1.ticket_id= A3.object_id) LEFT JOINost_thread_referralA4 ON (A3.id= A4.thread_id) LEFT JOINost_staffA5 ON (A4.object_type= 'S' AND A4.object_id= A5.staff_id) LEFT JOINost_threadA6 ON (A1.ticket_id= A6.object_idAND A6.object_type= 'C') LEFT JOINost_thread_referralA7 ON (A6.id= A7.thread_id) LEFT JOINost_staffA8 ON (A7.object_type= 'S' AND A7.object_id= A8.staff_id) LEFT JOINost_teamB0 ON (A4.object_type= 'E' AND A4.object_id= B0.team_id) LEFT JOINost_teamB1 ON (A7.object_type= 'E' AND A7.object_id= B1.team_id) LEFT JOINost_departmentB2 ON (A4.object_type= 'D' AND A4.object_id= B2.id) LEFT JOINost_departmentB3 ON (A7.object_type= 'D' AND A7.object_id= B3.id) LEFT JOINost_ticket__cdataB4 ON (A1.ticket_id= B4.ticket_id) WHERE ((A2.state= 'open' AND (A1.staff_id= 4 OR A5.staff_id= 4 OR A6.object_type= 'C' AND A8.staff_id= 4 OR A1.team_idIN (2) OR B0.team_idIN (2) OR A6.object_type= 'C' AND B1.team_idIN (2))) OR (A2.stateIN ('open', 'closed') AND (A1.dept_idIN (1, 3) OR B2.idIN (1, 3))) OR A6.object_type= 'C' AND B3.idIN (1, 3))

  • KevinTheJedi replied to this.
  • Marcius

    Seems like your MySQL instance is not optimized or your server specs aren't tuned. You can apply the below patch to run individual queue counts instead of lumping them into 1 query:

    /path/to/your/osticket/include/class.search.php (Lines 995-L1000)

    //        try {
    //            $counts = array_merge($counts, $query->values()->one());
    //        }  catch (Exception $ex) {
                foreach ($queues as $q)
                    $counts['q'.$q->getId()] = $q->getTotal();
    //        }

    Cheers.

    Marcius

    Seems like your MySQL instance is not optimized or your server specs aren't tuned. You can apply the below patch to run individual queue counts instead of lumping them into 1 query:

    /path/to/your/osticket/include/class.search.php (Lines 995-L1000)

    //        try {
    //            $counts = array_merge($counts, $query->values()->one());
    //        }  catch (Exception $ex) {
                foreach ($queues as $q)
                    $counts['q'.$q->getId()] = $q->getTotal();
    //        }

    Cheers.

      a month later

      KevinTheJedi We're experiencing a similar issue with another use case - trying to view "closed" tickets. Just clicking this button issues another large query, which runs forever

      Here's the query

      SELECT A1.`number` AS `number`, A1.`ticket_id` AS `ticket_id`, A1.`source` AS `source`, A1.`closed` AS `closed`, B4.`subject` AS `cdata__subject`, A1.`isoverdue` AS `isoverdue`, B5.`name` AS `user__name`, B6.`firstname` AS `staff__firstname`, B6.`lastname` AS `staff__lastname`, B7.`name` AS `team__name`, A1.`team_id` AS `team_id`, A1.`isanswered` = '0' AS `2GkBZxz`, (SELECT COUNT(R0.`id`) AS `count` FROM `ost_thread` Q7 JOIN `ost_ticket` Q8 ON (Q7.`object_type` = 'T' AND Q7.`object_id` = Q8.`ticket_id`) LEFT JOIN `ost_thread_entry` R0 ON (Q7.`id` = R0.`thread_id`) WHERE Q8.`ticket_id` = A1.`ticket_id` AND NOT R0.`flags` & 4 != 0) AS _thread_count, (SELECT COUNT(R1.`id`) AS `count` FROM `ost_thread` Q7 JOIN `ost_ticket` Q8 ON (Q7.`object_type` = 'T' AND Q7.`object_id` = Q8.`ticket_id`) LEFT JOIN `ost_thread_entry` R0 ON (Q7.`id` = R0.`thread_id`) LEFT JOIN `ost_attachment` R1 ON (R1.`type` = 'H' AND R0.`id` = R1.`object_id`) WHERE Q8.`ticket_id` = A1.`ticket_id` AND R1.`inline` = 0) AS _att_count, B8.`expire` > NOW() AND NOT B8.`staff_id` = 4 AS `_locked`, (SELECT COUNT(R0.`id`) AS `count` FROM `ost_thread` Q7 JOIN `ost_ticket` Q8 ON (Q7.`object_type` = 'T' AND Q7.`object_id` = Q8.`ticket_id`) LEFT JOIN `ost_thread_collaborator` R0 ON (Q7.`id` = R0.`thread_id`) WHERE Q8.`ticket_id` = A1.`ticket_id`) AS _collabs, COALESCE(B6.`firstname`, B6.`lastname`, B7.`name`, 'zzz') AS `assignee` FROM `ost_ticket` A1 JOIN `ost_ticket_status` A2 ON (A1.`status_id` = A2.`id`) LEFT JOIN `ost_thread` A3 ON (A3.`object_type` = 'T' AND A1.`ticket_id` = A3.`object_id`) LEFT JOIN `ost_thread_referral` A4 ON (A3.`id` = A4.`thread_id`) LEFT JOIN `ost_staff` A5 ON (A4.`object_type` = 'S' AND A4.`object_id` = A5.`staff_id`) LEFT JOIN `ost_thread` A6 ON (A1.`ticket_id` = A6.`object_id` AND A6.`object_type` = 'C') LEFT JOIN `ost_thread_referral` A7 ON (A6.`id` = A7.`thread_id`) LEFT JOIN `ost_staff` A8 ON (A7.`object_type` = 'S' AND A7.`object_id` = A8.`staff_id`) LEFT JOIN `ost_team` B0 ON (A4.`object_type` = 'E' AND A4.`object_id` = B0.`team_id`) LEFT JOIN `ost_team` B1 ON (A7.`object_type` = 'E' AND A7.`object_id` = B1.`team_id`) LEFT JOIN `ost_department` B2 ON (A4.`object_type` = 'D' AND A4.`object_id` = B2.`id`) LEFT JOIN `ost_department` B3 ON (A7.`object_type` = 'D' AND A7.`object_id` = B3.`id`) LEFT JOIN `ost_ticket__cdata` B4 ON (A1.`ticket_id` = B4.`ticket_id`) LEFT JOIN `ost_user` B5 ON (A1.`user_id` = B5.`id`) LEFT JOIN `ost_staff` B6 ON (A1.`staff_id` = B6.`staff_id`) LEFT JOIN `ost_team` B7 ON (A1.`team_id` = B7.`team_id`) LEFT JOIN `ost_lock` B8 ON (A1.`lock_id` = B8.`lock_id`) WHERE A2.`state` = 'closed' AND ((A2.`state` = 'open' AND (A1.`staff_id` = 4 OR A5.`staff_id` = 4 OR A6.`object_type` = 'C' AND A8.`staff_id` = 4 OR A1.`team_id` IN (2) OR B0.`team_id` IN (2) OR A6.`object_type` = 'C' AND B1.`team_id` IN (2))) OR A1.`dept_id` IN (1, 3) OR B2.`id` IN (1, 3) OR A6.`object_type` = 'C' AND B3.`id` IN (1, 3)) AND (A1.`ticket_pid` IS NULL OR A1.`flags` & 8 != 0) GROUP BY A1.`ticket_id` ORDER BY A1.`closed` DESC LIMIT 50

      Perhaps you have some suggestions what else we could tweak?

        Marcius

        That one is actually not that bad. You will most likely need to tweak MySQL and it’s settings and/or give the server more resources.

        You may also want to check the attachments in the database to see if it’s taking up a lot of storage. If so you might have better luck installing FS plugin and migrate all attachments to the file system.

        Cheers.

          KevinTheJedi Thanks for your reply, I'd like to note that it was working fine before the 1.16 update so I was expecting there was something I could do code-wise to fix this? Just adding more resources to the server because there might be a code error doesn't seem like the best way to go. I'll check on the attachment suggestion, thanks.

            Marcius

            If you can then by all means please; be my guest. However I don’t think there is much you can do. Maybe you can EXPLAIN the query in MySQL, see where it’s pain points are, and address it. If you can then make a pull request so we can review.

            Cheers.

            The change to class.search.php made no difference for me.

            The basic problem I have is that if I click on Closed to show all closed tickets, mariadb runs at 100.0 %CPU (as shown by top) for 5 minutes or more. I host at AWS and I've tried 1 vCPU Graviton (arm) up to 2 vCPU Xeon processors and it makes no difference. My 7 year old server, running v1.14.3, processes clicking Closed in a couple of seconds.

            Tuning MySQL has been mentioned a couple of times. What tuning? Shouldn't osTicket just work with a stock MySQL implementation. Did I miss something in the osTicket installation documentation?

              kwallace

              No you didn't miss anything. As with any application, when you have more and more data you should prepare to upgrade/expand your environment as well. A lot of people opt for tuning their MySQL instance but most people don't know MySQL well and can't do this on their own. These people upgrade their environment.

              Anyways, the whole queue system changed and the queries are quite complex nowadays. This can put some strain on MySQL if you have a lot of data or your system can't handle the load. As I mentioned before you can EXPLAIN the MySQL query to see it's pain points and try to fix it. The code is open source allowing you to modify to your heart's content. If you fix it you can make a pull request for us to review. We do have "looking into possibly optimizing the queue queries" on our todo list but we have much more important things at the moment.

              You also never mentioned your version of MySQL. People have been having great success with MySQL 8.0+. Older versions of MySQL were a lot slower and had other issues as well.

              Edit:
              After re-reading this comment it sounds like I’m blaming the environment which was not my intention; I was trying to highlight what people have done to workaround performance issues. We can always better optimize the queries we use however as I mentioned it’s lower on our todo list at the moment.

              Cheers.

              6 days later

              @KevinTheJedi
              This is the output of explain

              What troubles me the most is the B4 with null key used and 22706 rows. Any idea how I could make this situation better?

              I have modified the query a little bit

              select q1.*, q2.subject as `cdata__subject` from (
                  SELECT 
                A1.`number` AS `number`, 
                A1.`ticket_id` AS `ticket_id`, 
                A1.`source` AS `source`, 
                A1.`closed` AS `closed`, 
                A1.`isoverdue` AS `isoverdue`, 
                B5.`name` AS `user__name`, 
                B6.`firstname` AS `staff__firstname`, 
                B6.`lastname` AS `staff__lastname`, 
                B7.`name` AS `team__name`, 
                A1.`team_id` AS `team_id`, 
                A1.`isanswered` = '0' AS `2GkBZxz`, 
                (
                  SELECT 
                    COUNT(R0.`id`) AS `count` 
                  FROM 
                    `ost_thread` Q7 
                    JOIN `ost_ticket` Q8 ON (
                      Q7.`object_type` = 'T' 
                      AND Q7.`object_id` = Q8.`ticket_id`
                    ) 
                    LEFT JOIN `ost_thread_entry` R0 ON (Q7.`id` = R0.`thread_id`) 
                  WHERE 
                    Q8.`ticket_id` = A1.`ticket_id` 
                    AND NOT R0.`flags` & 4 != 0
                ) AS _thread_count, 
                (
                  SELECT 
                    COUNT(R1.`id`) AS `count` 
                  FROM 
                    `ost_thread` Q7 
                    JOIN `ost_ticket` Q8 ON (
                      Q7.`object_type` = 'T' 
                      AND Q7.`object_id` = Q8.`ticket_id`
                    ) 
                    LEFT JOIN `ost_thread_entry` R0 ON (Q7.`id` = R0.`thread_id`) 
                    LEFT JOIN `ost_attachment` R1 ON (
                      R1.`type` = 'H' 
                      AND R0.`id` = R1.`object_id`
                    ) 
                  WHERE 
                    Q8.`ticket_id` = A1.`ticket_id` 
                    AND R1.`inline` = 0
                ) AS _att_count, 
                B8.`expire` > NOW() 
                AND NOT B8.`staff_id` = 4 AS `_locked`, 
                (
                  SELECT 
                    COUNT(R0.`id`) AS `count` 
                  FROM 
                    `ost_thread` Q7 
                    JOIN `ost_ticket` Q8 ON (
                      Q7.`object_type` = 'T' 
                      AND Q7.`object_id` = Q8.`ticket_id`
                    ) 
                    LEFT JOIN `ost_thread_collaborator` R0 ON (Q7.`id` = R0.`thread_id`) 
                  WHERE 
                    Q8.`ticket_id` = A1.`ticket_id`
                ) AS _collabs, 
                COALESCE(
                  B6.`firstname`, B6.`lastname`, B7.`name`, 
                  'zzz'
                ) AS `assignee` 
              FROM 
                `ost_ticket` A1 
                JOIN `ost_ticket_status` A2 ON (A1.`status_id` = A2.`id`) 
                LEFT JOIN `ost_thread` A3 ON (
                  A3.`object_type` = 'T' 
                  AND A1.`ticket_id` = A3.`object_id`
                ) 
                LEFT JOIN `ost_thread_referral` A4 ON (A3.`id` = A4.`thread_id`) 
                LEFT JOIN `ost_staff` A5 ON (
                  A4.`object_type` = 'S' 
                  AND A4.`object_id` = A5.`staff_id`
                ) 
                LEFT JOIN `ost_thread` A6 ON (
                  A1.`ticket_id` = A6.`object_id` 
                  AND A6.`object_type` = 'C'
                ) 
                LEFT JOIN `ost_thread_referral` A7 ON (A6.`id` = A7.`thread_id`) 
                LEFT JOIN `ost_staff` A8 ON (
                  A7.`object_type` = 'S' 
                  AND A7.`object_id` = A8.`staff_id`
                ) 
                LEFT JOIN `ost_team` B0 ON (
                  A4.`object_type` = 'E' 
                  AND A4.`object_id` = B0.`team_id`
                ) 
                LEFT JOIN `ost_team` B1 ON (
                  A7.`object_type` = 'E' 
                  AND A7.`object_id` = B1.`team_id`
                ) 
                LEFT JOIN `ost_department` B2 ON (
                  A4.`object_type` = 'D' 
                  AND A4.`object_id` = B2.`id`
                ) 
                LEFT JOIN `ost_department` B3 ON (
                  A7.`object_type` = 'D' 
                  AND A7.`object_id` = B3.`id`
                ) 
                LEFT JOIN `ost_user` B5 ON (A1.`user_id` = B5.`id`) 
                LEFT JOIN `ost_staff` B6 ON (A1.`staff_id` = B6.`staff_id`) 
                LEFT JOIN `ost_team` B7 ON (A1.`team_id` = B7.`team_id`) 
                LEFT JOIN `ost_lock` B8 ON (A1.`lock_id` = B8.`lock_id`) 
              WHERE 
                A2.`state` = 'closed' 
                AND (
                  (
                    A2.`state` = 'open' 
                    AND (
                      A1.`staff_id` = 4 
                      OR A5.`staff_id` = 4 
                      OR A6.`object_type` = 'C' 
                      AND A8.`staff_id` = 4 
                      OR A1.`team_id` IN (2) 
                      OR B0.`team_id` IN (2) 
                      OR A6.`object_type` = 'C' 
                      AND B1.`team_id` IN (2)
                    )
                  ) 
                  OR A1.`dept_id` IN (1, 3) 
                  OR B2.`id` IN (1, 3) 
                  OR A6.`object_type` = 'C' 
                  AND B3.`id` IN (1, 3)
                ) 
                AND (
                  A1.`ticket_pid` IS NULL 
                  OR A1.`flags` & 8 != 0
                ) 
              GROUP BY 
                A1.`ticket_id` 
              ORDER BY 
                A1.`closed` DESC 
              LIMIT 
                50
              ) as q1
              left join ost_ticket__cdata as q2 on q1.ticket_id = q2.ticket_id

              This provides the following EXPLAIN output

              Results in the same data set but runs much faster (although still takes around 20 seconds but at least not 5 minutes)

              Perhaps you could guide me in which file I should look into to find the generation of this query? Which file is used when you click on "Closed" button?

              Thanks

              Write a Reply...