Skip to content

Commit

Permalink
Add basic caching layer.
Browse files Browse the repository at this point in the history
  • Loading branch information
modmuss50 committed Dec 17, 2023
1 parent 551e7ee commit 3556d03
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 11 deletions.
5 changes: 4 additions & 1 deletion src/main/java/net/fabricmc/meta/FabricMeta.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@

import net.fabricmc.meta.data.VersionDatabase;
import net.fabricmc.meta.utils.Reference;
import net.fabricmc.meta.web.CacheHandler;
import net.fabricmc.meta.web.WebServer;

public class FabricMeta {
Expand All @@ -46,6 +47,7 @@ public class FabricMeta {
private static final Map<String, String> config = new HashMap<>();
private static boolean configInitialized;
private static URL heartbeatUrl; // URL pinged with every successful update()
private static CacheHandler cacheHandler = new CacheHandler();

public static void main(String[] args) {
Path configFile = Paths.get("config.json");
Expand Down Expand Up @@ -79,13 +81,14 @@ public static void main(String[] args) {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
executorService.scheduleAtFixedRate(FabricMeta::update, 1, 1, TimeUnit.MINUTES);

WebServer webServer = new WebServer(() -> database);
WebServer webServer = new WebServer(() -> database, cacheHandler);
webServer.createServer().start(5555);
}

private static void update() {
try {
database = VersionDatabase.generate();
cacheHandler.invalidateCache();
updateHeartbeat();
} catch (Throwable t) {
if (database == null) {
Expand Down
90 changes: 90 additions & 0 deletions src/main/java/net/fabricmc/meta/web/CacheHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright (c) 2023 FabricMC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package net.fabricmc.meta.web;

import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import io.javalin.http.Handler;
import io.javalin.http.servlet.JavalinServletContext;

public class CacheHandler {
private final Map<String, Response> cache = new ConcurrentHashMap<>();

public CacheHandler() {
}

public Handler before() {
return ctx -> {
Response response = cache.get(ctx.path());

if (response == null) {
return;
}

// Replay the response
ctx.status(response.status());
response.headers().forEach(ctx::header);
ctx.contentType(response.contentType());
ctx.result(response.body());

// Replace with ctx.skipRemainingHandlers() in Javalin 6
JavalinServletContext jsc = (JavalinServletContext) ctx;
jsc.getTasks().clear();
};
}

public Handler after() {
return ctx -> {
if (ctx.statusCode() != 200) {
return;
}

if (!ctx.queryParamMap().isEmpty()) {
// Don't cache any requests with query params to prevent the cache from growing too big.
// Maybe look into something better here
return;
}

cache.put(ctx.path(), new Response(
ctx.statusCode(),
readAllBytes(ctx.resultInputStream()),
ctx.headerMap(),
ctx.res().getContentType()));
};
}

private static byte[] readAllBytes(InputStream is) throws IOException {
is.reset();
byte[] bytes = is.readAllBytes();
is.reset();
return bytes;
}

public void invalidateCache() {
cache.clear();
}

private record Response(
int status,
byte[] body,
Map<String, String> headers,
String contentType) {
}
}
10 changes: 9 additions & 1 deletion src/main/java/net/fabricmc/meta/web/WebServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package net.fabricmc.meta.web;

import static io.javalin.apibuilder.ApiBuilder.after;
import static io.javalin.apibuilder.ApiBuilder.before;
import static io.javalin.apibuilder.ApiBuilder.path;

import java.util.function.Function;
Expand All @@ -39,10 +41,14 @@ public class WebServer {
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();

private final DataProvider dataProvider;
private final CacheHandler cacheHandler;

private final EndpointsV1 endpointsV1;

public WebServer(DataProvider dataProvider) {
public WebServer(DataProvider dataProvider, CacheHandler cacheHandler) {
this.dataProvider = dataProvider;
this.cacheHandler = cacheHandler;

endpointsV1 = new EndpointsV1(dataProvider);
}

Expand All @@ -52,6 +58,8 @@ public Javalin createServer() {
config.showJavalinBanner = false;
config.plugins.enableCors(cors -> cors.add(CorsPluginConfig::anyHost));
}).routes(() -> {
before(cacheHandler.before());
after(cacheHandler.after());
path("v1", endpointsV1.routes());
});

Expand Down
17 changes: 9 additions & 8 deletions src/main/java/net/fabricmc/meta/web/v1/ModelsV1.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,14 @@
import net.fabricmc.meta.web.JsonModel;

/**
* Strongly defined records of the public API. Take extra care to record to method return types here.
* Strongly defined records of the public API. Take extra care when changing method return types and record components.
*/
public sealed interface ModelsV1 extends JsonModel permits ModelsV1.GameVersion, ModelsV1.MappingVersion, ModelsV1.LoaderVersion, ModelsV1.LoaderInfo, ModelsV1.LoaderLauncherInfo {
/**
* /v2/versions/game
* /v1/versions/game
*/
static List<GameVersion> gameVersions(DataProvider dataProvider) {
System.out.println("ohno");
LinkedList<GameVersion> versions = new LinkedList<>();

for (BaseVersion version : dataProvider.getGameVersions()) {
Expand All @@ -48,7 +49,7 @@ static List<GameVersion> gameVersions(DataProvider dataProvider) {
}

/**
* /v2/game/{game_version}
* /v1/game/{game_version}
*/
static List<GameVersion> gameVersions(DataProvider dataProvider, String gameVersion) {
for (BaseVersion version : dataProvider.getGameVersions()) {
Expand All @@ -61,7 +62,7 @@ static List<GameVersion> gameVersions(DataProvider dataProvider, String gameVers
}

/**
* /v2/versions/mappings
* /v1/versions/mappings
*/
static List<MappingVersion> mappingVersions(DataProvider dataProvider) {
LinkedList<MappingVersion> versions = new LinkedList<>();
Expand All @@ -74,7 +75,7 @@ static List<MappingVersion> mappingVersions(DataProvider dataProvider) {
}

/**
* /v2/mappings/{game_version}
* /v1/mappings/{game_version}
*/
static List<MappingVersion> mappingVersions(DataProvider dataProvider, String gameVersion) {
LinkedList<MappingVersion> versions = new LinkedList<>();
Expand All @@ -89,7 +90,7 @@ static List<MappingVersion> mappingVersions(DataProvider dataProvider, String ga
}

/**
* /v2/versions/loader
* /v1/versions/loader
*/
static List<LoaderVersion> loaderVersions(DataProvider dataProvider) {
LinkedList<LoaderVersion> versions = new LinkedList<>();
Expand All @@ -102,7 +103,7 @@ static List<LoaderVersion> loaderVersions(DataProvider dataProvider) {
}

/**
* /v2/versions/loader/{game_version}/{loader_version}
* /v1/versions/loader/{game_version}/{loader_version}
*/
static List<LoaderInfo> loaderInfo(DataProvider dataProvider, String gameVersion) {
MavenBuildGameVersion mappings = null;
Expand All @@ -128,7 +129,7 @@ static List<LoaderInfo> loaderInfo(DataProvider dataProvider, String gameVersion
}

/**
* /v2/versions/loader/{game_version}/{loader_version}
* /v1/versions/loader/{game_version}/{loader_version}
*/
static LoaderLauncherInfo loaderLauncherInfo(DataProvider dataProvider, String gameVersion, String loaderVersion) {
MavenBuildVersion loader = null;
Expand Down
4 changes: 3 additions & 1 deletion src/test/java/net/fabricmc/meta/test/TestUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@
import io.javalin.Javalin;

import net.fabricmc.meta.FabricMeta;
import net.fabricmc.meta.web.CacheHandler;
import net.fabricmc.meta.web.WebServer;

public class TestUtils {
public static Javalin createServer() {
return new WebServer(() -> FabricMeta.database).createServer();
CacheHandler cacheHandler = new CacheHandler();
return new WebServer(() -> FabricMeta.database, cacheHandler).createServer();
}
}

0 comments on commit 3556d03

Please sign in to comment.