]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-21655 ensure issue indexing chunks are properly closed when iterating over... 10.4.1.88267
authorSteve Marion <steve.marion@sonarsource.com>
Wed, 21 Feb 2024 16:56:56 +0000 (17:56 +0100)
committersonartech <sonartech@sonarsource.com>
Fri, 23 Feb 2024 20:02:46 +0000 (20:02 +0000)
(cherry picked from commit 95e6a254696765e8984a7a13f951fc7e6baf1736)

server/sonar-server-common/src/it/java/org/sonar/server/issue/index/IssueIndexerIT.java
server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueIteratorForMultipleChunks.java
server/sonar-server-common/src/main/java/org/sonar/server/issue/index/IssueIteratorForSingleChunk.java

index da7fa07acc7c6d5611d3d93d93885dbd836d3750..f4e20951b5e62ecbc3ecd7d754e21df7533f7f0f 100644 (file)
@@ -19,6 +19,7 @@
  */
 package org.sonar.server.issue.index;
 
+import com.google.common.collect.Iterables;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Date;
@@ -27,15 +28,19 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.function.Predicate;
+import java.util.stream.Stream;
 import org.assertj.core.api.Assertions;
 import org.elasticsearch.search.SearchHit;
 import org.junit.Rule;
 import org.junit.Test;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
 import org.slf4j.event.Level;
 import org.sonar.api.issue.impact.Severity;
 import org.sonar.api.issue.impact.SoftwareQuality;
 import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.testfixtures.log.LogTester;
+import org.sonar.db.DatabaseUtils;
 import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
 import org.sonar.db.component.BranchDto;
@@ -64,6 +69,7 @@ import static java.util.Collections.emptyList;
 import static java.util.Collections.emptySet;
 import static java.util.Collections.singletonList;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.assertj.core.groups.Tuple.tuple;
 import static org.sonar.db.component.ComponentTesting.newFileDto;
@@ -88,6 +94,51 @@ public class IssueIndexerIT {
 
   private final IssueIndexer underTest = new IssueIndexer(es.client(), db.getDbClient(), new IssueIteratorFactory(db.getDbClient()), null);
 
+  @Test
+  public void whenMultipleChunks_allShouldBeClosed() {
+    RuleDto rule = db.rules().insert();
+    ProjectData projectData = db.components().insertPrivateProject();
+    ComponentDto project = projectData.getMainBranchComponent();
+    ComponentDto file = db.components().insertComponent(newFileDto(project)
+      .setPath("src/main/java/Action.java"));
+
+    List<IssueDto> issuesToIndex = Stream.generate(() ->
+        db.issues().insert(rule, project, file,
+          t -> t.setResolution("FIXED")
+            .setStatus("RESOLVED")
+            .setSeverity("BLOCKER")
+            .setManualSeverity(false)
+            .setAssigneeUuid("uuid-of-guy1")
+            .setAuthorLogin("guy2")
+            .setChecksum("FFFFF")
+            .setGap(2D)
+            .setEffort(10L)
+            .setMessage(null)
+            .setLine(444)
+            .setRuleUuid(rule.getUuid())
+            .setTags(List.of("tag1", "tag2", "tag3"))
+            .setCreatedAt(1400000000000L)
+            .setUpdatedAt(1400000000000L)
+            .setIssueCreationDate(new Date(1115848800000L))
+            .setIssueUpdateDate(new Date(1356994800000L))
+            .setIssueCloseDate(null)
+            .setType(2)
+            .setCodeVariants(List.of("variant1", "variant2"))))
+      .limit(200)
+      .toList();
+
+
+    try (MockedStatic<DatabaseUtils> dbUtils = Mockito.mockStatic(DatabaseUtils.class)) {
+      dbUtils.when(() -> DatabaseUtils.toUniqueAndSortedPartitions(Mockito.any())).thenAnswer(invocation -> {
+        Collection<String> input = invocation.getArgument(0);
+        return Iterables.partition(List.copyOf(input), 10);
+      });
+      // should have no exception. if session aren't released properly, a timeout on acquiring a session should be thrown.
+      assertThatCode(() -> underTest.commitAndIndexIssues(db.getSession(), issuesToIndex))
+        .doesNotThrowAnyException();
+    }
+  }
+
   @Test
   public void getIndexTypes_shouldReturnTypeIssue() {
     assertThat(underTest.getIndexTypes()).containsExactly(TYPE_ISSUE);
index d84534e96763af01cd685e61c5f38f016746c39d..6b4f7812aeff473edbaeaf767d621db4ed426143 100644 (file)
@@ -48,7 +48,10 @@ public class IssueIteratorForMultipleChunks implements IssueIterator {
 
   @Override
   public IssueDoc next() {
-    if (currentChunk == null || !currentChunk.hasNext()) {
+    if (currentChunk == null) {
+      currentChunk = nextChunk();
+    } else if (!currentChunk.hasNext()) {
+      currentChunk.close();
       currentChunk = nextChunk();
     }
     return currentChunk.next();
index 8c4ee30f5a4c505fb77118a6554a5e808df09070..fad352719d533039f9a5d10ca60866f923f1c4a5 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.server.issue.index;
 
 import com.google.common.base.CharMatcher;
 import com.google.common.base.Splitter;
+import java.io.IOException;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -28,6 +29,8 @@ import java.util.Optional;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
 import org.apache.ibatis.cursor.Cursor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.sonar.api.resources.Qualifiers;
 import org.sonar.api.resources.Scopes;
 import org.sonar.api.rules.CleanCodeAttribute;
@@ -48,11 +51,13 @@ import static org.sonar.server.security.SecurityStandards.fromSecurityStandards;
  * the issues index
  */
 class IssueIteratorForSingleChunk implements IssueIterator {
+  private static final Logger LOG = LoggerFactory.getLogger(IssueIteratorForSingleChunk.class);
 
   static final Splitter STRING_LIST_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings();
 
   private final DbSession session;
 
+  private final Cursor<IndexedIssueDto> indexCursor;
   private final Iterator<IndexedIssueDto> iterator;
 
   IssueIteratorForSingleChunk(DbClient dbClient, @Nullable String branchUuid, @Nullable Collection<String> issueKeys) {
@@ -60,7 +65,7 @@ class IssueIteratorForSingleChunk implements IssueIterator {
       "Cannot search for more than " + DatabaseUtils.PARTITION_SIZE_FOR_ORACLE + " issue keys at once. Please provide the keys in smaller chunks.");
     this.session = dbClient.openSession(false);
     try {
-      Cursor<IndexedIssueDto> indexCursor = dbClient.issueDao().scrollIssuesForIndexation(session, branchUuid, issueKeys);
+      indexCursor = dbClient.issueDao().scrollIssuesForIndexation(session, branchUuid, issueKeys);
       iterator = indexCursor.iterator();
     } catch (Exception e) {
       session.close();
@@ -75,6 +80,7 @@ class IssueIteratorForSingleChunk implements IssueIterator {
 
   @Override
   public IssueDoc next() {
+
     return toIssueDoc(iterator.next());
   }
 
@@ -170,6 +176,11 @@ class IssueIteratorForSingleChunk implements IssueIterator {
 
   @Override
   public void close() {
+    try {
+      indexCursor.close();
+    } catch (IOException e) {
+      LOG.atWarn().setMessage("unable to close the cursor, this may lead to database connexion leak. error is : {}").addArgument(e).log();
+    }
     session.close();
   }
 }