CRM-16642 Switch to opportunisticCacheRefresh calls.
authoreileenmcnaugton <eileen@fuzion.co.nz>
Mon, 16 May 2016 01:44:58 +0000 (13:44 +1200)
committerTim Otten <totten@civicrm.org>
Thu, 19 May 2016 22:50:31 +0000 (15:50 -0700)
There is one call left to the function called 'remove' which needs a little more consideration

13 files changed:
CRM/Activity/BAO/Activity.php
CRM/Contact/BAO/Contact.php
CRM/Contact/BAO/Contact/Utils.php
CRM/Contact/BAO/GroupContact.php
CRM/Contact/BAO/GroupContactCache.php
CRM/Contact/Form/CustomData.php
CRM/Contact/Form/Inline/CustomData.php
CRM/Contact/Page/AJAX.php
CRM/Contribute/BAO/Contribution.php
CRM/Core/BAO/EntityTag.php
CRM/Event/BAO/Participant.php
CRM/Member/BAO/Membership.php
CRM/Upgrade/Incremental/php/FourSeven.php

index ea3077a2e6e4675da2e0bc7b9e90e7afcd5e2fca..d4027444e5ecb35f7afd6faec2b721862a134a0e 100644 (file)
@@ -584,8 +584,7 @@ class CRM_Activity_BAO_Activity extends CRM_Activity_DAO_Activity {
       }
     }
 
-    // reset the group contact cache since smart groups might be affected due to this
-    CRM_Contact_BAO_GroupContactCache::remove();
+    CRM_Contact_BAO_GroupContactCache::opportunisticCacheRefresh();
 
     if (!empty($params['id'])) {
       CRM_Utils_Hook::post('edit', 'Activity', $activity->id, $activity);
index 309d552023105311c6e6700c65c226f42353140a..0e9dc2b34b3a7a7dc3db1d03b3d86e0f34d8cd57 100644 (file)
@@ -935,7 +935,7 @@ WHERE     civicrm_contact.id = " . CRM_Utils_Type::escape($id, 'Integer');
       CRM_Core_DAO::executeQuery('DELETE FROM civicrm_acl_contact_cache WHERE contact_id = %1', array(1 => array($contactID, 'Integer')));
     }
     else {
-      CRM_Contact_BAO_GroupContactCache::remove();
+      CRM_Contact_BAO_GroupContactCache::opportunisticCacheRefresh();
     }
   }
 
@@ -1932,8 +1932,7 @@ ORDER BY civicrm_email.is_primary DESC";
       CRM_Contact_BAO_GroupContact::addContactsToGroup($contactIds, $addToGroupID);
     }
 
-    // reset the group contact cache for this group
-    CRM_Contact_BAO_GroupContactCache::remove();
+    CRM_Contact_BAO_GroupContactCache::opportunisticCacheRefresh();
 
     if ($editHook) {
       CRM_Utils_Hook::post('edit', 'Profile', $contactID, $params);
index 5324795647e176a6136b611de405a6c48207d359..5eaf2fa867827d66b43a8ad0d5de91276b867614 100644 (file)
@@ -910,8 +910,7 @@ Group By  componentId";
       CRM_Core_BAO_PrevNextCache::deleteItem();
     }
 
-    // reset the group contact cache for this group
-    CRM_Contact_BAO_GroupContactCache::remove();
+    CRM_Contact_BAO_GroupContactCache::opportunisticCacheRefresh();
   }
 
   /**
index fabed592d135e95e782f16e0dcb786fd47c7fa34..654b4513809ef19669d6c98ee90dbf4738a6aec0 100644 (file)
@@ -148,7 +148,7 @@ class CRM_Contact_BAO_GroupContact extends CRM_Contact_DAO_GroupContact {
 
     // reset the group contact cache for all group(s)
     // if this group is being used as a smart group
-    CRM_Contact_BAO_GroupContactCache::remove();
+    CRM_Contact_BAO_GroupContactCache::opportunisticCacheRefresh();
 
     CRM_Utils_Hook::post('create', 'GroupContact', $groupId, $contactIds);
 
@@ -250,6 +250,12 @@ class CRM_Contact_BAO_GroupContact extends CRM_Contact_DAO_GroupContact {
 
     // reset the group contact cache for all group(s)
     // if this group is being used as a smart group
+    // @todo consider what to do here - it feels like we should either
+    // 1) just invalidate the specific group's cache(& perhaps any parents) & let cron do it's thing or
+    // possibly clear this specific groups cache, or just call opportunisticCacheRefresh() - which would have the
+    // same effect as the remove call. The reservation about that is that it is no more aggressive for the group that
+    // we know is altered than for all the others, or perhaps, more the point with it's parents & groups that use it in
+    // their criteria.
     CRM_Contact_BAO_GroupContactCache::remove();
 
     CRM_Utils_Hook::post($op, 'GroupContact', $groupId, $contactIds);
index 8cbea8e0e82aad7b15789a7b6a210ee854df8476..febcee0e9781d751af242bbaff06f15ebe047b17 100644 (file)
@@ -290,6 +290,12 @@ WHERE  id IN ( $groupIDs )
    * cache date, i.e. the removal is not done if the group was recently
    * loaded into the cache.
    *
+   * In fact it turned out there is little overlap between the code when group is passed in
+   * and group is not so it makes more sense as separate functions.
+   *
+   * @todo remove last call to this function from outside the class then make function protected,
+   * enforce groupID as an array & remove non group handling.
+   *
    * @param int $groupID
    *   the groupID to delete cache entries, NULL for all groups.
    * @param bool $onceOnly
@@ -411,12 +417,23 @@ WHERE  id = %1
    * clear.
    */
   protected static function refreshCaches() {
-    if (self::isRefreshAlreadyInitiated()) {
+    try {
+      $lock = self::getLockForRefresh();
+    }
+    catch (CRM_Core_Exception $e) {
+      // Someone else is kindly doing the refresh for us right now.
       return;
     }
-
     $params = array(1 => array(self::getCacheInvalidDateTime(), 'String'));
-
+    // @todo this is consistent with previous behaviour but as the first query could take several seconds the second
+    // could become inaccurate. It seems to make more sense to fetch them first & delete from an array (which would
+    // also reduce joins). If we do this we should also consider how best to iterate the groups. If we do them one at
+    // a time we could call a hook, allowing people to manage the frequency on their groups, or possibly custom searches
+    // might do that too. However, for 2000 groups that's 2000 iterations. If we do all once we potentially create a
+    // slow query. It's worth noting the speed issue generally relates to the size of the group but if one slow group
+    // is in a query with 500 fast ones all 500 get locked. One approach might be to calculate group size or the
+    // number of groups & then process all at once or many query runs depending on what is found. Of course those
+    // preliminary queries would need speed testing.
     CRM_Core_DAO::executeQuery(
       "
         DELETE gc
@@ -427,15 +444,18 @@ WHERE  id = %1
       $params
     );
 
+    // Clear these out without resetting them because we are not building caches here, only clearing them,
+    // so the state is 'as if they had never been built'.
     CRM_Core_DAO::executeQuery(
       "
         UPDATE civicrm_group g
-        SET    cache_date = null,
-        refresh_date = NOW()
+        SET    cache_date = NULL,
+        refresh_date = NULL
         WHERE  g.cache_date <= %1
       ",
       $params
     );
+    $lock->release();
   }
 
   /**
@@ -446,16 +466,24 @@ WHERE  id = %1
    *   2) a mysql lock. This works fine as long as CiviMail is not running, or if mysql is version 5.7+
    *
    * Where these 2 locks fail we get 2 processes running at the same time, but we have at least minimised that.
+   *
+   * @return \Civi\Core\Lock\LockInterface
+   * @throws \CRM_Core_Exception
    */
-  protected static function isRefreshAlreadyInitiated() {
-    static $invoked = FALSE;
-    if ($invoked) {
-      return TRUE;
+  protected static function getLockForRefresh() {
+    if (!isset(Civi::$statics[__CLASS__])) {
+      Civi::$statics[__CLASS__] = array('is_refresh_init' => FALSE);
+    }
+
+    if (Civi::$statics[__CLASS__]['is_refresh_init']) {
+      throw new CRM_Core_Exception('A refresh has already run in this process');
     }
     $lock = Civi::lockManager()->acquire('data.core.group.refresh');
-    if (!$lock->isAcquired()) {
-      return TRUE;
+    if ($lock->isAcquired()) {
+      Civi::$statics[__CLASS__]['is_refresh_init'] = TRUE;
+      return $lock;
     }
+    throw new CRM_Core_Exception('Mysql lock unavailable');
   }
 
   /**
@@ -463,11 +491,9 @@ WHERE  id = %1
    *
    * Sites that do not run the smart group clearing cron job should refresh the caches under an opportunistic mode, akin
    * to a poor man's cron. The user session will be forced to wait on this so it is less desirable.
-   *
-   * @return bool
    */
   public static function opportunisticCacheRefresh() {
-    if (Civi::settings()->get('contact_smart_group_display') == 'opportunistic') {
+    if (Civi::settings()->get('smart_group_cache_refresh_mode') == 'opportunistic') {
       self::refreshCaches();
     }
   }
@@ -476,8 +502,6 @@ WHERE  id = %1
    * Do a forced cache refresh.
    *
    * This function is appropriate to be called by system jobs & non-user sessions.
-   *
-   * @return bool
    */
   public static function deterministicCacheRefresh() {
     if (self::smartGroupCacheTimeout() == 0) {
index 24feb13a81e15cc04225fa1a7b4bbf4e31fab7af..187d5fdbb6aa9f10fe7bb4553478db9eff8ab5db 100644 (file)
@@ -309,8 +309,7 @@ class CRM_Contact_Form_CustomData extends CRM_Core_Form {
       $this->ajaxResponse += CRM_Contact_Form_Inline::renderFooter($this->_tableID);
     }
 
-    // reset the group contact cache for this group
-    CRM_Contact_BAO_GroupContactCache::remove();
+    CRM_Contact_BAO_GroupContactCache::opportunisticCacheRefresh();
   }
 
 }
index 4570b75cc813e354a3de9a12645c20a11061aa34..a21e5e14971e6a1ff80d6e12af5eafb1d6cded57 100644 (file)
@@ -97,8 +97,7 @@ class CRM_Contact_Form_Inline_CustomData extends CRM_Contact_Form_Inline {
 
     $this->log();
 
-    // reset the group contact cache for this group
-    CRM_Contact_BAO_GroupContactCache::remove();
+    CRM_Contact_BAO_GroupContactCache::opportunisticCacheRefresh();
 
     $this->response();
   }
index c4a27de65b7b68d43cb8f78cf167b2c2f05159f1..00aa2051ee87203f658abd2418d83bf88439ee08 100644 (file)
@@ -294,8 +294,7 @@ class CRM_Contact_Page_AJAX {
       echo CRM_Contact_BAO_Contact::getCountComponent('custom_' . $customGroupID, $contactId);
     }
 
-    // reset the group contact cache for this group
-    CRM_Contact_BAO_GroupContactCache::remove();
+    CRM_Contact_BAO_GroupContactCache::opportunisticCacheRefresh();
     CRM_Utils_System::civiExit();
   }
 
index 1384065471024b7749c110931699d0706e35f26e..94a3388526e1761b67923cea09ad36b1c35faaf0 100644 (file)
@@ -212,8 +212,7 @@ class CRM_Contribute_BAO_Contribution extends CRM_Contribute_DAO_Contribution {
       );
     }
 
-    // reset the group contact cache for this group
-    CRM_Contact_BAO_GroupContactCache::remove();
+    CRM_Contact_BAO_GroupContactCache::opportunisticCacheRefresh();
 
     if ($contributionID) {
       CRM_Utils_Hook::post('edit', 'Contribution', $contribution->id, $contribution);
index 10182decce834322f7d99e17662b3f81e5d732bf..f3bed639a791adf4b4a10daa56ecef4db0b1921f 100644 (file)
@@ -168,9 +168,7 @@ class CRM_Core_BAO_EntityTag extends CRM_Core_DAO_EntityTag {
     $object = array($entityIdsAdded, $entityTable);
     CRM_Utils_Hook::post('create', 'EntityTag', $tagId, $object);
 
-    // reset the group contact cache for all groups
-    // if tags are being used in a smart group
-    CRM_Contact_BAO_GroupContactCache::remove();
+    CRM_Contact_BAO_GroupContactCache::opportunisticCacheRefresh();
 
     return array(count($entityIds), $numEntitiesAdded, $numEntitiesNotAdded);
   }
@@ -244,9 +242,7 @@ class CRM_Core_BAO_EntityTag extends CRM_Core_DAO_EntityTag {
     $object = array($entityIdsRemoved, $entityTable);
     CRM_Utils_Hook::post('delete', 'EntityTag', $tagId, $object);
 
-    // reset the group contact cache for all groups
-    // if tags are being used in a smart group
-    CRM_Contact_BAO_GroupContactCache::remove();
+    CRM_Contact_BAO_GroupContactCache::opportunisticCacheRefresh();
 
     return array(count($entityIds), $numEntitiesRemoved, $numEntitiesNotRemoved);
   }
index 60ccb75cd732c849d4262c4d2636047ac998d29c..d0b2b0f5d793036da906f0615df056a81e907319 100644 (file)
@@ -138,8 +138,7 @@ class CRM_Event_BAO_Participant extends CRM_Event_DAO_Participant {
 
     $session = CRM_Core_Session::singleton();
 
-    // reset the group contact cache for this group
-    CRM_Contact_BAO_GroupContactCache::remove();
+    CRM_Contact_BAO_GroupContactCache::opportunisticCacheRefresh();
 
     if (!empty($params['id'])) {
       CRM_Utils_Hook::post('edit', 'Participant', $participantBAO->id, $participantBAO);
index 52e79207db5966aca39b0a21ed8bf43c290b3369..0e4333d5c7d1203e17a3f74b918890016e6614c9 100644 (file)
@@ -137,7 +137,7 @@ class CRM_Member_BAO_Membership extends CRM_Member_DAO_Membership {
     CRM_Member_BAO_MembershipLog::add($membershipLog, CRM_Core_DAO::$_nullArray);
 
     // reset the group contact cache since smart groups might be affected due to this
-    CRM_Contact_BAO_GroupContactCache::remove();
+    CRM_Contact_BAO_GroupContactCache::opportunisticCacheRefresh();
 
     if ($id) {
       if ($membership->status_id != $oldStatus) {
index c4be97e4a39393e8f33afab456322e931b7481e7..0da96b8ed321ad2ec7147d591e5040160a789191 100644 (file)
@@ -208,7 +208,7 @@ class CRM_Upgrade_Incremental_php_FourSeven extends CRM_Upgrade_Incremental_Base
   public function upgrade_4_7_8($rev) {
     $this->addTask(ts('Upgrade DB to %1: SQL', array(1 => $rev)), 'runSql', $rev);
     $this->addTask('Upgrade mailing foreign key constraints', 'upgradeMailingFKs');
-    $this->addSmartGroupRefreshOptions();
+    $this->addTask('Add Smartgroup refresh options', 'addSmartGroupRefreshOptions');
   }
 
   /*
@@ -723,6 +723,7 @@ FROM `civicrm_dashboard_contact` JOIN `civicrm_contact` WHERE civicrm_dashboard_
       'filter' => 1,
       'is_reserved' => 1,
     ));
+    return TRUE;
   }
 
 }