Fix slow queries in snapshot process
authorEileen McNaughton <emcnaughton@wikimedia.org>
Wed, 25 Jan 2023 21:25:28 +0000 (10:25 +1300)
committerEileen McNaughton <emcnaughton@wikimedia.org>
Wed, 25 Jan 2023 21:27:45 +0000 (10:27 +1300)
SELECT COUNT(*) is slow to run on a large database but SELECT MAX(id) is
quick. The snapshot check only needs to be very rough - so MAX(id) should
be accurate enough.

This does make the message a little inaccurate - but my money says no-one
will notice & it's not worth creating work for the translators to change

CRM/Core/DAO.php
CRM/Upgrade/Snapshot.php

index f5f376fa120f51416b3e3e5fd6d84c31ee5ed994..eb84c1651ca9286d5f9912a503ba771ae12a7285 100644 (file)
@@ -1726,9 +1726,11 @@ LIKE %1
    * @param array $params
    * @param bool $abort
    * @param bool $i18nRewrite
+   *
    * @return string|null
    *   the result of the query if any
    *
+   * @throws \CRM_Core_Exception
    */
   public static function &singleValueQuery(
     $query,
index 9f5a493844f298b7c47ddcb8229fb0743a772df1..0f73ee3c5e958dfe5d9f909a7a0e387f2efff5de 100644 (file)
@@ -9,6 +9,8 @@
  +--------------------------------------------------------------------+
  */
 
+use Civi\Test\Invasive;
+
 /**
  * Provide helpers for recording data snapshots during an upgrade.
  */
@@ -35,6 +37,7 @@ class CRM_Upgrade_Snapshot {
 
   /**
    * Get a list of reasons why the snapshots should not run.
+   *
    * @return array
    *   List of printable messages.
    */
@@ -57,15 +60,16 @@ class CRM_Upgrade_Snapshot {
       static::$activationIssues = [];
       foreach ($limits as $table => $limit) {
         try {
-          $count = CRM_Core_DAO::singleValueQuery("SELECT count(*) FROM `{$table}`");
+          // Use select MAX(id) rather than COUNT as COUNT is slow on large databases.
+          $max = CRM_Core_DAO::singleValueQuery("SELECT MAX(id) FROM `{$table}`");
         }
-        catch (\Exception $e) {
-          $count = 0;
+        catch (CRM_Core_Exception $e) {
+          $max = 0;
         }
-        if ($count > $limit) {
+        if ($max > $limit) {
           static::$activationIssues["count_{$table}"] = ts('Table "%1" has a large number of records (%2 > %3).', [
             1 => $table,
-            2 => $count,
+            2 => $max,
             3 => $limit,
           ]);
         }
@@ -129,11 +133,13 @@ class CRM_Upgrade_Snapshot {
    *   Ex: '5.50'
    * @param string $name
    * @param \CRM_Utils_SQL_Select $select
+   *
+   * @return iterable
    * @throws \CRM_Core_Exception
    */
   public static function createTasks(string $owner, string $version, string $name, CRM_Utils_SQL_Select $select): iterable {
     $destTable = static::createTableName($owner, $version, $name);
-    $srcTable = \Civi\Test\Invasive::get([$select, 'from']);
+    $srcTable = Invasive::get([$select, 'from']);
 
     // Sometimes, backups fail and people rollback and try again. Reset prior snapshots.
     CRM_Core_DAO::executeQuery("DROP TABLE IF EXISTS `{$destTable}`");
@@ -164,7 +170,9 @@ class CRM_Upgrade_Snapshot {
   /**
    * @param \CRM_Queue_TaskContext $ctx
    * @param string $sql
+   *
    * @return bool
+   * @noinspection PhpUnusedParameterInspection
    */
   public static function insertSnapshotTask(CRM_Queue_TaskContext $ctx, string $sql): bool {
     CRM_Core_DAO::executeQuery($sql);
@@ -182,9 +190,13 @@ class CRM_Upgrade_Snapshot {
    *   The current version of CiviCRM.
    * @param int|null $cleanupAfter
    *   How long should we retain old snapshots?
-   *   Time is measured in terms of MINOR versions - eg "4" means "retain for 4 MINOR versions".
-   *   Thus, on v5.60, you could delete any snapshots predating 5.56.
+   *   Time is measured in terms of MINOR versions - eg "4" means "retain for 4
+   *   MINOR versions". Thus, on v5.60, you could delete any snapshots
+   *   predating 5.56.
+   *
    * @return bool
+   * @throws \CRM_Core_Exception
+   * @noinspection PhpUnusedParameterInspection
    */
   public static function cleanupTask(?CRM_Queue_TaskContext $ctx = NULL, string $owner = 'civicrm', ?string $version = NULL, ?int $cleanupAfter = NULL): bool {
     $version = $version ?: CRM_Core_BAO_Domain::version();
@@ -194,12 +206,12 @@ class CRM_Upgrade_Snapshot {
     $cutoff = $major . '.' . max(0, $minor - $cleanupAfter);
 
     $dao = new CRM_Core_DAO();
-    $query = "
+    $query = '
       SELECT TABLE_NAME as tableName
       FROM   INFORMATION_SCHEMA.TABLES
       WHERE  TABLE_SCHEMA = %1
       AND TABLE_NAME LIKE %2
-    ";
+    ';
     $tables = CRM_Core_DAO::executeQuery($query, [
       1 => [$dao->database(), 'String'],
       2 => ["snap_{$owner}_v%", 'String'],