/* * SonarQube * Copyright (C) 2009-2017 SonarSource SA * mailto:info 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.purge; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Lists; import java.util.List; import java.util.stream.Collectors; import org.sonar.db.DbSession; import static com.google.common.collect.FluentIterable.from; import static java.util.Arrays.asList; class PurgeCommands { private static final int MAX_SNAPSHOTS_PER_QUERY = 1000; private static final int MAX_RESOURCES_PER_QUERY = 1000; private final DbSession session; private final PurgeMapper purgeMapper; private final PurgeProfiler profiler; PurgeCommands(DbSession session, PurgeMapper purgeMapper, PurgeProfiler profiler) { this.session = session; this.purgeMapper = purgeMapper; this.profiler = profiler; } @VisibleForTesting PurgeCommands(DbSession session, PurgeProfiler profiler) { this(session, session.getMapper(PurgeMapper.class), profiler); } List<String> selectSnapshotUuids(PurgeSnapshotQuery query) { return purgeMapper.selectAnalysisIdsAndUuids(query).stream().map(IdUuidPair::getUuid).collect(Collectors.toList()); } List<IdUuidPair> selectSnapshotIdUuids(PurgeSnapshotQuery query) { return purgeMapper.selectAnalysisIdsAndUuids(query); } void deleteAnalyses(String rootUuid) { deleteAnalyses(purgeMapper.selectAnalysisIdsAndUuids(new PurgeSnapshotQuery().setComponentUuid(rootUuid))); } void deleteComponents(List<IdUuidPair> componentIdUuids) { List<List<Long>> componentIdPartitions = Lists.partition(IdUuidPairs.ids(componentIdUuids), MAX_RESOURCES_PER_QUERY); List<List<String>> componentUuidsPartitions = Lists.partition(IdUuidPairs.uuids(componentIdUuids), MAX_RESOURCES_PER_QUERY); // Note : do not merge the delete statements into a single loop of resource ids. It's // voluntarily grouped by tables in order to benefit from JDBC batch mode. // Batch requests can only relate to the same PreparedStatement. // possible missing optimization: filter requests according to resource scope profiler.start("deleteResourceLinks (project_links)"); componentUuidsPartitions.forEach(purgeMapper::deleteComponentLinks); session.commit(); profiler.stop(); profiler.start("deleteResourceProperties (properties)"); componentIdPartitions.forEach(purgeMapper::deleteComponentProperties); session.commit(); profiler.stop(); profiler.start("deleteResourceGroupRoles (group_roles)"); componentIdPartitions.forEach(purgeMapper::deleteComponentGroupRoles); session.commit(); profiler.stop(); profiler.start("deleteResourceUserRoles (user_roles)"); componentIdPartitions.forEach(purgeMapper::deleteComponentUserRoles); session.commit(); profiler.stop(); profiler.start("deleteResourceManualMeasures (manual_measures)"); componentUuidsPartitions.forEach(purgeMapper::deleteComponentManualMeasures); session.commit(); profiler.stop(); profiler.start("deleteComponentIssueChanges (issue_changes)"); componentUuidsPartitions.forEach(purgeMapper::deleteComponentIssueChanges); session.commit(); profiler.stop(); profiler.start("deleteComponentIssues (issues)"); componentUuidsPartitions.forEach(purgeMapper::deleteComponentIssues); session.commit(); profiler.stop(); profiler.start("deleteComponentEvents (events)"); componentUuidsPartitions.forEach(purgeMapper::deleteComponentEvents); session.commit(); profiler.stop(); profiler.start("deleteResource (projects)"); componentUuidsPartitions.forEach(purgeMapper::deleteComponents); session.commit(); profiler.stop(); profiler.start("deleteAuthors (authors)"); componentIdPartitions.forEach(purgeMapper::deleteAuthors); session.commit(); profiler.stop(); } public void deleteComponentMeasures(List<String> analysisUuids, List<String> componentUuids) { if (analysisUuids.isEmpty() || componentUuids.isEmpty()) { return; } List<List<String>> analysisUuidsPartitions = Lists.partition(analysisUuids, MAX_SNAPSHOTS_PER_QUERY); List<List<String>> componentUuidsPartitions = Lists.partition(componentUuids, MAX_RESOURCES_PER_QUERY); profiler.start("deleteComponentMeasures"); for (List<String> analysisUuidsPartition : analysisUuidsPartitions) { for (List<String> componentUuidsPartition : componentUuidsPartitions) { purgeMapper.deleteComponentMeasures(analysisUuidsPartition, componentUuidsPartition); } } session.commit(); profiler.stop(); } void deleteAnalyses(PurgeSnapshotQuery... queries) { List<IdUuidPair> snapshotIds = from(asList(queries)) .transformAndConcat(purgeMapper::selectAnalysisIdsAndUuids) .toList(); deleteAnalyses(snapshotIds); } @VisibleForTesting protected void deleteAnalyses(List<IdUuidPair> analysisIdUuids) { List<List<String>> analysisUuidsPartitions = Lists.partition(IdUuidPairs.uuids(analysisIdUuids), MAX_SNAPSHOTS_PER_QUERY); deleteAnalysisDuplications(analysisUuidsPartitions); profiler.start("deleteAnalyses (events)"); analysisUuidsPartitions.forEach(purgeMapper::deleteAnalysisEvents); session.commit(); profiler.stop(); profiler.start("deleteAnalyses (project_measures)"); analysisUuidsPartitions.forEach(purgeMapper::deleteAnalysisMeasures); session.commit(); profiler.stop(); profiler.start("deleteAnalyses (snapshots)"); analysisUuidsPartitions.forEach(purgeMapper::deleteAnalyses); session.commit(); profiler.stop(); } public void purgeAnalyses(List<IdUuidPair> analysisUuids) { List<List<String>> analysisUuidsPartitions = Lists.partition(IdUuidPairs.uuids(analysisUuids), MAX_SNAPSHOTS_PER_QUERY); deleteAnalysisDuplications(analysisUuidsPartitions); profiler.start("deleteSnapshotWastedMeasures (project_measures)"); List<Long> metricIdsWithoutHistoricalData = purgeMapper.selectMetricIdsWithoutHistoricalData(); analysisUuidsPartitions.stream() .forEach(analysisUuidsPartition -> purgeMapper.deleteAnalysisWastedMeasures(analysisUuidsPartition, metricIdsWithoutHistoricalData)); session.commit(); profiler.stop(); profiler.start("updatePurgeStatusToOne (snapshots)"); analysisUuidsPartitions.forEach(purgeMapper::updatePurgeStatusToOne); session.commit(); profiler.stop(); } private void deleteAnalysisDuplications(List<List<String>> snapshotUuidsPartitions) { profiler.start("deleteAnalysisDuplications (duplications_index)"); snapshotUuidsPartitions.forEach(purgeMapper::deleteAnalysisDuplications); session.commit(); profiler.stop(); } public void deleteFileSources(String rootUuid) { profiler.start("deleteFileSources (file_sources)"); purgeMapper.deleteFileSourcesByProjectUuid(rootUuid); session.commit(); profiler.stop(); } public void deleteCeActivity(String rootUuid) { profiler.start("deleteCeActivity (ce_activity)"); purgeMapper.deleteCeActivityByProjectUuid(rootUuid); session.commit(); profiler.stop(); } public void deleteCeQueue(String rootUuid) { profiler.start("deleteCeQueue (ce_queue)"); purgeMapper.deleteCeQueueByProjectUuid(rootUuid); session.commit(); profiler.stop(); } public void deleteWebhookDeliveries(String rootUuid) { profiler.start("deleteWebhookDeliveries (webhook_deliveries)"); purgeMapper.deleteWebhookDeliveriesByProjectUuid(rootUuid); session.commit(); profiler.stop(); } }