From e5faffc44fd5d86f757ae1442a10f31427a08b7b Mon Sep 17 00:00:00 2001 From: Seamus Lee Date: Thu, 24 Oct 2019 08:45:31 +1100 Subject: [PATCH] Refactor ACL Contact Cache generation to be more performant --- CRM/Contact/BAO/Contact/Permission.php | 41 +++++++++++++++++++++----- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/CRM/Contact/BAO/Contact/Permission.php b/CRM/Contact/BAO/Contact/Permission.php index 80cbe37847..76918fe9de 100644 --- a/CRM/Contact/BAO/Contact/Permission.php +++ b/CRM/Contact/BAO/Contact/Permission.php @@ -236,20 +236,44 @@ AND $operationClause } } + // grab a lock so other processes don't compete and do the same query + $lock = Civi::lockManager()->acquire("data.core.aclcontact.{$userID}"); + if (!$lock->isAcquired()) { + // this can cause inconsistent results since we don't know if the other process + // will fill up the cache before our calling routine needs it. + // The default 3 second timeout should be enough for the other process to finish. + // However this routine does not return the status either, so basically + // its a "lets return and hope for the best" + return; + } + $tables = []; $whereTables = []; $permission = CRM_ACL_API::whereClause($type, $tables, $whereTables, $userID, FALSE, FALSE, TRUE); $from = CRM_Contact_BAO_Query::fromClause($whereTables); - CRM_Core_DAO::executeQuery(" -INSERT INTO civicrm_acl_contact_cache ( user_id, contact_id, operation ) -SELECT DISTINCT $userID as user_id, contact_a.id as contact_id, '{$operation}' as operation + /* Ends up something like this: + CREATE TEMPORARY TABLE civicrm_temp_acl_contact_cache1310 (SELECT DISTINCT 2960 as user_id, contact_a.id as contact_id, 'View' as operation + FROM civicrm_contact contact_a LEFT JOIN civicrm_group_contact_cache `civicrm_group_contact_cache-ACL` ON contact_a.id = `civicrm_group_contact_cache-ACL`.contact_id + LEFT JOIN civicrm_acl_contact_cache ac ON ac.user_id = 2960 AND ac.contact_id = contact_a.id AND ac.operation = 'View' + WHERE ( `civicrm_group_contact_cache-ACL`.group_id IN (14, 25, 46, 47, 48, 49, 50, 51) ) AND (contact_a.is_deleted = 0) + AND ac.user_id IS NULL*/ + /*$sql = "SELECT DISTINCT $userID as user_id, contact_a.id as contact_id, '{$operation}' as operation + $from + LEFT JOIN civicrm_acl_contact_cache ac ON ac.user_id = $userID AND ac.contact_id = contact_a.id AND ac.operation = '{$operation}' + WHERE $permission + AND ac.user_id IS NULL + ";*/ + $sql = "SELECT DISTINCT $userID as user_id, contact_a.id as contact_id, '{$operation}' as operation $from - LEFT JOIN civicrm_acl_contact_cache ac ON ac.user_id = $userID AND ac.contact_id = contact_a.id AND ac.operation = '{$operation}' -WHERE $permission -AND ac.user_id IS NULL -"); +WHERE $permission"; + $aclContactsTempTable = CRM_Utils_SQL_TempTable::build()->setCategory('aclccache')->setMemory(); + $tempTable = $aclContactsTempTable->getName(); + $aclContactsTempTable->createWithColumns('user_id int, contact_id int, operation varchar(255), UNIQUE UI_user_contact_operation (user_id,contact_id,operation)'); + CRM_Core_DAO::executeQuery("INSERT INTO {$tempTable} (user_id, contact_id, operation) {$sql}"); + CRM_Core_DAO::executeQuery("INSERT IGNORE INTO civicrm_acl_contact_cache (user_id, contact_id, operation) SELECT user_id, contact_id, operation FROM {$tempTable}"); + $aclContactsTempTable->drop(); // Add in a row for the logged in contact. Do not try to combine with the above query or an ugly OR will appear in // the permission clause. @@ -257,10 +281,11 @@ AND ac.user_id IS NULL ($type == CRM_Core_Permission::VIEW && CRM_Core_Permission::check('view my contact'))) { if (!CRM_Core_DAO::singleValueQuery(" SELECT count(*) FROM civicrm_acl_contact_cache WHERE user_id = %1 AND contact_id = %1 AND operation = '{$operation}' LIMIT 1", $queryParams)) { - CRM_Core_DAO::executeQuery("INSERT INTO civicrm_acl_contact_cache ( user_id, contact_id, operation ) VALUES(%1, %1, '{$operation}')", $queryParams); + CRM_Core_DAO::executeQuery("INSERT IGNORE INTO civicrm_acl_contact_cache ( user_id, contact_id, operation ) VALUES(%1, %1, '{$operation}')", $queryParams); } } Civi::$statics[__CLASS__]['processed'][$type][$userID] = 1; + $lock->release(); } /** -- 2.25.1