From 11c9d4f6e496041a0dee7b7f637890a51f2a8632 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Fri, 14 Jun 2013 18:00:08 +0200 Subject: [PATCH] SONAR-4321 Fix performance issue on Issues search SQL --- .../org/sonar/core/issue/db/IssueDao.java | 10 +-- .../org/sonar/core/issue/db/IssueMapper.java | 2 + .../org/sonar/core/resource/ResourceDao.java | 5 +- .../sonar/core/resource/ResourceMapper.java | 5 +- .../org/sonar/core/user/AuthorizationDao.java | 12 +-- .../org/sonar/core/issue/db/IssueMapper.xml | 43 ++++------ .../org/sonar/core/persistence/rows-h2.sql | 2 + .../org/sonar/core/persistence/schema-h2.ddl | 4 + .../sonar/core/resource/ResourceMapper.xml | 82 +++++++++---------- .../sonar/core/user/AuthorizationMapper.xml | 40 ++++----- .../sonar/core/resource/ResourceDaoTest.java | 40 +++++++-- .../core/resource/ResourceDaoTest/fixture.xml | 4 + ...nent_ids_for_secured_project_for_group.xml | 78 ++++++++++++++++++ ...onent_ids_for_secured_project_for_user.xml | 76 +++++++++++++++++ ..._add_index_to_snapshots_root_project_id.rb | 36 ++++++++ .../405_add_index_to_group_roles_role.rb | 36 ++++++++ 16 files changed, 357 insertions(+), 118 deletions(-) create mode 100644 sonar-core/src/test/resources/org/sonar/core/resource/ResourceDaoTest/should_find_children_component_ids_for_secured_project_for_group.xml create mode 100644 sonar-core/src/test/resources/org/sonar/core/resource/ResourceDaoTest/should_find_children_component_ids_for_secured_project_for_user.xml create mode 100644 sonar-server/src/main/webapp/WEB-INF/db/migrate/404_add_index_to_snapshots_root_project_id.rb create mode 100644 sonar-server/src/main/webapp/WEB-INF/db/migrate/405_add_index_to_group_roles_role.rb diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDao.java b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDao.java index 7b93ecfb41f..9cc7fa17b8b 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDao.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDao.java @@ -21,7 +21,6 @@ package org.sonar.core.issue.db; import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.Lists; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.SqlSession; import org.sonar.api.BatchComponent; @@ -35,10 +34,6 @@ import javax.annotation.Nullable; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.Map; - -import static com.google.common.collect.Lists.newArrayList; -import static com.google.common.collect.Maps.newHashMap; /** * @since 3.6 @@ -118,9 +113,6 @@ public class IssueDao implements BatchComponent, ServerComponent { if (ids.isEmpty()) { return Collections.emptyList(); } - Object idsPartition = Lists.partition(newArrayList(ids), 1000); - Map params = newHashMap(); - params.put("ids", idsPartition); - return session.selectList("org.sonar.core.issue.db.IssueMapper.selectByIds", params); + return session.getMapper(IssueMapper.class).selectByIds(ids); } } diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueMapper.java b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueMapper.java index 42c9b091340..18d9a4b98e5 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueMapper.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueMapper.java @@ -36,6 +36,8 @@ public interface IssueMapper { List selectIssues(@Param("query") IssueQuery query, @Param("componentRootKeys") Collection componentRootKeys, @Nullable @Param("userId") Integer userId, @Param("role") String role, @Param("maxResults") Integer maxResult); + List selectByIds(@Param("ids") Collection ids); + void insert(IssueDto issue); int update(IssueDto issue); diff --git a/sonar-core/src/main/java/org/sonar/core/resource/ResourceDao.java b/sonar-core/src/main/java/org/sonar/core/resource/ResourceDao.java index d87ee7c7014..bcb7514322d 100644 --- a/sonar-core/src/main/java/org/sonar/core/resource/ResourceDao.java +++ b/sonar-core/src/main/java/org/sonar/core/resource/ResourceDao.java @@ -26,6 +26,7 @@ import org.sonar.core.component.ComponentDto; import org.sonar.core.persistence.MyBatis; import javax.annotation.CheckForNull; +import javax.annotation.Nullable; import java.util.Collection; import java.util.Collections; @@ -159,13 +160,13 @@ public class ResourceDao { return resourceDto != null ? toComponent(resourceDto) : null; } - public List findChildrenComponentIds(Collection componentRootKeys){ + public List findAuthorizedChildrenComponentIds(Collection componentRootKeys, @Nullable Integer userId, String role){ if (componentRootKeys.isEmpty()) { return Collections.emptyList(); } SqlSession session = mybatis.openSession(); try { - return session.getMapper(ResourceMapper.class).selectChildrenComponentIds(componentRootKeys); + return session.getMapper(ResourceMapper.class).selectAuthorizedChildrenComponentIds(componentRootKeys, userId, role); } finally { MyBatis.closeQuietly(session); } diff --git a/sonar-core/src/main/java/org/sonar/core/resource/ResourceMapper.java b/sonar-core/src/main/java/org/sonar/core/resource/ResourceMapper.java index 05b68a913ee..ea89cbfac58 100644 --- a/sonar-core/src/main/java/org/sonar/core/resource/ResourceMapper.java +++ b/sonar-core/src/main/java/org/sonar/core/resource/ResourceMapper.java @@ -22,6 +22,8 @@ package org.sonar.core.resource; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.session.ResultHandler; +import javax.annotation.Nullable; + import java.util.Collection; import java.util.List; @@ -69,7 +71,8 @@ public interface ResourceMapper { /** * @since 3.6 */ - List selectChildrenComponentIds(@Param("componentRootKeys") Collection componentRootKeys); + List selectAuthorizedChildrenComponentIds(@Param("componentRootKeys") Collection componentRootKeys, + @Param("userId") @Nullable Integer userId, @Param("role") String role); void insert(ResourceDto resource); diff --git a/sonar-core/src/main/java/org/sonar/core/user/AuthorizationDao.java b/sonar-core/src/main/java/org/sonar/core/user/AuthorizationDao.java index 19be037eedd..90f0fcefb69 100644 --- a/sonar-core/src/main/java/org/sonar/core/user/AuthorizationDao.java +++ b/sonar-core/src/main/java/org/sonar/core/user/AuthorizationDao.java @@ -20,7 +20,6 @@ package org.sonar.core.user; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.apache.ibatis.session.SqlSession; import org.sonar.api.ServerComponent; @@ -28,9 +27,11 @@ import org.sonar.core.persistence.MyBatis; import javax.annotation.Nullable; -import java.util.*; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Set; -import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Maps.newHashMap; public class AuthorizationDao implements ServerComponent { @@ -57,13 +58,12 @@ public class AuthorizationDao implements ServerComponent { } String sql; Map params; - List> componentIdsPartition = Lists.partition(newArrayList(componentIds), 1000); if (userId == null) { sql = "keepAuthorizedComponentIdsForAnonymous"; - params = ImmutableMap.of("role", role, "componentIds", componentIdsPartition); + params = ImmutableMap.of("role", role, "componentIds", componentIds); } else { sql = "keepAuthorizedComponentIdsForUser"; - params = ImmutableMap.of("userId", userId, "role", role, "componentIds", componentIdsPartition); + params = ImmutableMap.of("userId", userId, "role", role, "componentIds", componentIds); } return Sets.newHashSet(session.selectList(sql, params)); diff --git a/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml b/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml index aff84d57572..2d11f53f828 100644 --- a/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml @@ -192,12 +192,7 @@ inner join projects p on p.id=i.component_id inner join projects root on root.id=i.root_component_id - and - - - #{element} - - + and i.id=#{element} @@ -225,13 +220,15 @@ from issues i - inner join () authorizedProjects on authorizedProjects.root_project_id=i.root_component_id - - inner join () components on components.project_id=i.component_id + + inner join () authorizedProjects on authorizedProjects.root_project_id=i.root_component_id + + + inner join () authorizedComponents on authorizedComponents.project_id=i.component_id - inner join projects project_component on project_component.id=i.component_id and project_component.enabled=${_true} and project_component.kee in - #{component} + inner join projects project_component on project_component.id=i.component_id and project_component.enabled=${_true} and + project_component.kee=#{component} @@ -240,23 +237,19 @@ - and i.kee in - #{key} + and i.kee=#{key} - and i.severity in - #{severity} + and i.severity=#{severity} - and i.status in - #{status} + and i.status=#{status} - and i.resolution in - #{resolution} + and i.resolution=#{resolution} @@ -268,13 +261,11 @@ - and i.reporter in - #{reporter} + and i.reporter=#{reporter} - and i.assignee in - #{assignee} + and i.assignee=#{assignee} @@ -294,9 +285,9 @@ - and i.action_plan_key in - - #{action_plan} + and + + i.action_plan_key=#{action_plan} diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql b/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql index 18020cb76a1..47f7780a0f8 100644 --- a/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql +++ b/sonar-core/src/main/resources/org/sonar/core/persistence/rows-h2.sql @@ -167,6 +167,8 @@ INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('400'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('401'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('402'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('403'); +INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('404'); +INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('405'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('410'); INSERT INTO SCHEMA_MIGRATIONS(VERSION) VALUES ('411'); diff --git a/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl b/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl index d32ca781b6b..99c8814b2c5 100644 --- a/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl +++ b/sonar-core/src/main/resources/org/sonar/core/persistence/schema-h2.ddl @@ -661,3 +661,7 @@ CREATE INDEX "ISSUE_FILTERS_NAME" ON "ISSUE_FILTERS" ("NAME"); CREATE INDEX "ISSUE_FILTER_FAVS_USER" ON "ISSUE_FILTER_FAVOURITES" ("USER_LOGIN"); CREATE UNIQUE INDEX "USERS_LOGIN" ON "USERS" ("LOGIN"); + +CREATE INDEX "SNAPSHOTS_ROOT_PROJECT_ID" ON "SNAPSHOTS" ("ROOT_PROJECT_ID"); + +CREATE INDEX "GROUP_ROLES_ROLE" ON "GROUP_ROLES" ("ROLE"); diff --git a/sonar-core/src/main/resources/org/sonar/core/resource/ResourceMapper.xml b/sonar-core/src/main/resources/org/sonar/core/resource/ResourceMapper.xml index bfaff59ad83..3a3955aca9a 100644 --- a/sonar-core/src/main/resources/org/sonar/core/resource/ResourceMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/resource/ResourceMapper.xml @@ -121,51 +121,49 @@ - + - - -- project ids of the children of a component which is not PRJ/TRK - select children.project_id - from snapshots children + + select s.project_id from snapshots s, ( + select project_components.id as id, + snapshot_components.id as sid, + root_snapshot_components.project_id as root_project_id, + root_snapshot_components.id as root_snapshot_id, + snapshot_components.path as path + from projects project_components + inner join snapshots snapshot_components on snapshot_components.project_id = project_components.id and snapshot_components.islast = ${_true} + inner join snapshots root_snapshot_components on root_snapshot_components.project_id = snapshot_components.root_project_id and root_snapshot_components.islast = ${_true} inner join ( - select rootSnapshot.id, rootSnapshot.root_snapshot_id, rootSnapshot.path - from snapshots rootSnapshot - inner join ( - - ) rootProject on rootProject.id = rootSnapshot.project_id - and rootSnapshot.islast=${_true} - and rootSnapshot.root_snapshot_id is not null - ) rootSnapshot on children.root_snapshot_id = rootSnapshot.root_snapshot_id - and - - - children.path LIKE rootSnapshot.path + CAST(rootSnapshot.id AS varchar(15)) + '.%' - - - children.path LIKE concat(rootSnapshot.path, rootSnapshot.id, '.%') - - - children.path LIKE rootSnapshot.path || rootSnapshot.id || '.%' - - - union - -- project ids of the children of a PRJ/TRK component - select children.project_id - from snapshots children - inner join ( - select rootSnapshot.id - from snapshots rootSnapshot - inner join ( - - ) rootProject on rootProject.id = rootSnapshot.project_id - and rootSnapshot.islast=${_true} - and rootSnapshot.root_snapshot_id is null - ) rootSnapshot on children.root_snapshot_id = rootSnapshot.id - union - -- project id of the component itself - + + ) authorized_projects on authorized_projects.root_project_id = root_snapshot_components.project_id + + and ( project_components.kee=#{componentRootKey}) + and project_components.enabled = ${_true} + + ) as authorized_input_components + + and s.root_project_id = authorized_input_components.root_project_id + and s.islast = ${_true} + and ( + (s.root_snapshot_id = authorized_input_components.root_snapshot_id + and + + + s.path LIKE authorized_input_components.path + CAST(authorized_input_components.sid AS varchar(15)) + '.%' + + + s.path LIKE concat(authorized_input_components.path, authorized_input_components.sid, '.%') + + + s.path LIKE authorized_input_components.path || authorized_input_components.sid || '.%' + + + ) + or (s.id = authorized_input_components.sid) + ) + diff --git a/sonar-core/src/main/resources/org/sonar/core/user/AuthorizationMapper.xml b/sonar-core/src/main/resources/org/sonar/core/user/AuthorizationMapper.xml index 2dfbded21f3..88677e1dd94 100644 --- a/sonar-core/src/main/resources/org/sonar/core/user/AuthorizationMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/user/AuthorizationMapper.xml @@ -10,12 +10,7 @@ gr.role=#{role} and (gr.group_id is null or gr.group_id in (select gu.group_id from groups_users gu where gu.user_id=#{userId})) and gr.resource_id = s.root_project_id and - - - #{element} - - - + project_id=#{element} and s.islast = ${_true} UNION SELECT s.project_id @@ -23,11 +18,7 @@ WHERE ur.role=#{role} and ur.user_id=#{userId} and - - - #{element} - - + project_id=#{element} and s.islast = ${_true} @@ -38,11 +29,7 @@ gr.role=#{role} and gr.group_id is null and gr.resource_id = s.root_project_id and - - - #{element} - - + project_id=#{element}