@@ -20,13 +20,14 @@ | |||
package org.sonar.db.purge; | |||
import com.google.common.collect.ImmutableSet; | |||
import com.google.common.collect.Lists; | |||
import java.util.Collection; | |||
import java.util.Collections; | |||
import java.util.Date; | |||
import java.util.List; | |||
import java.util.Objects; | |||
import java.util.Optional; | |||
import java.util.Set; | |||
import java.util.function.Predicate; | |||
import java.util.stream.Stream; | |||
import org.sonar.api.utils.System2; | |||
import org.sonar.api.utils.log.Logger; | |||
import org.sonar.api.utils.log.Loggers; | |||
@@ -46,8 +47,8 @@ import static org.sonar.db.DatabaseUtils.executeLargeInputs; | |||
public class PurgeDao implements Dao { | |||
private static final Logger LOG = Loggers.get(PurgeDao.class); | |||
private static final String[] UNPROCESSED_STATUS = new String[] {"U"}; | |||
private static final ImmutableSet<String> QUALIFIERS_PROJECT_VIEW = ImmutableSet.of("TRK", "VW"); | |||
private static final ImmutableSet<String> QUALIFIERS_MODULE_SUBVIEW = ImmutableSet.of("BRC", "SVW"); | |||
private static final Set<String> QUALIFIERS_PROJECT_VIEW = ImmutableSet.of("TRK", "VW"); | |||
private static final Set<String> QUALIFIERS_MODULE_SUBVIEW = ImmutableSet.of("BRC", "SVW"); | |||
private static final String SCOPE_PROJECT = "PRJ"; | |||
private final ComponentDao componentDao; | |||
@@ -157,12 +158,38 @@ public class PurgeDao implements Dao { | |||
} | |||
public List<PurgeableAnalysisDto> selectPurgeableAnalyses(String componentUuid, DbSession session) { | |||
List<PurgeableAnalysisDto> result = Lists.newArrayList(); | |||
result.addAll(mapper(session).selectPurgeableAnalysesWithEvents(componentUuid)); | |||
result.addAll(mapper(session).selectPurgeableAnalysesWithoutEvents(componentUuid)); | |||
// sort by date | |||
Collections.sort(result); | |||
return result; | |||
PurgeMapper mapper = mapper(session); | |||
Stream<PurgeableAnalysisDto> allPurgeableAnalyses = Stream.concat( | |||
mapper.selectPurgeableAnalysesWithEvents(componentUuid).stream(), | |||
mapper.selectPurgeableAnalysesWithoutEvents(componentUuid).stream()); | |||
return allPurgeableAnalyses | |||
.filter(new ManualBaselineAnalysisFilter(mapper, componentUuid)) | |||
.sorted() | |||
.collect(MoreCollectors.toList()); | |||
} | |||
private static final class ManualBaselineAnalysisFilter implements Predicate<PurgeableAnalysisDto> { | |||
private static final String[] NO_BASELINE = {null}; | |||
private final PurgeMapper mapper; | |||
private final String componentUuid; | |||
private String[] manualBaselineAnalysisUuid; | |||
private ManualBaselineAnalysisFilter(PurgeMapper mapper, String componentUuid) { | |||
this.mapper = mapper; | |||
this.componentUuid = componentUuid; | |||
} | |||
@Override | |||
public boolean test(PurgeableAnalysisDto purgeableAnalysisDto) { | |||
if (manualBaselineAnalysisUuid == null) { | |||
manualBaselineAnalysisUuid = Optional.ofNullable(mapper.selectManualBaseline(componentUuid)) | |||
.map(t -> new String[] {t}) | |||
.orElse(NO_BASELINE); | |||
} | |||
return !Objects.equals(manualBaselineAnalysisUuid[0], purgeableAnalysisDto.getAnalysisUuid()); | |||
} | |||
} | |||
public void deleteBranch(DbSession session, String uuid) { | |||
@@ -257,4 +284,5 @@ public class PurgeDao implements Dao { | |||
private static PurgeMapper mapper(DbSession session) { | |||
return session.getMapper(PurgeMapper.class); | |||
} | |||
} |
@@ -20,6 +20,7 @@ | |||
package org.sonar.db.purge; | |||
import java.util.List; | |||
import javax.annotation.CheckForNull; | |||
import javax.annotation.Nullable; | |||
import org.apache.ibatis.annotations.Param; | |||
@@ -53,8 +54,8 @@ public interface PurgeMapper { | |||
void deleteAnalysisWastedMeasures(@Param("analysisUuids") List<String> analysisUuids, @Param("metricIds") List<Long> metricIds); | |||
/** | |||
* Purge status flag is used to not attempt to remove duplications & historical data of analysis of which we already | |||
* removed them. | |||
* Purge status flag is used to not attempt to remove duplications & historical data of analyses | |||
* for which we already removed them. | |||
*/ | |||
void updatePurgeStatusToOne(@Param("analysisUuids") List<String> analysisUuid); | |||
@@ -90,6 +91,9 @@ public interface PurgeMapper { | |||
List<String> selectStaleShortLivingBranchesAndPullRequests(@Param("projectUuid") String projectUuid, @Param("toDate") Long toDate); | |||
@CheckForNull | |||
String selectManualBaseline(@Param("projectUuid") String projectUuid); | |||
void deleteIssuesFromKeys(@Param("keys") List<String> keys); | |||
void deleteIssueChangesFromIssueKeys(@Param("issueKeys") List<String> issueKeys); |
@@ -43,7 +43,16 @@ | |||
and not exists(select e.id from events e where e.analysis_uuid=s.uuid) | |||
</select> | |||
<select id="selectStaleShortLivingBranchesAndPullRequests" parameterType="map" resultType="String"> | |||
<select id="selectManualBaseline" parameterType="String" resultType="String"> | |||
select | |||
manual_baseline_analysis_uuid | |||
from | |||
project_branches pb | |||
where | |||
pb.uuid=#{projectUuid,jdbcType=VARCHAR} | |||
</select> | |||
<select id="selectStaleShortLivingBranchesAndPullRequests" parameterType="map" resultType="String"> | |||
select | |||
pb.uuid | |||
from |
@@ -37,7 +37,6 @@ public class EventTesting { | |||
.setName(randomAlphanumeric(400)) | |||
.setDescription(null) | |||
.setCategory("Other") | |||
.setComponentUuid(analysis.getComponentUuid()) | |||
.setCreatedAt(System.currentTimeMillis()) | |||
.setDate(System.currentTimeMillis()); | |||
} |
@@ -58,6 +58,7 @@ import org.sonar.db.component.ComponentTesting; | |||
import org.sonar.db.component.SnapshotDto; | |||
import org.sonar.db.event.EventComponentChangeDto; | |||
import org.sonar.db.event.EventDto; | |||
import org.sonar.db.event.EventTesting; | |||
import org.sonar.db.issue.IssueDto; | |||
import org.sonar.db.measure.LiveMeasureDto; | |||
import org.sonar.db.measure.MeasureDto; | |||
@@ -85,6 +86,10 @@ import static org.sonar.db.component.ComponentTesting.newDirectory; | |||
import static org.sonar.db.component.ComponentTesting.newFileDto; | |||
import static org.sonar.db.component.ComponentTesting.newModuleDto; | |||
import static org.sonar.db.component.ComponentTesting.newProjectCopy; | |||
import static org.sonar.db.component.SnapshotDto.STATUS_PROCESSED; | |||
import static org.sonar.db.component.SnapshotDto.STATUS_UNPROCESSED; | |||
import static org.sonar.db.component.SnapshotTesting.newSnapshot; | |||
import static org.sonar.db.event.EventDto.CATEGORY_VERSION; | |||
import static org.sonar.db.webhook.WebhookDeliveryTesting.newDto; | |||
import static org.sonar.db.webhook.WebhookDeliveryTesting.selectAllDeliveryUuids; | |||
@@ -364,19 +369,106 @@ public class PurgeDaoTest { | |||
@Test | |||
public void selectPurgeableAnalyses() { | |||
db.prepareDbUnit(getClass(), "shouldSelectPurgeableAnalysis.xml"); | |||
List<PurgeableAnalysisDto> analyses = underTest.selectPurgeableAnalyses(PROJECT_UUID, dbSession); | |||
assertThat(analyses).hasSize(3); | |||
assertThat(getById(analyses, "u1").isLast()).isTrue(); | |||
assertThat(getById(analyses, "u1").hasEvents()).isFalse(); | |||
assertThat(getById(analyses, "u1").getVersion()).isNull(); | |||
assertThat(getById(analyses, "u4").isLast()).isFalse(); | |||
assertThat(getById(analyses, "u4").hasEvents()).isFalse(); | |||
assertThat(getById(analyses, "u4").getVersion()).isNull(); | |||
assertThat(getById(analyses, "u5").isLast()).isFalse(); | |||
assertThat(getById(analyses, "u5").hasEvents()).isTrue(); | |||
assertThat(getById(analyses, "u5").getVersion()).isEqualTo("V5"); | |||
SnapshotDto[] analyses = new SnapshotDto[] { | |||
newSnapshot() | |||
.setUuid("u1") | |||
.setComponentUuid(PROJECT_UUID) | |||
.setStatus(STATUS_PROCESSED) | |||
.setLast(true), | |||
// not processed -> exclude | |||
newSnapshot() | |||
.setUuid("u2") | |||
.setComponentUuid(PROJECT_UUID) | |||
.setStatus(STATUS_UNPROCESSED) | |||
.setLast(false), | |||
// on other resource -> exclude | |||
newSnapshot() | |||
.setUuid("u3") | |||
.setComponentUuid("uuid_222") | |||
.setStatus(STATUS_PROCESSED) | |||
.setLast(true), | |||
// without event -> select | |||
newSnapshot() | |||
.setUuid("u4") | |||
.setComponentUuid(PROJECT_UUID) | |||
.setStatus(STATUS_PROCESSED) | |||
.setLast(false), | |||
// with event -> select | |||
newSnapshot() | |||
.setUuid("u5") | |||
.setComponentUuid(PROJECT_UUID) | |||
.setStatus(STATUS_PROCESSED) | |||
.setLast(false) | |||
.setCodePeriodVersion("V5") | |||
}; | |||
db.components().insertSnapshots(analyses); | |||
db.events().insertEvent(EventTesting.newEvent(analyses[4]) | |||
.setName("V5") | |||
.setCategory(CATEGORY_VERSION)); | |||
List<PurgeableAnalysisDto> purgeableAnalyses = underTest.selectPurgeableAnalyses(PROJECT_UUID, dbSession); | |||
assertThat(purgeableAnalyses).hasSize(3); | |||
assertThat(getById(purgeableAnalyses, "u1").isLast()).isTrue(); | |||
assertThat(getById(purgeableAnalyses, "u1").hasEvents()).isFalse(); | |||
assertThat(getById(purgeableAnalyses, "u1").getVersion()).isNull(); | |||
assertThat(getById(purgeableAnalyses, "u4").isLast()).isFalse(); | |||
assertThat(getById(purgeableAnalyses, "u4").hasEvents()).isFalse(); | |||
assertThat(getById(purgeableAnalyses, "u4").getVersion()).isNull(); | |||
assertThat(getById(purgeableAnalyses, "u5").isLast()).isFalse(); | |||
assertThat(getById(purgeableAnalyses, "u5").hasEvents()).isTrue(); | |||
assertThat(getById(purgeableAnalyses, "u5").getVersion()).isEqualTo("V5"); | |||
} | |||
@Test | |||
public void selectPurgeableAnalyses_does_not_return_the_baseline() { | |||
ComponentDto project1 = db.components().insertMainBranch(db.getDefaultOrganization(), "master"); | |||
SnapshotDto analysis1 = db.components().insertSnapshot(newSnapshot() | |||
.setComponentUuid(project1.uuid()) | |||
.setStatus(STATUS_PROCESSED) | |||
.setLast(false)); | |||
dbClient.branchDao().updateManualBaseline(dbSession, project1.uuid(), analysis1.getUuid()); | |||
ComponentDto project2 = db.components().insertPrivateProject(); | |||
SnapshotDto analysis2 = db.components().insertSnapshot(newSnapshot() | |||
.setComponentUuid(project2.uuid()) | |||
.setStatus(STATUS_PROCESSED) | |||
.setLast(false)); | |||
db.components().insertProjectBranch(project2); | |||
assertThat(underTest.selectPurgeableAnalyses(project1.uuid(), dbSession)).isEmpty(); | |||
assertThat(underTest.selectPurgeableAnalyses(project2.uuid(), dbSession)) | |||
.extracting(PurgeableAnalysisDto::getAnalysisUuid) | |||
.containsOnly(analysis2.getUuid()); | |||
} | |||
@Test | |||
public void selectPurgeableAnalyses_does_not_return_the_baseline_of_specific_branch() { | |||
ComponentDto project = db.components().insertMainBranch(db.getDefaultOrganization(), "master"); | |||
SnapshotDto analysisProject = db.components().insertSnapshot(newSnapshot() | |||
.setComponentUuid(project.uuid()) | |||
.setStatus(STATUS_PROCESSED) | |||
.setLast(false)); | |||
dbClient.branchDao().updateManualBaseline(dbSession, project.uuid(), analysisProject.getUuid()); | |||
ComponentDto branch1 = db.components().insertProjectBranch(project); | |||
SnapshotDto analysisBranch1 = db.components().insertSnapshot(newSnapshot() | |||
.setComponentUuid(branch1.uuid()) | |||
.setStatus(STATUS_PROCESSED) | |||
.setLast(false)); | |||
ComponentDto branch2 = db.components().insertProjectBranch(project); | |||
SnapshotDto analysisBranch2 = db.components().insertSnapshot(newSnapshot() | |||
.setComponentUuid(branch2.uuid()) | |||
.setStatus(STATUS_PROCESSED) | |||
.setLast(false)); | |||
dbClient.branchDao().updateManualBaseline(dbSession, branch2.uuid(), analysisBranch2.getUuid()); | |||
dbSession.commit(); | |||
assertThat(underTest.selectPurgeableAnalyses(project.uuid(), dbSession)) | |||
.isEmpty(); | |||
assertThat(underTest.selectPurgeableAnalyses(branch1.uuid(), dbSession)) | |||
.extracting(PurgeableAnalysisDto::getAnalysisUuid) | |||
.containsOnly(analysisBranch1.getUuid()); | |||
assertThat(underTest.selectPurgeableAnalyses(branch2.uuid(), dbSession)) | |||
.isEmpty(); | |||
} | |||
@Test |
@@ -1,154 +0,0 @@ | |||
<dataset> | |||
<!-- last -> select --> | |||
<snapshots id="1" | |||
uuid="u1" | |||
component_uuid="P1" | |||
status="P" | |||
islast="[true]" | |||
purge_status="[null]" | |||
period1_mode="[null]" | |||
period1_param="[null]" | |||
period1_date="[null]" | |||
period2_mode="[null]" | |||
period2_param="[null]" | |||
period2_date="[null]" | |||
period3_mode="[null]" | |||
period3_param="[null]" | |||
period3_date="[null]" | |||
period4_mode="[null]" | |||
period4_param="[null]" | |||
period4_date="[null]" | |||
period5_mode="[null]" | |||
period5_param="[null]" | |||
period5_date="[null]" | |||
created_at="1228222680000" | |||
build_date="1228222680000" | |||
version="[null]" | |||
project_version="[null]" | |||
/> | |||
<!-- not processed -> exclude --> | |||
<snapshots id="2" | |||
uuid="u2" | |||
component_uuid="P1" | |||
status="U" | |||
islast="[false]" | |||
purge_status="[null]" | |||
period1_mode="[null]" | |||
period1_param="[null]" | |||
period1_date="[null]" | |||
period2_mode="[null]" | |||
period2_param="[null]" | |||
period2_date="[null]" | |||
period3_mode="[null]" | |||
period3_param="[null]" | |||
period3_date="[null]" | |||
period4_mode="[null]" | |||
period4_param="[null]" | |||
period4_date="[null]" | |||
period5_mode="[null]" | |||
period5_param="[null]" | |||
period5_date="[null]" | |||
created_at="1228222680000" | |||
build_date="1228222680000" | |||
version="[null]" | |||
project_version="[null]" | |||
/> | |||
<!-- on other resource -> exclude --> | |||
<snapshots id="3" | |||
uuid="u3" | |||
component_uuid="uuid_222" | |||
status="P" | |||
islast="[true]" | |||
purge_status="[null]" | |||
period1_mode="[null]" | |||
period1_param="[null]" | |||
period1_date="[null]" | |||
period2_mode="[null]" | |||
period2_param="[null]" | |||
period2_date="[null]" | |||
period3_mode="[null]" | |||
period3_param="[null]" | |||
period3_date="[null]" | |||
period4_mode="[null]" | |||
period4_param="[null]" | |||
period4_date="[null]" | |||
period5_mode="[null]" | |||
period5_param="[null]" | |||
period5_date="[null]" | |||
created_at="1228222680000" | |||
build_date="1228222680000" | |||
version="[null]" | |||
project_version="[null]" | |||
/> | |||
<!-- without event -> select --> | |||
<snapshots id="4" | |||
uuid="u4" | |||
component_uuid="P1" | |||
status="P" | |||
islast="[false]" | |||
purge_status="[null]" | |||
period1_mode="[null]" | |||
period1_param="[null]" | |||
period1_date="[null]" | |||
period2_mode="[null]" | |||
period2_param="[null]" | |||
period2_date="[null]" | |||
period3_mode="[null]" | |||
period3_param="[null]" | |||
period3_date="[null]" | |||
period4_mode="[null]" | |||
period4_param="[null]" | |||
period4_date="[null]" | |||
period5_mode="[null]" | |||
period5_param="[null]" | |||
period5_date="[null]" | |||
created_at="1228222680000" | |||
build_date="1228222680000" | |||
version="[null]" | |||
project_version="[null]" | |||
/> | |||
<!-- with event -> select --> | |||
<snapshots id="5" | |||
uuid="u5" | |||
component_uuid="P1" | |||
status="P" | |||
islast="[false]" | |||
purge_status="[null]" | |||
period1_mode="[null]" | |||
period1_param="[null]" | |||
period1_date="[null]" | |||
period2_mode="[null]" | |||
period2_param="[null]" | |||
period2_date="[null]" | |||
period3_mode="[null]" | |||
period3_param="[null]" | |||
period3_date="[null]" | |||
period4_mode="[null]" | |||
period4_param="[null]" | |||
period4_date="[null]" | |||
period5_mode="[null]" | |||
period5_param="[null]" | |||
period5_date="[null]" | |||
created_at="1228222680000" | |||
build_date="1228222680000" | |||
version="V5" | |||
project_version="[null]" | |||
/> | |||
<events id="2" | |||
uuid="P2" | |||
analysis_uuid="u5" | |||
component_uuid="1" | |||
category="Version" | |||
description="[null]" | |||
name="V5" | |||
event_date="1228222680000" | |||
created_at="1228222680000" | |||
event_data="[null]"/> | |||
</dataset> |