When computing a report of a project, instead of indexing all changed issues, only updated issues from the project are now indexed.
package org.sonar.server.computation.step;
+import org.sonar.server.computation.component.TreeRootHolder;
import org.sonar.server.issue.index.IssueIndexer;
public class IndexIssuesStep implements ComputationStep {
private final IssueIndexer indexer;
+ private final TreeRootHolder treeRootHolder;
- public IndexIssuesStep(IssueIndexer indexer) {
+ public IndexIssuesStep(IssueIndexer indexer, TreeRootHolder treeRootHolder) {
this.indexer = indexer;
+ this.treeRootHolder = treeRootHolder;
}
@Override
public void execute() {
- indexer.index();
+ indexer.index(treeRootHolder.getRoot().getUuid());
}
@Override
package org.sonar.server.issue.index;
import java.util.Iterator;
+import javax.annotation.Nullable;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.index.query.FilterBuilders;
@Override
protected long doIndex(long lastUpdatedAt) {
- return doIndex(createBulkIndexer(false), lastUpdatedAt);
+ return doIndex(createBulkIndexer(false), lastUpdatedAt, null);
}
public void indexAll() {
- doIndex(createBulkIndexer(true), 0L);
+ doIndex(createBulkIndexer(true), 0L, null);
+ }
+
+ public void index(final String projectUuid) {
+ super.index(new IndexerTask() {
+ @Override
+ public long index(long lastUpdatedAt) {
+ return doIndex(createBulkIndexer(false), lastUpdatedAt, projectUuid);
+ }
+ });
}
/**
doIndex(createBulkIndexer(false), issues);
}
- private long doIndex(BulkIndexer bulk, long lastUpdatedAt) {
+ private long doIndex(BulkIndexer bulk, long lastUpdatedAt, @Nullable String projectUuid) {
DbSession dbSession = dbClient.openSession(false);
long maxDate;
try {
- IssueResultSetIterator rowIt = IssueResultSetIterator.create(dbClient, dbSession, lastUpdatedAt);
+ IssueResultSetIterator rowIt = IssueResultSetIterator.create(dbClient, dbSession, lastUpdatedAt, projectUuid);
maxDate = doIndex(bulk, rowIt);
rowIt.close();
return maxDate;
private static final String SQL_AFTER_DATE = SQL_ALL + " where i.updated_at>?";
+ private static final String PROJECT_FILTER = " AND root.uuid=?";
+
private static final Splitter TAGS_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings();
private static final Splitter MODULE_PATH_SPLITTER = Splitter.on('.').trimResults().omitEmptyStrings();
super(stmt);
}
- static IssueResultSetIterator create(DbClient dbClient, DbSession session, long afterDate) {
+ static IssueResultSetIterator create(DbClient dbClient, DbSession session, long afterDate, @Nullable String projectUuid) {
try {
String sql = afterDate > 0L ? SQL_AFTER_DATE : SQL_ALL;
+ sql += projectUuid == null ? "" : PROJECT_FILTER;
PreparedStatement stmt = dbClient.getMyBatis().newScrollingSelectStatement(session, sql);
+ int index = 1;
if (afterDate > 0L) {
- stmt.setLong(1, afterDate);
+ stmt.setLong(index, afterDate);
+ index++;
+ }
+ if (projectUuid != null) {
+ stmt.setString(index, projectUuid);
}
return new IssueResultSetIterator(stmt);
} catch (SQLException e) {
package org.sonar.server.computation.step;
+import org.junit.Rule;
import org.junit.Test;
+import org.sonar.server.computation.batch.TreeRootHolderRule;
+import org.sonar.server.computation.component.Component;
+import org.sonar.server.computation.component.ReportComponent;
import org.sonar.server.issue.index.IssueIndexer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import static org.sonar.server.computation.component.Component.Type.*;
+import static org.sonar.server.computation.component.ReportComponent.*;
public class IndexIssuesStepTest {
+ static String PROJECT_UUID = "PROJECT_UUID";
+
+ @Rule
+ public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule()
+ .setRoot(builder(PROJECT, 1).setUuid(PROJECT_UUID).setKey("PROJECT_KEY").build());
+
@Test
public void call_indexers() {
IssueIndexer issueIndexer = mock(IssueIndexer.class);
- IndexIssuesStep underTest = new IndexIssuesStep(issueIndexer);
+ IndexIssuesStep underTest = new IndexIssuesStep(issueIndexer, treeRootHolder);
underTest.execute();
- verify(issueIndexer).index();
+ verify(issueIndexer).index(PROJECT_UUID);
}
}
@Category(DbTests.class)
public class IssueIndexerTest {
- @Rule
- public DbTester dbTester = DbTester.create(System2.INSTANCE);
-
@ClassRule
public static EsTester esTester = new EsTester().addDefinitions(new IssueIndexDefinition(new Settings()));
+ @Rule
+ public DbTester dbTester = DbTester.create(System2.INSTANCE);
@Before
public void setUp() {
List<IssueDoc> docs = esTester.getDocuments("issues", "issue", IssueDoc.class);
assertThat(docs).hasSize(1);
IssueDoc doc = docs.get(0);
+ assertThat(doc.key()).isEqualTo("ABCDE");
assertThat(doc.projectUuid()).isEqualTo("THE_PROJECT");
assertThat(doc.componentUuid()).isEqualTo("THE_FILE");
assertThat(doc.moduleUuid()).isEqualTo("THE_PROJECT");
// technical date
assertThat(doc.getTechnicalUpdateDate().getTime()).isEqualTo(1550000000000L);
+ }
+
+ @Test
+ public void delete_project_remove_issue() {
+ dbTester.prepareDbUnit(getClass(), "index.xml");
+
+ IssueIndexer indexer = createIndexer();
+ indexer.index();
+
+ assertThat(esTester.countDocuments("issues", "issue")).isEqualTo(1);
- // delete project
indexer.deleteProject("THE_PROJECT", true);
assertThat(esTester.countDocuments("issues", "issue")).isZero();
}
+ @Test
+ public void index_issues_from_project() {
+ dbTester.prepareDbUnit(getClass(), "index_project.xml");
+
+ IssueIndexer indexer = createIndexer();
+ indexer.index("THE_PROJECT_1");
+
+ List<IssueDoc> docs = esTester.getDocuments("issues", "issue", IssueDoc.class);
+ assertThat(docs).hasSize(1);
+ assertThat(docs.get(0).key()).isEqualTo("ABCDE");
+ }
+
private IssueIndexer createIndexer() {
IssueIndexer indexer = new IssueIndexer(new DbClient(dbTester.database(), dbTester.myBatis()), esTester.client());
indexer.setEnabled(true);
import com.google.common.base.Function;
import com.google.common.collect.Maps;
import java.util.Map;
+import javax.annotation.Nonnull;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@Rule
public DbTester dbTester = DbTester.create(System2.INSTANCE);
+ private static Map<String, IssueDoc> issuesByKey(IssueResultSetIterator it) {
+ return Maps.uniqueIndex(it, new Function<IssueDoc, String>() {
+ @Override
+ public String apply(@Nonnull IssueDoc issue) {
+ return issue.key();
+ }
+ });
+ }
+
@Before
public void setUp() {
dbTester.truncateTables();
@Test
public void iterator_over_one_issue() {
dbTester.prepareDbUnit(getClass(), "one_issue.xml");
- IssueResultSetIterator it = IssueResultSetIterator.create(dbTester.getDbClient(), dbTester.getSession(), 0L);
+ IssueResultSetIterator it = IssueResultSetIterator.create(dbTester.getDbClient(), dbTester.getSession(), 0L, null);
Map<String, IssueDoc> issuesByKey = issuesByKey(it);
it.close();
@Test
public void iterator_over_issues() {
dbTester.prepareDbUnit(getClass(), "shared.xml");
- IssueResultSetIterator it = IssueResultSetIterator.create(dbTester.getDbClient(), dbTester.getSession(), 0L);
+ IssueResultSetIterator it = IssueResultSetIterator.create(dbTester.getDbClient(), dbTester.getSession(), 0L, null);
Map<String, IssueDoc> issuesByKey = issuesByKey(it);
it.close();
assertThat(issue.debt().toMinutes()).isGreaterThan(0L);
}
+ @Test
+ public void iterator_over_issue_from_project() {
+ dbTester.prepareDbUnit(getClass(), "many_projects.xml");
+ IssueResultSetIterator it = IssueResultSetIterator.create(dbTester.getDbClient(), dbTester.getSession(), 0L, "THE_PROJECT_1");
+ Map<String, IssueDoc> issuesByKey = issuesByKey(it);
+ it.close();
+
+ assertThat(issuesByKey).hasSize(2);
+ }
+
+ @Test
+ public void iterator_over_issue_from_project_and_date() {
+ dbTester.prepareDbUnit(getClass(), "many_projects.xml");
+ IssueResultSetIterator it = IssueResultSetIterator.create(dbTester.getDbClient(), dbTester.getSession(), 1_600_000_000_000L, "THE_PROJECT_1");
+ Map<String, IssueDoc> issuesByKey = issuesByKey(it);
+ it.close();
+
+ assertThat(issuesByKey).hasSize(1);
+ }
+
@Test
public void extract_directory_path() {
dbTester.prepareDbUnit(getClass(), "extract_directory_path.xml");
- IssueResultSetIterator it = IssueResultSetIterator.create(dbTester.getDbClient(), dbTester.getSession(), 0L);
+ IssueResultSetIterator it = IssueResultSetIterator.create(dbTester.getDbClient(), dbTester.getSession(), 0L, null);
Map<String, IssueDoc> issuesByKey = issuesByKey(it);
it.close();
@Test
public void extract_file_path() {
dbTester.prepareDbUnit(getClass(), "extract_file_path.xml");
- IssueResultSetIterator it = IssueResultSetIterator.create(dbTester.getDbClient(), dbTester.getSession(), 0L);
+ IssueResultSetIterator it = IssueResultSetIterator.create(dbTester.getDbClient(), dbTester.getSession(), 0L, null);
Map<String, IssueDoc> issuesByKey = issuesByKey(it);
it.close();
@Test
public void select_after_date() {
dbTester.prepareDbUnit(getClass(), "shared.xml");
- IssueResultSetIterator it = IssueResultSetIterator.create(dbTester.getDbClient(), dbTester.getSession(), 1_420_000_000_000L);
+ IssueResultSetIterator it = IssueResultSetIterator.create(dbTester.getDbClient(), dbTester.getSession(), 1_420_000_000_000L, null);
assertThat(it.hasNext()).isTrue();
IssueDoc issue = it.next();
assertThat(it.hasNext()).isFalse();
it.close();
}
-
- private static Map<String, IssueDoc> issuesByKey(IssueResultSetIterator it) {
- return Maps.uniqueIndex(it, new Function<IssueDoc, String>() {
- @Override
- public String apply(IssueDoc issue) {
- return issue.key();
- }
- });
- }
}
--- /dev/null
+<dataset>
+ <rules id="1" tags="[null]" system_tags="[null]" name="Avoid Cycles" plugin_rule_key="AvoidCycles"
+ plugin_config_key="[null]" plugin_name="squid"/>
+
+ <!-- Project 1 -->
+ <projects id="10" scope="PRJ" qualifier="TRK" kee="the_project_1" name="TheProject1"
+ uuid="THE_PROJECT_1" module_uuid="[null]" module_uuid_path="." path="[null]"/>
+ <projects id="11" scope="FIL" qualifier="FIL" kee="the_file_1" name="TheFile1"
+ uuid="THE_FILE_1" module_uuid="THE_PROJECT_1" module_uuid_path=".THE_PROJECT_1."
+ path="src/main/java/TheFile.java"/>
+
+ <issues id="1"
+ kee="ABCDE"
+ resolution="FIXED"
+ status="RESOLVED"
+ severity="BLOCKER"
+ manual_severity="[false]"
+ assignee="winner"
+ author_login="[null]"
+ checksum="FFFFF"
+ effort_to_fix="[null]"
+ technical_debt="[null]"
+ message="[null]"
+ line="444"
+ component_uuid="THE_FILE_1"
+ project_uuid="THE_PROJECT_1"
+ rule_id="1"
+ reporter="[null]"
+ issue_attributes="JIRA=http://jira.com"
+ action_plan_key="[null]"
+ created_at="1500000000000"
+ updated_at="1550000000000"
+ issue_creation_date="1115848800000"
+ issue_update_date="1368828000000"
+ issue_close_date="[null]"
+ locations="[null]"
+ />
+
+ <!-- Project 2 -->
+ <projects id="100" scope="PRJ" qualifier="TRK" kee="the_project_2" name="TheProject2"
+ uuid="THE_PROJECT_2" module_uuid="[null]" module_uuid_path="." path="[null]"/>
+ <projects id="111" scope="FIL" qualifier="FIL" kee="the_file_2" name="TheFile2"
+ uuid="THE_FILE_2" module_uuid="THE_PROJECT_2" module_uuid_path=".THE_PROJECT_2."
+ path="src/main/java/TheFile.java"/>
+
+ <issues id="10"
+ kee="EDCBA"
+ resolution="FIXED"
+ status="RESOLVED"
+ severity="BLOCKER"
+ manual_severity="[false]"
+ assignee="winner"
+ author_login="[null]"
+ checksum="FFFFF"
+ effort_to_fix="[null]"
+ technical_debt="[null]"
+ message="[null]"
+ line="444"
+ component_uuid="THE_FILE_2"
+ project_uuid="THE_PROJECT_2"
+ rule_id="1"
+ reporter="[null]"
+ issue_attributes="JIRA=http://jira.com"
+ action_plan_key="[null]"
+ created_at="1500000000000"
+ updated_at="1550000000000"
+ issue_creation_date="1115848800000"
+ issue_update_date="1368828000000"
+ issue_close_date="[null]"
+ locations="[null]"
+ />
+</dataset>
--- /dev/null
+<dataset>
+ <rules id="1" tags="[null]" system_tags="[null]" name="Avoid Cycles" plugin_rule_key="AvoidCycles"
+ plugin_config_key="[null]" plugin_name="squid"/>
+
+ <!-- Project 1 -->
+ <projects id="10" scope="PRJ" qualifier="TRK" kee="the_project_1" name="TheProject1"
+ uuid="THE_PROJECT_1" module_uuid="[null]" module_uuid_path="." path="[null]"/>
+ <projects id="11" scope="FIL" qualifier="FIL" kee="the_file_1" name="TheFile1"
+ uuid="THE_FILE_1" module_uuid="THE_PROJECT_1" module_uuid_path=".THE_PROJECT_1."
+ path="src/main/java/TheFile.java"/>
+
+ <issues id="1"
+ kee="ABCDE"
+ resolution="FIXED"
+ status="RESOLVED"
+ severity="BLOCKER"
+ manual_severity="[false]"
+ assignee="winner"
+ author_login="[null]"
+ checksum="FFFFF"
+ effort_to_fix="[null]"
+ technical_debt="[null]"
+ message="[null]"
+ line="444"
+ component_uuid="THE_FILE_1"
+ project_uuid="THE_PROJECT_1"
+ rule_id="1"
+ reporter="[null]"
+ issue_attributes="JIRA=http://jira.com"
+ action_plan_key="[null]"
+ created_at="1500000000000"
+ updated_at="1550000000000"
+ issue_creation_date="1115848800000"
+ issue_update_date="1368828000000"
+ issue_close_date="[null]"
+ locations="[null]"
+ />
+
+ <issues id="2"
+ kee="BCDEF"
+ resolution="FIXED"
+ status="RESOLVED"
+ severity="BLOCKER"
+ manual_severity="[false]"
+ assignee="winner"
+ author_login="[null]"
+ checksum="FFFFF"
+ effort_to_fix="[null]"
+ technical_debt="[null]"
+ message="[null]"
+ line="444"
+ component_uuid="THE_FILE_1"
+ project_uuid="THE_PROJECT_1"
+ rule_id="1"
+ reporter="[null]"
+ issue_attributes="JIRA=http://jira.com"
+ action_plan_key="[null]"
+ created_at="2000000000000"
+ updated_at="2550000000000"
+ issue_creation_date="2115848800000"
+ issue_update_date="2368828000000"
+ issue_close_date="[null]"
+ locations="[null]"
+ />
+
+ <!-- Project 2 -->
+ <projects id="100" scope="PRJ" qualifier="TRK" kee="the_project_2" name="TheProject2"
+ uuid="THE_PROJECT_2" module_uuid="[null]" module_uuid_path="." path="[null]"/>
+ <projects id="111" scope="FIL" qualifier="FIL" kee="the_file_2" name="TheFile2"
+ uuid="THE_FILE_2" module_uuid="THE_PROJECT_2" module_uuid_path=".THE_PROJECT_2."
+ path="src/main/java/TheFile.java"/>
+
+ <issues id="10"
+ kee="EDCBA"
+ resolution="FIXED"
+ status="RESOLVED"
+ severity="BLOCKER"
+ manual_severity="[false]"
+ assignee="winner"
+ author_login="[null]"
+ checksum="FFFFF"
+ effort_to_fix="[null]"
+ technical_debt="[null]"
+ message="[null]"
+ line="444"
+ component_uuid="THE_FILE_2"
+ project_uuid="THE_PROJECT_2"
+ rule_id="1"
+ reporter="[null]"
+ issue_attributes="JIRA=http://jira.com"
+ action_plan_key="[null]"
+ created_at="1500000000000"
+ updated_at="1550000000000"
+ issue_creation_date="1115848800000"
+ issue_update_date="1368828000000"
+ issue_close_date="[null]"
+ locations="[null]"
+ />
+</dataset>