feat(launcher): enhance flow-mds with support for retries with increasing delays to prevent log spam
This commit is contained in:
parent
a67a0b4b4f
commit
fabe5afed0
@ -150,7 +150,11 @@ public class FlowMds implements ModsDirScanner {
|
||||
try {
|
||||
performScanTask(Map.of(ScanStage.ALL, R::nop));
|
||||
Thread.sleep(1000);
|
||||
} catch (IOException | InterruptedException e) {
|
||||
} catch (IOException e) {
|
||||
for (SavedTask task : tasks.values()) {
|
||||
task.future.completeExceptionally(e);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
for (SavedTask task : tasks.values()) {
|
||||
task.future.completeExceptionally(e);
|
||||
}
|
||||
@ -159,6 +163,7 @@ public class FlowMds implements ModsDirScanner {
|
||||
}
|
||||
}
|
||||
|
||||
private final HashMap<String, MdsRetryState> timeouts = new HashMap<>();
|
||||
private void performScanTask(Map<ScanStage, ThrowingBiConsumer<Path, Mod, IOException>> discovered) throws IOException {
|
||||
if (!Files.isDirectory(instance.modsDir())) {
|
||||
return;
|
||||
@ -171,22 +176,22 @@ public class FlowMds implements ModsDirScanner {
|
||||
ScanStage targetStage = discovered.keySet().stream()
|
||||
.max(Comparator.naturalOrder())
|
||||
.orElse(ScanStage.DISCOVER);
|
||||
pipeline.addTask(ScanStage.NONE, discovered.get(ScanStage.NONE));
|
||||
pipeline.addTask(ScanStage.NONE, discovered.get(ScanStage.DISCOVER));
|
||||
pipeline.addFeedbackTask(discovered.get(ScanStage.NONE));
|
||||
pipeline.addFeedbackTask(discovered.get(ScanStage.DISCOVER));
|
||||
if (targetStage.contains(ScanStage.DOWNLOAD)) {
|
||||
pipeline.addTask(ScanStage.DOWNLOAD, new MdsDownloadTask(instance));
|
||||
pipeline.addTask(ScanStage.NONE, discovered.get(ScanStage.DOWNLOAD));
|
||||
pipeline.addTask(ScanStage.DOWNLOAD, new MdsTaskRetryWrapper(timeouts, new MdsDownloadTask(instance)));
|
||||
pipeline.addFeedbackTask(discovered.get(ScanStage.DOWNLOAD));
|
||||
}
|
||||
if (targetStage.contains(ScanStage.CROSSREFERENCE)) {
|
||||
pipeline.addTask(ScanStage.CROSSREFERENCE, new MdsCrossReferenceTask(instance));
|
||||
pipeline.addTask(ScanStage.NONE, discovered.get(ScanStage.CROSSREFERENCE));
|
||||
pipeline.addTask(ScanStage.CROSSREFERENCE, new MdsTaskRetryWrapper(timeouts, new MdsCrossReferenceTask(instance)));
|
||||
pipeline.addFeedbackTask(discovered.get(ScanStage.CROSSREFERENCE));
|
||||
}
|
||||
if (targetStage.contains(ScanStage.UPDATECHECK)) {
|
||||
pipeline.addTask(ScanStage.UPDATECHECK, new MdsUpdateTask(instance, getGameVersion()));
|
||||
pipeline.addTask(ScanStage.NONE, discovered.get(ScanStage.UPDATECHECK));
|
||||
pipeline.addTask(ScanStage.UPDATECHECK, new MdsTaskRetryWrapper(timeouts, new MdsUpdateTask(instance, getGameVersion())));
|
||||
pipeline.addFeedbackTask(discovered.get(ScanStage.UPDATECHECK));
|
||||
}
|
||||
pipeline.addTask(ScanStage.NONE, discovered.get(ScanStage.ALL));
|
||||
var futures1 = pipeline.run(factory, getToScan(), new MdsDiscoverTask(instance, getGameVersion()));
|
||||
pipeline.addFeedbackTask(discovered.get(ScanStage.ALL));
|
||||
var futures1 = pipeline.run(factory, getToScan(), new MdsDiscoverTask(timeouts, instance, getGameVersion()));
|
||||
var futures2 = pipeline.run(factory, descriptions.entrySet().stream().map(Tuple::from).collect(Collectors.toSet()));
|
||||
for (CompletableFuture<Void> future : Stream.concat(futures1.stream(), futures2.stream()).toList()) {
|
||||
try {
|
||||
|
@ -11,8 +11,9 @@ import io.gitlab.jfronny.inceptum.launcher.system.mds.noop.NoopMod;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashMap;
|
||||
|
||||
public record MdsDiscoverTask(ProtoInstance instance, String gameVersion) implements ThrowingFunction<Path, Mod, IOException> {
|
||||
public record MdsDiscoverTask(HashMap<String, MdsRetryState> timeouts, ProtoInstance instance, String gameVersion) implements ThrowingFunction<Path, Mod, IOException> {
|
||||
@Override
|
||||
public Mod apply(Path file) throws IOException {
|
||||
if (!Files.exists(file)) return null;
|
||||
@ -23,12 +24,22 @@ public record MdsDiscoverTask(ProtoInstance instance, String gameVersion) implem
|
||||
}
|
||||
|
||||
private Mod discover(Path jarPath, Path imodPath) throws IOException {
|
||||
ModMeta meta;
|
||||
if (Files.exists(imodPath)) meta = GC_ModMeta.deserialize(imodPath, GsonPreset.CONFIG);
|
||||
else {
|
||||
meta = ModMeta.fromJar(jarPath);
|
||||
GC_ModMeta.serialize(meta, imodPath, GsonPreset.CONFIG);
|
||||
String key = imodPath.getFileName().toString() + "/" + MdsDiscoverTask.class.getTypeName();
|
||||
MdsRetryState state = timeouts.computeIfAbsent(key, _ -> new MdsRetryState());
|
||||
if (!state.shouldRetry()) return null;
|
||||
try {
|
||||
ModMeta meta;
|
||||
if (Files.exists(imodPath)) meta = GC_ModMeta.deserialize(imodPath, GsonPreset.CONFIG);
|
||||
else {
|
||||
meta = ModMeta.fromJar(jarPath);
|
||||
GC_ModMeta.serialize(meta, imodPath, GsonPreset.CONFIG);
|
||||
}
|
||||
Mod result = new MdsMod(instance, imodPath, jarPath, meta);
|
||||
state.success();
|
||||
return result;
|
||||
} catch (RuntimeException | IOException e) {
|
||||
state.fail();
|
||||
throw e;
|
||||
}
|
||||
return new MdsMod(instance, imodPath, jarPath, meta);
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class MdsPipeline {
|
||||
@ -22,7 +21,14 @@ public class MdsPipeline {
|
||||
}));
|
||||
}
|
||||
|
||||
public void addTask(ScanStage stage, @Nullable ThrowingBiConsumer<Path, ? super MdsMod, IOException> task) {
|
||||
public void addFeedbackTask(@Nullable ThrowingBiConsumer<Path, Mod, IOException> task) {
|
||||
if (task == null) return;
|
||||
functions.add(Tuple.of(ScanStage.NONE, tuple -> {
|
||||
if (tuple.right() != null) task.accept(tuple.left(), tuple.right());
|
||||
}));
|
||||
}
|
||||
|
||||
public void addTask(ScanStage stage, @Nullable ThrowingBiConsumer<Path, MdsMod, IOException> task) {
|
||||
if (task == null) return;
|
||||
functions.add(Tuple.of(stage, tuple -> {
|
||||
if (tuple.right() instanceof MdsMod mmod) task.accept(tuple.left(), mmod);
|
||||
|
@ -0,0 +1,19 @@
|
||||
package io.gitlab.jfronny.inceptum.launcher.system.mds.flow;
|
||||
|
||||
public class MdsRetryState {
|
||||
private int retries = 0;
|
||||
private long nextRetry = 0;
|
||||
|
||||
public boolean shouldRetry() {
|
||||
return retries == 0 || System.currentTimeMillis() > nextRetry;
|
||||
}
|
||||
|
||||
public void success() {
|
||||
retries = 0;
|
||||
}
|
||||
|
||||
public void fail() {
|
||||
retries++;
|
||||
nextRetry = System.currentTimeMillis() + (1L << retries) * 1000;
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package io.gitlab.jfronny.inceptum.launcher.system.mds.flow;
|
||||
|
||||
import io.gitlab.jfronny.commons.throwable.ThrowingConsumer;
|
||||
import io.gitlab.jfronny.inceptum.launcher.system.mds.MdsMod;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
public record MdsTaskRetryWrapper(Map<String, MdsRetryState> timeouts, ThrowingConsumer<MdsMod, IOException> inner) implements ThrowingConsumer<MdsMod, IOException> {
|
||||
@Override
|
||||
public void accept(MdsMod mod) throws IOException {
|
||||
String key = mod.getMetadataPath().getFileName().toString() + "/" + inner.getClass().getTypeName();
|
||||
MdsRetryState state = timeouts.computeIfAbsent(key, _ -> new MdsRetryState());
|
||||
if (!state.shouldRetry()) return;
|
||||
try {
|
||||
inner.accept(mod);
|
||||
state.success();
|
||||
} catch (IOException e) {
|
||||
state.fail();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user