diff options
author | Roman / Linnea Gräf <roman.graef@gmail.com> | 2023-02-15 18:50:56 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-15 18:50:56 +0100 |
commit | d3ca199f904cd72e419c6320eda261f023c71937 (patch) | |
tree | 32ea0eb2ceac0e1cb24ab09b21f5e5581f809a39 /src/main/java | |
parent | e0ab2af457daf50b838248afbc4110c97a0c8b4a (diff) | |
download | NotEnoughUpdates-d3ca199f904cd72e419c6320eda261f023c71937.tar.gz NotEnoughUpdates-d3ca199f904cd72e419c6320eda261f023c71937.tar.bz2 NotEnoughUpdates-d3ca199f904cd72e419c6320eda261f023c71937.zip |
ApiUtil: Add cache with per request timeout and per class histogram (#592)
* ApiUtil: Add cache with per request timeout and per class histogram
* MinionHelper: Only load minion helper data when needed
* Api: Make api response processing more async.
* Lower cache for /pv to 30 seconds and rename cacheDuration to max age
* Disk cache for the API
Diffstat (limited to 'src/main/java')
9 files changed, 73 insertions, 56 deletions
diff --git a/src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java b/src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java index 5ec3724a..ac60ffd9 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java @@ -292,7 +292,7 @@ public class APIManager { .newMoulberryRequest("lowestbin.json.gz") .gunzip() .requestJson() - .thenAccept(jsonObject -> { + .thenAcceptAsync(jsonObject -> { if (lowestBins == null) { lowestBins = new JsonObject(); } @@ -465,12 +465,12 @@ public class APIManager { }; manager.apiUtils.newMoulberryRequest("auctionLast.json.gz") - .gunzip().requestJson().thenAccept(process); + .gunzip().requestJson().thenAcceptAsync(process); manager.apiUtils .newMoulberryRequest("auction.json.gz") .gunzip().requestJson() - .thenAccept(jsonObject -> { + .thenAcceptAsync(jsonObject -> { if (jsonObject.get("success").getAsBoolean()) { long apiUpdate = (long) jsonObject.get("time").getAsFloat(); if (lastApiUpdate == apiUpdate) { @@ -683,7 +683,7 @@ public class APIManager { manager.apiUtils .newAnonymousHypixelApiRequest("skyblock/auctions") .requestJson() - .thenAccept(jsonObject -> { + .thenAcceptAsync(jsonObject -> { if (jsonObject == null) return; if (jsonObject.get("success").getAsBoolean()) { @@ -733,7 +733,7 @@ public class APIManager { manager.apiUtils .newAnonymousHypixelApiRequest("skyblock/bazaar") .requestJson() - .thenAccept(jsonObject -> { + .thenAcceptAsync(jsonObject -> { if (!jsonObject.get("success").getAsBoolean()) return; craftCost.clear(); @@ -789,7 +789,7 @@ public class APIManager { public void updateAvgPrices() { manager.apiUtils .newMoulberryRequest("auction_averages/3day.json.gz") - .gunzip().requestJson().thenAccept((jsonObject) -> { + .gunzip().requestJson().thenAcceptAsync((jsonObject) -> { craftCost.clear(); auctionPricesJson = jsonObject; lastAuctionAvgUpdate = System.currentTimeMillis(); @@ -797,7 +797,7 @@ public class APIManager { manager.apiUtils .newMoulberryRequest("auction_averages_lbin/1day.json.gz") .gunzip().requestJson() - .thenAccept((jsonObject) -> { + .thenAcceptAsync((jsonObject) -> { auctionPricesAvgLowestBinJson = jsonObject; }); } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.java b/src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.java index 35474ff3..8dda864a 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/commands/dev/DevTestCommand.java @@ -33,11 +33,13 @@ import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.Locati import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.SpecialBlockZone; import io.github.moulberry.notenoughupdates.miscgui.GuiPriceGraph; import io.github.moulberry.notenoughupdates.miscgui.minionhelper.MinionHelperManager; +import io.github.moulberry.notenoughupdates.util.ApiCache; import io.github.moulberry.notenoughupdates.util.PronounDB; import io.github.moulberry.notenoughupdates.util.SBInfo; import io.github.moulberry.notenoughupdates.util.TabListUtils; import io.github.moulberry.notenoughupdates.util.Utils; import io.github.moulberry.notenoughupdates.util.hypixelapi.ProfileCollectionInfo; +import lombok.var; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiScreen; import net.minecraft.command.CommandException; @@ -126,6 +128,24 @@ public class DevTestCommand extends ClientCommandBase { Utils.addChatMessage(EnumChatFormatting.RED + DEV_FAIL_STRINGS[devFailIndex++]); return; } + if (args.length == 1 && args[0].equalsIgnoreCase("dumpapihistogram")) { + synchronized (ApiCache.INSTANCE) { + Utils.addChatMessage("§e[NEU] API Request Histogram"); + Utils.addChatMessage("§e[NEU] §bClass Name§e: §aCached§e/§cNonCached§e/§dTotal"); + ApiCache.INSTANCE.getHistogramTotalRequests().forEach((className, totalRequests) -> { + var nonCachedRequests = ApiCache.INSTANCE.getHistogramNonCachedRequests().getOrDefault(className, 0); + var cachedRequests = totalRequests - nonCachedRequests; + Utils.addChatMessage( + String.format( + "§e[NEU] §b%s §a%d§e/§c%d§e/§d%d", + className, + cachedRequests, + nonCachedRequests, + totalRequests + )); + }); + } + } if (args.length == 1 && args[0].equalsIgnoreCase("testprofile")) { NotEnoughUpdates.INSTANCE.manager.apiUtils.newHypixelApiRequest("skyblock/profiles") .queryArgument( diff --git a/src/main/java/io/github/moulberry/notenoughupdates/commands/misc/PronounsCommand.java b/src/main/java/io/github/moulberry/notenoughupdates/commands/misc/PronounsCommand.java index 5a4f1400..cf0d0c56 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/commands/misc/PronounsCommand.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/commands/misc/PronounsCommand.java @@ -88,7 +88,7 @@ public class PronounsCommand extends ClientCommandBase { "§e[NEU] Pronouns for §b" + user + " §eon §b" + platform + "§e:"), id); betterPronounChoice.render().forEach(it -> nc.printChatMessage(new ChatComponentText("§e[NEU] §a" + it))); return null; - }, MinecraftExecutor.INSTANCE); + }, MinecraftExecutor.OnThread); } } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/cosmetics/CapeManager.java b/src/main/java/io/github/moulberry/notenoughupdates/cosmetics/CapeManager.java index 4a7c1939..984a7931 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/cosmetics/CapeManager.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/cosmetics/CapeManager.java @@ -150,7 +150,7 @@ public class CapeManager { NotEnoughUpdates.INSTANCE.manager.apiUtils .newMoulberryRequest("activecapes.json") .requestJson() - .thenAccept(jsonObject -> { + .thenAcceptAsync(jsonObject -> { if (jsonObject.get("success").getAsBoolean()) { lastJsonSync = jsonObject; @@ -171,7 +171,7 @@ public class CapeManager { NotEnoughUpdates.INSTANCE.manager.apiUtils .newMoulberryRequest("permscapes.json") .requestJson() - .thenAccept(jsonObject -> { + .thenAcceptAsync(jsonObject -> { if (!jsonObject.get("success").getAsBoolean()) return; permSyncTries = 0; diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/MinionHelperApiLoader.java b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/MinionHelperApiLoader.java index aaa398f4..ecf02236 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/MinionHelperApiLoader.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscgui/minionhelper/loaders/MinionHelperApiLoader.java @@ -47,7 +47,6 @@ import java.util.Map; public class MinionHelperApiLoader { private final MinionHelperManager manager; private boolean dirty = true; - private int ticks = 0; private boolean collectionApiEnabled = true; private boolean ignoreWorldSwitches = false; private boolean readyToUse = false; @@ -72,11 +71,7 @@ public class MinionHelperApiLoader { if (Minecraft.getMinecraft().thePlayer == null) return; if (!NotEnoughUpdates.INSTANCE.hasSkyblockScoreboard()) return; if (!NotEnoughUpdates.INSTANCE.config.minionHelper.gui) return; - ticks++; - - if (ticks % 20 != 0) return; - - if (dirty) { + if (dirty && "Crafted Minions".equals(Utils.getOpenChestName())) { load(); } else { if (System.currentTimeMillis() > lastLoaded + 60_000 * 3) { diff --git a/src/main/java/io/github/moulberry/notenoughupdates/options/customtypes/NEUDebugFlag.java b/src/main/java/io/github/moulberry/notenoughupdates/options/customtypes/NEUDebugFlag.java index 50f459c0..90ef93bb 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/options/customtypes/NEUDebugFlag.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/options/customtypes/NEUDebugFlag.java @@ -31,6 +31,7 @@ public enum NEUDebugFlag { WISHING("Wishing Compass Solver"), MAP("Dungeon Map Player Information"), SEARCH("SearchString Matches"), + API_CACHE("Api Cache"), ; private final String description; @@ -43,6 +44,10 @@ public enum NEUDebugFlag { return description; } + public void log(String message) { + NEUDebugLogger.log(this, message); + } + public boolean isSet() { return NEUDebugLogger.isFlagEnabled(this); } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/ProfileViewer.java b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/ProfileViewer.java index 17a14d1f..6f8427ae 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/ProfileViewer.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/profileviewer/ProfileViewer.java @@ -42,6 +42,7 @@ import net.minecraft.util.EnumChatFormatting; import javax.annotation.Nullable; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; @@ -537,6 +538,7 @@ public class ProfileViewer { manager.apiUtils .newHypixelApiRequest("player") .queryArgument("name", nameF) + .maxCacheAge(Duration.ofSeconds(30)) .requestJson() .thenAccept(jsonObject -> { if ( diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/ApiUtil.java b/src/main/java/io/github/moulberry/notenoughupdates/util/ApiUtil.java index 45522329..28298fe0 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/util/ApiUtil.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/util/ApiUtil.java @@ -48,18 +48,26 @@ import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; +import java.time.Duration; import java.util.ArrayList; import java.util.Collection; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.zip.GZIPInputStream; public class ApiUtil { private static final Gson gson = new Gson(); + + private static final Comparator<NameValuePair> nameValuePairComparator = Comparator + .comparing(NameValuePair::getName) + .thenComparing(NameValuePair::getValue); + private static final ExecutorService executorService = Executors.newFixedThreadPool(3); private static String getUserAgent() { if (NotEnoughUpdates.INSTANCE.config.hidden.customUserAgent != null) { @@ -110,6 +118,7 @@ public class ApiUtil { private final List<NameValuePair> queryArguments = new ArrayList<>(); private String baseUrl = null; private boolean shouldGunzip = false; + private Duration maxCacheAge = Duration.ofSeconds(500); private String method = "GET"; private String postData = null; private String postContentType = null; @@ -119,6 +128,15 @@ public class ApiUtil { return this; } + /** + * Specify a cache timeout of {@code null} to signify an uncacheable request. + * Non {@code GET} requests are always uncacheable. + */ + public Request maxCacheAge(Duration maxCacheAge) { + this.maxCacheAge = maxCacheAge; + return this; + } + public Request url(String baseUrl) { this.baseUrl = baseUrl; return this; @@ -160,7 +178,17 @@ public class ApiUtil { return fut; } - public CompletableFuture<String> requestString() { + public String getBaseUrl() { + return baseUrl; + } + + private ApiCache.CacheKey getCacheKey() { + if (!"GET".equals(method)) return null; + queryArguments.sort(nameValuePairComparator); + return new ApiCache.CacheKey(baseUrl, queryArguments, shouldGunzip); + } + + private CompletableFuture<String> requestString0() { return buildUrl().thenApplyAsync(url -> { try { InputStream inputStream = null; @@ -183,7 +211,7 @@ public class ApiUtil { conn.setDoOutput(true); OutputStream os = conn.getOutputStream(); try { - os.write(this.postData.getBytes("utf-8")); + os.write(this.postData.getBytes(StandardCharsets.UTF_8)); } finally { os.close(); } @@ -221,12 +249,16 @@ public class ApiUtil { }); } + public CompletableFuture<String> requestString() { + return ApiCache.INSTANCE.cacheRequest(this, getCacheKey(), this::requestString0, maxCacheAge); + } + public CompletableFuture<JsonObject> requestJson() { return requestJson(JsonObject.class); } public <T> CompletableFuture<T> requestJson(Class<? extends T> clazz) { - return requestString().thenApply(str -> gson.fromJson(str, clazz)); + return requestString().thenApplyAsync(str -> gson.fromJson(str, clazz)); } } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/MinecraftExecutor.java b/src/main/java/io/github/moulberry/notenoughupdates/util/MinecraftExecutor.java deleted file mode 100644 index bf973b76..00000000 --- a/src/main/java/io/github/moulberry/notenoughupdates/util/MinecraftExecutor.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2022 NotEnoughUpdates contributors - * - * This file is part of NotEnoughUpdates. - * - * NotEnoughUpdates is free software: you can redistribute it - * and/or modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * NotEnoughUpdates is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with NotEnoughUpdates. If not, see <https://www.gnu.org/licenses/>. - */ - -package io.github.moulberry.notenoughupdates.util; - -import net.minecraft.client.Minecraft; -import org.jetbrains.annotations.NotNull; - -import java.util.concurrent.Executor; - -public class MinecraftExecutor implements Executor { - - public static MinecraftExecutor INSTANCE = new MinecraftExecutor(); - - private MinecraftExecutor() {} - - @Override - public void execute(@NotNull Runnable runnable) { - Minecraft.getMinecraft().addScheduledTask(runnable); - } -} |