From 35485c876e9929e256a953f1fe20e726e5bbc915 Mon Sep 17 00:00:00 2001 From: Teryk Bellahsene Date: Wed, 25 Jan 2017 16:39:07 +0100 Subject: SONAR-7305 Improve performances of WS api/measures/search_history --- .../server/measure/ws/SearchHistoryAction.java | 19 ++++--- .../server/measure/ws/SearchHistoryResult.java | 25 +++++--- .../main/java/org/sonar/db/measure/MeasureDao.java | 19 ++++--- .../java/org/sonar/db/measure/MeasureMapper.java | 3 +- .../org/sonar/db/measure/PastMeasureQuery.java | 66 ++++++++++++++++++++++ .../org/sonar/db/measure/MeasureMapper.xml | 14 +++-- .../java/org/sonar/db/measure/MeasureDaoTest.java | 27 ++++++--- 7 files changed, 137 insertions(+), 36 deletions(-) create mode 100644 sonar-db/src/main/java/org/sonar/db/measure/PastMeasureQuery.java diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/SearchHistoryAction.java b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/SearchHistoryAction.java index 8400bef04ab..ab2d4394762 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/SearchHistoryAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/SearchHistoryAction.java @@ -21,6 +21,7 @@ package org.sonar.server.measure.ws; import com.google.common.collect.Sets; +import java.util.Date; import java.util.List; import java.util.Set; import java.util.function.Function; @@ -39,6 +40,7 @@ import org.sonar.db.component.SnapshotQuery; import org.sonar.db.component.SnapshotQuery.SORT_FIELD; import org.sonar.db.component.SnapshotQuery.SORT_ORDER; import org.sonar.db.measure.MeasureDto; +import org.sonar.db.measure.PastMeasureQuery; import org.sonar.db.metric.MetricDto; import org.sonar.server.component.ComponentFinder; import org.sonar.server.user.UserSession; @@ -122,7 +124,7 @@ public class SearchHistoryAction implements MeasuresWsAction { .setComponent(component) .setAnalyses(searchAnalyses(dbSession, request, component)) .setMetrics(searchMetrics(dbSession, request)); - return result.setMeasures(searchMeasures(dbSession, component, result.getAnalyses(), result.getMetrics())); + return result.setMeasures(searchMeasures(dbSession, request, result)); } }; } @@ -133,12 +135,15 @@ public class SearchHistoryAction implements MeasuresWsAction { return component; } - private List searchMeasures(DbSession dbSession, ComponentDto component, List analyses, List metrics) { - return dbClient.measureDao().selectPastMeasures( - dbSession, - component.uuid(), - analyses.stream().map(SnapshotDto::getUuid).collect(Collectors.toList()), - metrics.stream().map(MetricDto::getId).collect(Collectors.toList())); + private List searchMeasures(DbSession dbSession, SearchHistoryRequest request, SearchHistoryResult result) { + Date from = parseStartingDateOrDateTime(request.getFrom()); + Date to = parseEndingDateOrDateTime(request.getTo()); + PastMeasureQuery dbQuery = new PastMeasureQuery( + result.getComponent().uuid(), + result.getMetrics().stream().map(MetricDto::getId).collect(Collectors.toList()), + from == null ? null : from.getTime(), + to == null ? null : (to.getTime() + 1_000L)); + return dbClient.measureDao().selectPastMeasures(dbSession, dbQuery); } private List searchAnalyses(DbSession dbSession, SearchHistoryRequest request, ComponentDto component) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/SearchHistoryResult.java b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/SearchHistoryResult.java index 3d23e3c4a3b..f2d3e52474f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/SearchHistoryResult.java +++ b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/SearchHistoryResult.java @@ -25,6 +25,8 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.Table; import java.util.ArrayList; import java.util.List; +import java.util.Set; +import org.sonar.core.util.stream.Collectors; import org.sonar.db.component.ComponentDto; import org.sonar.db.component.SnapshotDto; import org.sonar.db.measure.MeasureDto; @@ -35,7 +37,6 @@ import org.sonarqube.ws.client.measure.SearchHistoryRequest; import static java.util.Collections.emptyList; import static java.util.Objects.requireNonNull; import static org.sonar.api.utils.Paging.offset; -import static org.sonar.core.util.stream.Collectors.toList; import static org.sonar.db.metric.MetricDtoFunctions.isOptimizedForBestValue; import static org.sonar.server.measure.ws.MetricDtoWithBestValue.isEligibleForBestValue; @@ -51,8 +52,8 @@ class SearchHistoryResult { this.request = request; } - boolean hasResults() { - return !analyses.isEmpty(); + public ComponentDto getComponent() { + return requireNonNull(component); } SearchHistoryResult setComponent(ComponentDto component) { @@ -67,7 +68,8 @@ class SearchHistoryResult { SearchHistoryResult setAnalyses(List analyses) { this.paging = Common.Paging.newBuilder().setPageIndex(request.getPage()).setPageSize(request.getPageSize()).setTotal(analyses.size()).build(); - this.analyses = analyses.stream().skip(offset(request.getPage(), request.getPageSize())).limit(request.getPageSize()).collect(toList()); + this.analyses = analyses.stream().skip(offset(request.getPage(), request.getPageSize())).limit(request.getPageSize()).collect(Collectors.toList()); + return this; } @@ -85,9 +87,16 @@ class SearchHistoryResult { } SearchHistoryResult setMeasures(List measures) { - this.measures = ImmutableList.builder() - .addAll(measures) - .addAll(addBestValuesToMeasures(component, measures)).build(); + Set analysisUuids = analyses.stream().map(SnapshotDto::getUuid).collect(Collectors.toHashSet()); + ImmutableList.Builder measuresBuilder = ImmutableList.builder(); + List filteredMeasures = measures.stream() + .filter(measure -> analysisUuids.contains(measure.getAnalysisUuid())) + .collect(Collectors.toArrayList()); + measuresBuilder.addAll(filteredMeasures); + measuresBuilder.addAll(computeBestValues(filteredMeasures)); + + this.measures = measuresBuilder.build(); + return this; } @@ -98,7 +107,7 @@ class SearchHistoryResult { *
  • metric is optimized for best value
  • * */ - private List addBestValuesToMeasures(ComponentDto component, List measures) { + private List computeBestValues(List measures) { if (!isEligibleForBestValue().test(component)) { return emptyList(); } diff --git a/sonar-db/src/main/java/org/sonar/db/measure/MeasureDao.java b/sonar-db/src/main/java/org/sonar/db/measure/MeasureDao.java index 5539c79a73d..0727df94d04 100644 --- a/sonar-db/src/main/java/org/sonar/db/measure/MeasureDao.java +++ b/sonar-db/src/main/java/org/sonar/db/measure/MeasureDao.java @@ -120,14 +120,17 @@ public class MeasureDao implements Dao { ids -> mapper(dbSession).selectPastMeasuresOnSingleAnalysis(componentUuid, analysisUuid, ids)); } - public List selectPastMeasures(DbSession dbSession, String componentUuid, List analysisUuids, List metricIds) { - if (analysisUuids.isEmpty() || metricIds.isEmpty()) { - return emptyList(); - } - - return executeLargeInputs( - analysisUuids, - analyses -> mapper(dbSession).selectPastMeasuresOnSeveralAnalyses(componentUuid, analyses, metricIds)); + /** + * Select measures of: + * - one component + * - for a list of metrics + * - with analysis from a date (inclusive) - optional + * - with analysis to a date (exclusive) - optional + * + * If no constraints on dates, all the history is returned + */ + public List selectPastMeasures(DbSession dbSession, PastMeasureQuery query) { + return mapper(dbSession).selectPastMeasuresOnSeveralAnalyses(query); } /** diff --git a/sonar-db/src/main/java/org/sonar/db/measure/MeasureMapper.java b/sonar-db/src/main/java/org/sonar/db/measure/MeasureMapper.java index eb282770edc..05c548436ea 100644 --- a/sonar-db/src/main/java/org/sonar/db/measure/MeasureMapper.java +++ b/sonar-db/src/main/java/org/sonar/db/measure/MeasureMapper.java @@ -43,8 +43,7 @@ public interface MeasureMapper { List selectPastMeasuresOnSingleAnalysis(@Param("componentUuid") String componentUuid, @Param("analysisUuid") String analysisUuid, @Param("metricIds") List metricIds); - List selectPastMeasuresOnSeveralAnalyses(@Param("componentUuid") String componentUuid, @Param("analysisUuids") Collection analysisUuid, - @Param("metricIds") Collection metricIds); + List selectPastMeasuresOnSeveralAnalyses(@Param("query") PastMeasureQuery query); List selectProjectMeasuresOfDeveloper(@Param("developerId") long developerId, @Param("metricIds") Collection metricIds); diff --git a/sonar-db/src/main/java/org/sonar/db/measure/PastMeasureQuery.java b/sonar-db/src/main/java/org/sonar/db/measure/PastMeasureQuery.java new file mode 100644 index 00000000000..4db1a3f7168 --- /dev/null +++ b/sonar-db/src/main/java/org/sonar/db/measure/PastMeasureQuery.java @@ -0,0 +1,66 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.db.measure; + +import java.util.List; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import org.sonar.db.component.SnapshotDto; + +import static java.util.Objects.requireNonNull; + +public class PastMeasureQuery { + private final String componentUuid; + private final List metricIds; + private final Long from; + private final Long to; + private final String status; + + public PastMeasureQuery(String componentUuid, List metricIds, @Nullable Long from, @Nullable Long to) { + this.componentUuid = requireNonNull(componentUuid); + this.metricIds = requireNonNull(metricIds); + this.from = from; + this.to = to; + this.status = SnapshotDto.STATUS_PROCESSED; + } + + public String getComponentUuid() { + return componentUuid; + } + + public List getMetricIds() { + return metricIds; + } + + @CheckForNull + public Long getFrom() { + return from; + } + + @CheckForNull + public Long getTo() { + return to; + } + + public String getStatus() { + return status; + } +} diff --git a/sonar-db/src/main/resources/org/sonar/db/measure/MeasureMapper.xml b/sonar-db/src/main/resources/org/sonar/db/measure/MeasureMapper.xml index c56d91b0cb5..2a75e4a422c 100644 --- a/sonar-db/src/main/resources/org/sonar/db/measure/MeasureMapper.xml +++ b/sonar-db/src/main/resources/org/sonar/db/measure/MeasureMapper.xml @@ -142,12 +142,18 @@