Skip to content

Commit 2dd8fb2

Browse files
committed
Fix asset downloads to not try to download nearly as many things at once
1 parent 89468b6 commit 2dd8fb2

File tree

1 file changed

+40
-32
lines changed

1 file changed

+40
-32
lines changed

src/main/java/dev/lukebemish/taskgraphrunner/runtime/util/AssetsUtils.java

Lines changed: 40 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import org.slf4j.LoggerFactory;
77

88
import java.io.IOException;
9-
import java.io.UncheckedIOException;
109
import java.net.URI;
1110
import java.nio.file.Files;
1211
import java.nio.file.Path;
@@ -17,13 +16,16 @@
1716
import java.util.Set;
1817
import java.util.concurrent.ExecutionException;
1918
import java.util.concurrent.Future;
19+
import java.util.concurrent.Semaphore;
2020

2121
public final class AssetsUtils {
2222
private static final String INDEX_FOLDER = "indexes";
2323
private static final String OBJECT_FOLDER = "objects";
2424
private static final String ASSETS_BASE_URL = "https://resources.download.minecraft.net/";
2525
private static final Logger LOGGER = LoggerFactory.getLogger(AssetsUtils.class);
2626

27+
private static final String PARALLEL_DOWNLOADS_PROPERTY = "dev.lukebemish.taskgraphrunner.assets.parallel-downloads";
28+
2729
// Sort from most to least indexes
2830
private static final Comparator<Target> ASSET_INDEX_COUNT_DESCENDING = Comparator.<Target>comparingInt(d -> d.indexes.size()).reversed();
2931

@@ -80,40 +82,46 @@ public static Path findOrDownloadIndexAndAssets(DownloadUtils.Spec spec, String
8082
try (var reader = Files.newBufferedReader(targetPath)) {
8183
json = JsonUtils.GSON.fromJson(reader, JsonObject.class);
8284
}
83-
}
8485

85-
var objectsPath = assetOptions.assetRoot().resolve(OBJECT_FOLDER);
86-
var objects = json.getAsJsonObject("objects");
87-
var targets = objects.asMap().values().stream()
88-
.distinct() // The same object can be referenced multiple times
89-
.map(entry -> {
90-
var obj = entry.getAsJsonObject();
91-
var hash = obj.getAsJsonPrimitive("hash").getAsString();
92-
var size = obj.getAsJsonPrimitive("size").getAsLong();
93-
var objectPath = objectsPath.resolve(hash.substring(0, 2)).resolve(hash);
94-
var url = URI.create(ASSETS_BASE_URL + hash.substring(0, 2) + "/" + hash);
95-
var objectSpec = new DownloadUtils.Spec.ChecksumAndSize(url, hash, "SHA-1", size);
96-
return new DownloadTarget(hash, objectSpec, objectPath);
97-
})
98-
.toList();
99-
100-
try (var ignored = context.lockManager().locks(targets.stream().map(t -> "assets."+t.target().getFileName().toString()).toList())) {
101-
var futures = new ArrayList<Future<?>>();
102-
for (var target : targets) {
103-
futures.add(context.submit(() -> {
86+
var objectsPath = assetOptions.assetRoot().resolve(OBJECT_FOLDER);
87+
var objects = json.getAsJsonObject("objects");
88+
var targets = objects.asMap().values().stream()
89+
.distinct() // The same object can be referenced multiple times
90+
.map(entry -> {
91+
var obj = entry.getAsJsonObject();
92+
var hash = obj.getAsJsonPrimitive("hash").getAsString();
93+
var size = obj.getAsJsonPrimitive("size").getAsLong();
94+
var objectPath = objectsPath.resolve(hash.substring(0, 2)).resolve(hash);
95+
var url = URI.create(ASSETS_BASE_URL + hash.substring(0, 2) + "/" + hash);
96+
var objectSpec = new DownloadUtils.Spec.ChecksumAndSize(url, hash, "SHA-1", size);
97+
return new DownloadTarget(hash, objectSpec, objectPath);
98+
})
99+
.toList();
100+
101+
try (var ignored1 = context.lockManager().locks(targets.stream().map(t -> "assets." + t.target().getFileName().toString()).toList())) {
102+
var futures = new ArrayList<Future<?>>();
103+
var semaphore = new Semaphore(Integer.getInteger(PARALLEL_DOWNLOADS_PROPERTY, 8));
104+
for (var target : targets) {
105+
futures.add(context.submit(() -> {
106+
try {
107+
semaphore.acquire();
108+
DownloadUtils.download(target.spec(), target.target());
109+
} catch (IOException | InterruptedException e) {
110+
LOGGER.error("Failed to download asset {} from {}", target.name(), target.spec().uri(), e);
111+
throw new RuntimeException(e);
112+
} finally {
113+
semaphore.release();
114+
}
115+
}));
116+
}
117+
for (var future : futures) {
104118
try {
105-
DownloadUtils.download(target.spec(), target.target());
106-
} catch (IOException e) {
107-
LOGGER.error("Failed to download asset {}", target.name(), e);
108-
throw new UncheckedIOException(e);
119+
future.get();
120+
} catch (InterruptedException | ExecutionException e) {
121+
// It failed; don't keep the bad index around
122+
Files.deleteIfExists(targetPath);
123+
throw new RuntimeException(e);
109124
}
110-
}));
111-
}
112-
for (var future : futures) {
113-
try {
114-
future.get();
115-
} catch (InterruptedException | ExecutionException e) {
116-
throw new RuntimeException(e);
117125
}
118126
}
119127
}

0 commit comments

Comments
 (0)