Skip to content

Commit 3c00367

Browse files
RollczivLuckyyy
andauthored
GH-799 Improve dependency loader performance with parallel downloading and relocation. (#802)
* Improve dependency loader performance. * Fix * Add Spotify Completable Futures version to Versions.kt --------- Co-authored-by: Martin <[email protected]>
1 parent fd277f6 commit 3c00367

File tree

10 files changed

+58
-40
lines changed

10 files changed

+58
-40
lines changed

buildSrc/src/main/kotlin/Versions.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ object Versions {
4040

4141
const val CAFFEINE = "3.1.8"
4242

43+
const val SPOTIFY_COMPLETABLE_FUTURES = "0.3.6"
44+
4345
// tests
4446
const val EXPRESSIBLE_JUNIT = "1.3.6"
4547
const val GROOVY_ALL = "3.0.21"

buildSrc/src/main/kotlin/eternalcore-repositories.gradle.kts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,8 @@ plugins {
55
repositories {
66
mavenCentral()
77
maven { url = uri("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") }
8-
maven { url = uri("https://repo.panda-lang.org/releases/") }
98
maven { url = uri("https://repo.papermc.io/repository/maven-public/") }
10-
maven { url = uri("https://repository.minecodes.pl/releases/") }
11-
maven { url = uri("https://repository.minecodes.pl/snapshots/") }
12-
maven { url = uri("https://repo.eternalcode.pl/snapshots/") }
9+
maven { url = uri("https://repo.panda-lang.org/releases/") }
1310
maven { url = uri("https://repo.eternalcode.pl/releases/") }
1411
maven { url = uri("https://repo.extendedclip.com/content/repositories/placeholderapi/") }
1512
}

eternalcore-plugin/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ eternalShadowCompiler {
3232
}
3333
}
3434

35+
dependencies {
36+
implementation("com.spotify:completable-futures:${Versions.SPOTIFY_COMPLETABLE_FUTURES}")
37+
}
38+
3539
tasks {
3640
runServer {
3741
minecraftVersion("1.20.4")

eternalcore-plugin/src/main/java/com/eternalcode/core/loader/dependency/Dependency.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public ResourceLocator toMavenJar(Repository repository, String classifier) {
2929
String url = String.format(
3030
JAR_MAVEN_FORMAT_WITH_CLASSIFIER,
3131
repository.url(),
32-
this.groupId,
32+
this.groupId.replace(".", "/"),
3333
this.artifactId,
3434
this.version,
3535
this.artifactId,

eternalcore-plugin/src/main/java/com/eternalcode/core/loader/dependency/DependencyCollector.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public boolean hasScannedDependency(Dependency dependency) {
1212
return this.fullScannedDependencies.containsKey(dependency.getGroupArtifactId());
1313
}
1414

15-
public void scannedDependency(Dependency dependency) {
15+
public void addScannedDependency(Dependency dependency) {
1616
Dependency current = this.fullScannedDependencies.get(dependency.getGroupArtifactId());
1717

1818
if (current == null) {
@@ -25,13 +25,13 @@ public void scannedDependency(Dependency dependency) {
2525
}
2626
}
2727

28-
public void scannedDependencies(Collection<Dependency> dependencies) {
28+
public void addScannedDependencies(Collection<Dependency> dependencies) {
2929
for (Dependency dependency : dependencies) {
30-
this.scannedDependency(dependency);
30+
this.addScannedDependency(dependency);
3131
}
3232
}
3333

34-
public Collection<Dependency> scannedDependencies() {
34+
public Collection<Dependency> getScannedDependencies() {
3535
return Collections.unmodifiableCollection(this.fullScannedDependencies.values());
3636
}
3737

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.eternalcode.core.loader.dependency;
2+
3+
import java.nio.file.Path;
4+
5+
public record DependencyLoadEntry(Dependency dependency, Path path) {
6+
}

eternalcore-plugin/src/main/java/com/eternalcode/core/loader/dependency/DependencyLoadResult.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22

33
import com.eternalcode.core.loader.classloader.IsolatedClassLoader;
44

5-
import java.nio.file.Path;
6-
import java.util.Collection;
75
import java.util.List;
86

9-
public record DependencyLoadResult(IsolatedClassLoader loader, Collection<Dependency> dependencies, List<Path> paths) {
7+
public record DependencyLoadResult(IsolatedClassLoader loader, List<DependencyLoadEntry> paths) {
108
}

eternalcore-plugin/src/main/java/com/eternalcode/core/loader/dependency/DependencyLoaderImpl.java

Lines changed: 37 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.eternalcode.core.loader.relocation.RelocationHandler;
99
import com.eternalcode.core.loader.repository.Repository;
1010

11+
import com.spotify.futures.CompletableFutures;
1112
import java.io.File;
1213
import java.io.IOException;
1314
import java.net.URL;
@@ -18,27 +19,34 @@
1819
import java.util.HashMap;
1920
import java.util.List;
2021
import java.util.Map;
22+
import java.util.concurrent.CompletableFuture;
23+
import java.util.concurrent.Executor;
24+
import java.util.concurrent.Executors;
25+
import java.util.concurrent.TimeUnit;
2126
import java.util.logging.Logger;
2227

2328
public class DependencyLoaderImpl implements DependencyLoader {
2429

2530
private static final String LOCAL_REPOSITORY_PATH = "localRepository";
2631

32+
private final Executor executor;
2733
private final Logger logger;
2834
private final DependencyDownloader downloadDependency;
2935
private final RelocationHandler relocationHandler;
3036
private final DependencyScanner pomXmlScanner;
37+
private final Repository localRepository;
3138

3239
private final Map<Dependency, Path> loaded = new HashMap<>();
3340

3441
public DependencyLoaderImpl(Logger logger, File dataFolder, List<Repository> repositories) {
3542
Path localRepositoryPath = this.setupCacheDirectory(dataFolder);
36-
Repository localRepository = Repository.localRepository(localRepositoryPath);
43+
this.localRepository = Repository.localRepository(localRepositoryPath);
3744

3845
List<Repository> allRepositories = new ArrayList<>();
3946
allRepositories.add(localRepository);
4047
allRepositories.addAll(repositories);
4148

49+
this.executor = Executors.newCachedThreadPool();
4250
this.logger = logger;
4351
this.pomXmlScanner = new PomXmlScanner(allRepositories, localRepository);
4452
this.downloadDependency = new DependencyDownloader(logger, localRepository, allRepositories);
@@ -64,39 +72,43 @@ public DependencyLoadResult load(IsolatedClassLoader loader, List<Dependency> de
6472
collector = this.pomXmlScanner.findAllChildren(collector, dependency);
6573
}
6674

67-
collector.scannedDependencies(dependencies);
68-
this.logger.info("Found " + collector.scannedDependencies().size() + " dependencies");
75+
collector.addScannedDependencies(dependencies);
76+
this.logger.info("Found " + collector.getScannedDependencies().size() + " dependencies");
6977

70-
List<Path> paths = new ArrayList<>();
78+
List<CompletableFuture<DependencyLoadEntry>> futures = new ArrayList<>();
7179

72-
for (Dependency dependency : collector.scannedDependencies()) {
73-
Path loaded = this.loaded.get(dependency);
80+
for (Dependency dependency : collector.getScannedDependencies()) {
81+
CompletableFuture<DependencyLoadEntry> pathToDependency = CompletableFuture.supplyAsync(() -> {
82+
Path loaded = this.loaded.get(dependency);
7483

75-
if (loaded != null) {
76-
// TODO: jeśli już wcześniej pobrano zależność to można zweryfikować czy relokacja jest poprawna (może jakiś plik z informacjami o relokacjach?)
77-
loader.addPath(loaded);
78-
paths.add(loaded);
79-
continue;
80-
}
84+
if (loaded != null) {
85+
return new DependencyLoadEntry(dependency, loaded);
86+
}
8187

82-
Path downloadedDependencyPath = this.downloadDependency.downloadDependency(dependency);
88+
Path downloadedDependencyPath = this.downloadDependency.downloadDependency(dependency);
8389

84-
if (this.relocationHandler == null) {
85-
this.loaded.put(dependency, downloadedDependencyPath);
86-
loader.addPath(downloadedDependencyPath);
87-
paths.add(downloadedDependencyPath);
88-
continue;
89-
}
90+
if (this.relocationHandler == null) {
91+
return new DependencyLoadEntry(dependency, downloadedDependencyPath);
92+
}
9093

91-
// TODO: to jest trochę blocking można to zrobić w parallel streamie
92-
Path relocatedDependency = this.relocationHandler.relocateDependency(downloadedDependencyPath, dependency, relocations);
94+
Path relocatedDependency = this.relocationHandler.relocateDependency(localRepository, downloadedDependencyPath, dependency, relocations);
9395

94-
this.loaded.put(dependency, relocatedDependency);
95-
loader.addPath(relocatedDependency);
96-
paths.add(relocatedDependency);
96+
return new DependencyLoadEntry(dependency, relocatedDependency);
97+
}, this.executor);
98+
99+
futures.add(pathToDependency);
100+
}
101+
102+
List<DependencyLoadEntry> dependencyLoadEntries = CompletableFutures.allAsList(futures)
103+
.orTimeout(60, TimeUnit.MINUTES)
104+
.join();
105+
106+
for (DependencyLoadEntry dependencyLoadEntry : dependencyLoadEntries) {
107+
loader.addPath(dependencyLoadEntry.path());
108+
this.loaded.put(dependencyLoadEntry.dependency(), dependencyLoadEntry.path());
97109
}
98110

99-
return new DependencyLoadResult(loader, collector.scannedDependencies(), paths);
111+
return new DependencyLoadResult(loader, dependencyLoadEntries);
100112
}
101113

102114
@Override

eternalcore-plugin/src/main/java/com/eternalcode/core/loader/pom/PomXmlScanner.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public DependencyCollector findAllChildren(DependencyCollector collector, Depend
6868
}
6969

7070
collector = this.findAllChildren(collector, firstChild);
71-
collector.scannedDependency(firstChild);
71+
collector.addScannedDependency(firstChild);
7272
}
7373

7474
break;

eternalcore-plugin/src/main/java/com/eternalcode/core/loader/relocation/RelocationHandler.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,11 @@ private RelocationHandler(IsolatedClassLoader classLoader, Constructor<?> jarRel
7979
this.jarRelocatorRunMethod = jarRelocatorRunMethod;
8080
}
8181

82-
public Path relocateDependency(Path dependencyPath, Dependency dependency, List<Relocation> relocations) {
82+
public Path relocateDependency(Repository localRepository, Path dependencyPath, Dependency dependency, List<Relocation> relocations) {
8383
if (relocations.isEmpty()) {
8484
return dependencyPath;
8585
}
8686

87-
Repository localRepository = Repository.localRepository(dependencyPath.getParent());
8887
Path relocatedJar = dependency.toMavenJar(localRepository, "relocated").toPath();
8988

9089
if (Files.exists(relocatedJar)) {

0 commit comments

Comments
 (0)