]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-20737 Improve performance of large SARIF imports
authorAlain Kermis <alain.kermis@sonarsource.com>
Thu, 30 Nov 2023 13:16:50 +0000 (14:16 +0100)
committersonartech <sonartech@sonarsource.com>
Fri, 1 Dec 2023 20:02:43 +0000 (20:02 +0000)
sonar-scanner-engine/src/main/java/org/sonar/scanner/externalissue/sarif/LocationMapper.java

index 129d4b61d93c3baef5dbcd1a072eb6a891ab2a71..d41de202f89dc99340f752325ed9b9a0f82357bd 100644 (file)
 package org.sonar.scanner.externalissue.sarif;
 
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
 import java.io.File;
 import java.io.IOException;
 import java.net.URI;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.Optional;
+import java.util.concurrent.TimeUnit;
 import javax.annotation.CheckForNull;
 import javax.annotation.Nullable;
+import org.jetbrains.annotations.NotNull;
 import org.sonar.api.batch.fs.InputFile;
 import org.sonar.api.batch.fs.internal.predicates.AbstractFilePredicate;
 import org.sonar.api.batch.sensor.SensorContext;
@@ -43,10 +48,18 @@ import static org.sonar.api.utils.Preconditions.checkArgument;
 
 @ScannerSide
 public class LocationMapper {
+  private static final int CACHE_SIZE = 500;
+  private static final int CACHE_EXPIRY = 60;
 
   private final SensorContext sensorContext;
   private final RegionMapper regionMapper;
 
+  LoadingCache<String, Optional<InputFile>> inputFileCache = CacheBuilder.newBuilder()
+    .maximumSize(CACHE_SIZE)
+    .expireAfterAccess(CACHE_EXPIRY, TimeUnit.SECONDS)
+    .concurrencyLevel(Runtime.getRuntime().availableProcessors())
+    .build(getCacheLoader());
+
   LocationMapper(SensorContext sensorContext, RegionMapper regionMapper) {
     this.sensorContext = sensorContext;
     this.regionMapper = regionMapper;
@@ -64,12 +77,13 @@ public class LocationMapper {
     PhysicalLocation physicalLocation = location.getPhysicalLocation();
 
     String fileUri = getFileUriOrThrow(location);
-    InputFile file = findFile(sensorContext, fileUri);
-    if (file == null) {
+    Optional<InputFile> file = findFile(fileUri);
+    if (file.isEmpty()) {
       return null;
     }
-    newIssueLocation.on(file);
-    regionMapper.mapRegion(physicalLocation.getRegion(), file).ifPresent(newIssueLocation::at);
+    InputFile inputFile = file.get();
+    newIssueLocation.on(inputFile);
+    regionMapper.mapRegion(physicalLocation.getRegion(), inputFile).ifPresent(newIssueLocation::at);
     return newIssueLocation;
   }
 
@@ -88,10 +102,23 @@ public class LocationMapper {
     return Optional.ofNullable(location).map(PhysicalLocation::getArtifactLocation).map(ArtifactLocation::getUri).isPresent();
   }
 
-  @CheckForNull
-  private static InputFile findFile(SensorContext context, String filePath) {
+  private Optional<InputFile> findFile(String filePath) {
+    return inputFileCache.getUnchecked(filePath);
+  }
+
+  private CacheLoader<String, Optional<InputFile>> getCacheLoader() {
+    return new CacheLoader<>() {
+      @NotNull
+      @Override
+      public Optional<InputFile> load(final String filePath) {
+        return computeInputFile(filePath);
+      }
+    };
+  }
+
+  private Optional<InputFile> computeInputFile(String key) {
     // we use a custom predicate (which is not optimized) because fileSystem().predicates().is() doesn't handle symlinks correctly
-    return context.fileSystem().inputFile(new IsPredicate(getFileFromAbsoluteUriOrPath(filePath).toPath()));
+    return Optional.ofNullable(sensorContext.fileSystem().inputFile(new LocationMapper.IsPredicate(getFileFromAbsoluteUriOrPath(key).toPath())));
   }
 
   private static File getFileFromAbsoluteUriOrPath(String filePath) {
@@ -120,4 +147,5 @@ public class LocationMapper {
       }
     }
   }
+
 }