From fabe5afed04060460e110390ec04dd30a3ddeeb4 Mon Sep 17 00:00:00 2001 From: JFronny Date: Sat, 29 Jun 2024 18:26:20 +0200 Subject: [PATCH] feat(launcher): enhance flow-mds with support for retries with increasing delays to prevent log spam --- .../launcher/system/mds/flow/FlowMds.java | 27 +++++++++++-------- .../system/mds/flow/MdsDiscoverTask.java | 25 ++++++++++++----- .../launcher/system/mds/flow/MdsPipeline.java | 10 +++++-- .../system/mds/flow/MdsRetryState.java | 19 +++++++++++++ .../system/mds/flow/MdsTaskRetryWrapper.java | 23 ++++++++++++++++ 5 files changed, 84 insertions(+), 20 deletions(-) create mode 100644 launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/flow/MdsRetryState.java create mode 100644 launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/flow/MdsTaskRetryWrapper.java diff --git a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/flow/FlowMds.java b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/flow/FlowMds.java index 4cd6ff9..3a4b4e9 100644 --- a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/flow/FlowMds.java +++ b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/flow/FlowMds.java @@ -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 timeouts = new HashMap<>(); private void performScanTask(Map> 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 future : Stream.concat(futures1.stream(), futures2.stream()).toList()) { try { diff --git a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/flow/MdsDiscoverTask.java b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/flow/MdsDiscoverTask.java index f03fdaf..3ff7ce9 100644 --- a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/flow/MdsDiscoverTask.java +++ b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/flow/MdsDiscoverTask.java @@ -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 { +public record MdsDiscoverTask(HashMap timeouts, ProtoInstance instance, String gameVersion) implements ThrowingFunction { @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); } } diff --git a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/flow/MdsPipeline.java b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/flow/MdsPipeline.java index 63af2af..7dc4126 100644 --- a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/flow/MdsPipeline.java +++ b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/flow/MdsPipeline.java @@ -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 task) { + public void addFeedbackTask(@Nullable ThrowingBiConsumer 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 task) { if (task == null) return; functions.add(Tuple.of(stage, tuple -> { if (tuple.right() instanceof MdsMod mmod) task.accept(tuple.left(), mmod); diff --git a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/flow/MdsRetryState.java b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/flow/MdsRetryState.java new file mode 100644 index 0000000..2c2eaa1 --- /dev/null +++ b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/flow/MdsRetryState.java @@ -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; + } +} diff --git a/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/flow/MdsTaskRetryWrapper.java b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/flow/MdsTaskRetryWrapper.java new file mode 100644 index 0000000..5c48e14 --- /dev/null +++ b/launcher/src/main/java/io/gitlab/jfronny/inceptum/launcher/system/mds/flow/MdsTaskRetryWrapper.java @@ -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 timeouts, ThrowingConsumer inner) implements ThrowingConsumer { + @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; + } + } +}