There is one call left to the function called 'remove' which needs a little more consideration
}
}
- // 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);
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();
}
}
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);
CRM_Core_BAO_PrevNextCache::deleteItem();
}
- // reset the group contact cache for this group
- CRM_Contact_BAO_GroupContactCache::remove();
+ CRM_Contact_BAO_GroupContactCache::opportunisticCacheRefresh();
}
/**
// 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);
// 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);
* 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
* 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
$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();
}
/**
* 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');
}
/**
*
* 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();
}
}
* 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) {
$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();
}
}
$this->log();
- // reset the group contact cache for this group
- CRM_Contact_BAO_GroupContactCache::remove();
+ CRM_Contact_BAO_GroupContactCache::opportunisticCacheRefresh();
$this->response();
}
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();
}
);
}
- // 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);
$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);
}
$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);
}
$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);
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) {
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');
}
/*
'filter' => 1,
'is_reserved' => 1,
));
+ return TRUE;
}
}