From 07394c91efabb111523f6f05cb90984026937ea3 Mon Sep 17 00:00:00 2001 From: Adrien Morais <31647811+adr-mo@users.noreply.github.com> Date: Tue, 19 Oct 2021 14:16:22 +0200 Subject: [PATCH] enh(resource-status): performance optimizations (#10323) * enh(resource-status): escape "_" character to use indexed search * enh(resources): use EXISTS statement instead of INNER JOIN --- .../Resource/Provider/HostProvider.php | 19 ++++--- .../Resource/Provider/MetaServiceProvider.php | 22 ++++--- .../Resource/Provider/ServiceProvider.php | 54 ++++++++++-------- .../Resource/ResourceRepositoryRDB.php | 57 ++++++++++++------- 4 files changed, 88 insertions(+), 64 deletions(-) diff --git a/src/Centreon/Infrastructure/Monitoring/Resource/Provider/HostProvider.php b/src/Centreon/Infrastructure/Monitoring/Resource/Provider/HostProvider.php index 93ae3afbba7..96dd0da0d18 100644 --- a/src/Centreon/Infrastructure/Monitoring/Resource/Provider/HostProvider.php +++ b/src/Centreon/Infrastructure/Monitoring/Resource/Provider/HostProvider.php @@ -69,9 +69,9 @@ public function prepareSubQueryWithAcl( StatementCollector $collector, array $accessGroupIds ): string { - $aclSubQuery = ' INNER JOIN `:dbstg`.`centreon_acl` AS host_acl ON host_acl.host_id = h.host_id + $aclSubQuery = ' EXISTS (SELECT 1 FROM `:dbstg`.`centreon_acl` AS host_acl WHERE host_acl.host_id = h.host_id AND host_acl.service_id IS NULL - AND host_acl.group_id IN (' . implode(',', $accessGroupIds) . ') '; + AND host_acl.group_id IN (' . implode(',', $accessGroupIds) . ') LIMIT 1) '; return $this->prepareSubQuery($filter, $collector, $aclSubQuery); } @@ -89,7 +89,7 @@ private function prepareSubQuery( StatementCollector $collector, ?string $aclSubQuery ): string { - $sql = "SELECT DISTINCT + $sql = "SELECT h.host_id AS `id`, 'host' AS `type`, h.name AS `name`, @@ -160,11 +160,6 @@ private function prepareSubQuery( AND host_cvl.service_id = 0 AND host_cvl.name = "CRITICALITY_LEVEL"'; - // set ACL limitations - if ($aclSubQuery !== null) { - $sql .= $aclSubQuery; - } - $hasWhereCondition = false; $this->sqlRequestTranslator->setConcordanceArray($this->hostConcordances); @@ -179,9 +174,15 @@ private function prepareSubQuery( $sql .= $searchRequest; } + // set ACL limitations + if ($aclSubQuery !== null) { + $sql .= ($hasWhereCondition ? ' AND ' : ' WHERE ') . $aclSubQuery; + $hasWhereCondition = true; + } + // show active hosts and aren't related to some module $sql .= ($hasWhereCondition ? ' AND ' : ' WHERE ') - . 'h.enabled = 1 AND h.name NOT LIKE "_Module_%"'; + . 'h.enabled = 1 AND h.name NOT LIKE "\_Module\_%"'; // apply the state filter to SQL query if ($filter->getStates() && !$filter->hasState(ResourceServiceInterface::STATE_ALL)) { diff --git a/src/Centreon/Infrastructure/Monitoring/Resource/Provider/MetaServiceProvider.php b/src/Centreon/Infrastructure/Monitoring/Resource/Provider/MetaServiceProvider.php index ca7a57a621a..a0abbd61336 100644 --- a/src/Centreon/Infrastructure/Monitoring/Resource/Provider/MetaServiceProvider.php +++ b/src/Centreon/Infrastructure/Monitoring/Resource/Provider/MetaServiceProvider.php @@ -69,9 +69,12 @@ public function prepareSubQueryWithAcl( StatementCollector $collector, array $accessGroupIds ): string { - $aclSubQuery = ' INNER JOIN `:dbstg`.`centreon_acl` AS service_acl ON service_acl.host_id = s.host_id - AND service_acl.service_id = s.service_id - AND service_acl.group_id IN (' . implode(',', $accessGroupIds) . ') '; + $aclSubQuery = ' EXISTS ( + SELECT 1 FROM `:dbstg`.`centreon_acl` AS service_acl + WHERE service_acl.host_id = s.host_id + AND service_acl.service_id = s.service_id + AND service_acl.group_id IN (' . implode(',', $accessGroupIds) . ') + LIMIT 1) '; return $this->prepareSubQuery($filter, $collector, $aclSubQuery); } @@ -89,7 +92,7 @@ private function prepareSubQuery( StatementCollector $collector, ?string $aclSubQuery ): string { - $sql = "SELECT DISTINCT + $sql = "SELECT SUBSTRING(s.description, 6) AS `id`, 'metaservice' AS `type`, s.display_name AS `name`, @@ -155,17 +158,18 @@ private function prepareSubQuery( FROM `:dbstg`.`services` AS s INNER JOIN `:dbstg`.`hosts` sh ON sh.host_id = s.host_id - AND sh.name LIKE '_Module_Meta%' + AND sh.name LIKE '\_Module\_Meta%' AND sh.enabled = 1"; + // show active services only + $sql .= ' WHERE s.enabled = 1 '; + + // set ACL limitations if ($aclSubQuery !== null) { - $sql .= $aclSubQuery; + $sql .= ' AND ' . $aclSubQuery; } - // show active services only - $sql .= ' WHERE s.enabled = 1'; - // apply the state filter to SQL query if ($filter->getStates() && !$filter->hasState(ResourceServiceInterface::STATE_ALL)) { $sqlState = []; diff --git a/src/Centreon/Infrastructure/Monitoring/Resource/Provider/ServiceProvider.php b/src/Centreon/Infrastructure/Monitoring/Resource/Provider/ServiceProvider.php index 4ab9f1d692b..a4b82d7c9f2 100644 --- a/src/Centreon/Infrastructure/Monitoring/Resource/Provider/ServiceProvider.php +++ b/src/Centreon/Infrastructure/Monitoring/Resource/Provider/ServiceProvider.php @@ -71,9 +71,12 @@ public function prepareSubQueryWithAcl( StatementCollector $collector, array $accessGroupIds ): string { - $aclSubQuery = ' INNER JOIN `:dbstg`.`centreon_acl` AS service_acl ON service_acl.host_id = s.host_id - AND service_acl.service_id = s.service_id - AND service_acl.group_id IN (' . implode(',', $accessGroupIds) . ') '; + $aclSubQuery = ' EXISTS ( + SELECT 1 FROM `:dbstg`.`centreon_acl` AS service_acl + WHERE service_acl.host_id = s.host_id + AND service_acl.service_id = s.service_id + AND service_acl.group_id IN (' . implode(',', $accessGroupIds) . ') + LIMIT 1) '; return $this->prepareSubQuery($filter, $collector, $aclSubQuery); } @@ -178,28 +181,6 @@ private function prepareSubQuery( AND service_cvl.service_id = s.service_id AND service_cvl.name = "CRITICALITY_LEVEL"'; - // set ACL limitations - if ($aclSubQuery !== null) { - $sql .= $aclSubQuery; - } - - // apply the service group filter to SQL query - if ($filter->getServicegroupIds()) { - $groupList = []; - - foreach ($filter->getServicegroupIds() as $index => $groupId) { - $key = ":serviceServicegroupId_{$index}"; - - $groupList[] = $key; - $collector->addValue($key, $groupId, \PDO::PARAM_INT); - } - - $sql .= ' INNER JOIN `:dbstg`.`services_servicegroups` AS ssg - ON ssg.host_id = s.host_id - AND ssg.service_id = s.service_id - AND ssg.servicegroup_id IN (' . implode(', ', $groupList) . ') '; - } - $hasWhereCondition = false; $this->sqlRequestTranslator->setConcordanceArray($this->serviceConcordances); @@ -218,6 +199,29 @@ private function prepareSubQuery( $sql .= ($hasWhereCondition ? ' AND ' : ' WHERE ') . 's.enabled = 1'; + // set ACL limitations + if ($aclSubQuery !== null) { + $sql .= ' AND ' . $aclSubQuery; + } + + // apply the service group filter to SQL query + if ($filter->getServicegroupNames()) { + $groupList = []; + + foreach ($filter->getServicegroupNames() as $index => $groupName) { + $key = ":serviceServicegroupName_{$index}"; + + $groupList[] = $key; + $collector->addValue($key, $groupName, \PDO::PARAM_STR); + } + + $sql .= ' AND EXISTS (SELECT 1 FROM `:dbstg`.`services_servicegroups` AS ssg + WHERE ssg.host_id = s.host_id AND ssg.service_id = s.service_id + AND EXISTS (SELECT 1 FROM `:dbstg`.`servicegroups` AS sg + WHERE ssg.servicegroup_id = sg.servicegroup_id + AND sg.id = ssg.id AND sg.name IN (' . implode(', ', $groupList) . ') LIMIT 1) LIMIT 1)'; + } + // apply the state filter to SQL query if ($filter->getStates() && !$filter->hasState(ResourceServiceInterface::STATE_ALL)) { $sqlState = []; diff --git a/src/Centreon/Infrastructure/Monitoring/Resource/ResourceRepositoryRDB.php b/src/Centreon/Infrastructure/Monitoring/Resource/ResourceRepositoryRDB.php index 49ec109e7c2..a6384d3e027 100644 --- a/src/Centreon/Infrastructure/Monitoring/Resource/ResourceRepositoryRDB.php +++ b/src/Centreon/Infrastructure/Monitoring/Resource/ResourceRepositoryRDB.php @@ -68,7 +68,7 @@ final class ResourceRepositoryRDB extends AbstractRepositoryDRB implements Resou private $accessGroups = []; /** - * @var array Association of resource search parameters + * @var array Association of resource search parameters */ private $resourceConcordances = [ 'id' => 'resource.id', @@ -162,7 +162,7 @@ public function findResources(ResourceFilter $filter): array } $collector = new StatementCollector(); - $request = 'SELECT SQL_CALC_FOUND_ROWS DISTINCT ' + $request = 'SELECT SQL_CALC_FOUND_ROWS ' . 'resource.id, resource.type, resource.name, resource.alias, resource.fqdn, ' . 'resource.host_id, resource.service_id, ' . 'resource.status_code, resource.status_name, resource.status_severity_code, ' // status @@ -211,11 +211,26 @@ function ($accessGroup) { return []; } - $request .= implode('UNION ALL ', $subRequests); + $request .= implode(' UNION ALL ', $subRequests); unset($subRequests); $request .= ') AS `resource`'; + $hasWhereCondition = false; + + // Search + $this->sqlRequestTranslator->setConcordanceArray($this->resourceConcordances); + try { + $searchRequest = $this->sqlRequestTranslator->translateSearchParameterToSql(); + } catch (RequestParametersTranslatorException $ex) { + throw new RepositoryException($ex->getMessage(), 0, $ex); + } + + if ($searchRequest !== null) { + $hasWhereCondition = true; + $request .= $searchRequest; + } + // apply the host group filter to SQL query if ($filter->getHostgroupIds()) { $groupList = []; @@ -226,9 +241,17 @@ function ($accessGroup) { $collector->addValue($key, $groupId, \PDO::PARAM_INT); } - $request .= ' INNER JOIN `:dbstg`.`hosts_hostgroups` AS hhg - ON hhg.host_id = resource.host_id - AND hhg.hostgroup_id IN (' . implode(', ', $groupList) . ') '; + $request .= ($hasWhereCondition === false) ? ' WHERE ' : ' AND '; + $hasWhereCondition = true; + + $request .= ' EXISTS ( + SELECT 1 FROM `:dbstg`.`hosts_hostgroups` AS hhg + WHERE hhg.host_id = resource.host_id + AND EXISTS ( + SELECT 1 FROM `:dbstg`.`hostgroups` AS hg + WHERE hg.hostgroup_id = hhg.hostgroup_id AND hg.name IN (' . implode(', ', $groupList) . ') + LIMIT 1) + LIMIT 1) '; } /** @@ -236,25 +259,17 @@ function ($accessGroup) { * Then only resources with existing metrics referencing index_data services will be returned. */ if ($filter->getOnlyWithPerformanceData() === true) { - $request .= ' INNER JOIN `:dbstg`.index_data AS idata - ON idata.host_id = resource.parent_id + $request .= $hasWhereCondition ? ' AND ' : ' WHERE '; + $request .= ' EXISTS ( + SELECT 1 FROM `:dbstg`.index_data AS idata + WHERE idata.host_id = resource.parent_id AND idata.service_id = resource.id AND resource.type = "service" - INNER JOIN `:dbstg`.metrics AS m - ON m.index_id = idata.id - AND m.hidden = "0" '; - } - - // Search - $this->sqlRequestTranslator->setConcordanceArray($this->resourceConcordances); - try { - $searchRequest = $this->sqlRequestTranslator->translateSearchParameterToSql(); - } catch (RequestParametersTranslatorException $ex) { - throw new RepositoryException($ex->getMessage(), 0, $ex); + AND EXISTS (SELECT 1 FROM `:dbstg`.metrics AS m + WHERE m.index_id = idata.id + AND m.hidden = "0" LIMIT 1) LIMIT 1) '; } - $request .= $searchRequest !== null ? $searchRequest : ''; - foreach ($this->sqlRequestTranslator->getSearchValues() as $key => $data) { $collector->addValue($key, current($data), key($data)); }