From 4159b8d83e76d5928a533e01bc24627206b24368 Mon Sep 17 00:00:00 2001 From: gyakkun Date: Sun, 18 May 2025 17:00:49 +0800 Subject: [PATCH 1/2] [Java/muserver] Add muserver. --- frameworks/Java/muserver/README.md | 60 ++++++ .../Java/muserver/benchmark_config.json | 53 +++++ .../Java/muserver/muserver-pg.dockerfile | 15 ++ frameworks/Java/muserver/muserver.dockerfile | 15 ++ frameworks/Java/muserver/pom.xml | 95 +++++++++ frameworks/Java/muserver/run-pg.sh | 9 + frameworks/Java/muserver/run.sh | 9 + .../src/main/java/benchmark/TFBBase.java | 183 ++++++++++++++++++ .../src/main/java/benchmark/TFBPg.java | 8 + .../src/main/java/benchmark/TFBRest.java | 9 + .../main/java/benchmark/model/Fortune.java | 12 ++ .../src/main/java/benchmark/model/World.java | 12 ++ .../java/benchmark/repository/DbFactory.java | 62 ++++++ .../java/benchmark/repository/DbService.java | 33 ++++ .../repository/JDBCConnectionFactory.java | 28 +++ .../benchmark/repository/JDBCDbService.java | 94 +++++++++ .../src/main/resources/environment.properties | 7 + .../src/main/resources/hikari.properties | 7 + .../src/main/resources/templates/fortune.peb | 12 ++ 19 files changed, 723 insertions(+) create mode 100644 frameworks/Java/muserver/README.md create mode 100644 frameworks/Java/muserver/benchmark_config.json create mode 100644 frameworks/Java/muserver/muserver-pg.dockerfile create mode 100644 frameworks/Java/muserver/muserver.dockerfile create mode 100644 frameworks/Java/muserver/pom.xml create mode 100755 frameworks/Java/muserver/run-pg.sh create mode 100755 frameworks/Java/muserver/run.sh create mode 100644 frameworks/Java/muserver/src/main/java/benchmark/TFBBase.java create mode 100644 frameworks/Java/muserver/src/main/java/benchmark/TFBPg.java create mode 100644 frameworks/Java/muserver/src/main/java/benchmark/TFBRest.java create mode 100644 frameworks/Java/muserver/src/main/java/benchmark/model/Fortune.java create mode 100644 frameworks/Java/muserver/src/main/java/benchmark/model/World.java create mode 100644 frameworks/Java/muserver/src/main/java/benchmark/repository/DbFactory.java create mode 100644 frameworks/Java/muserver/src/main/java/benchmark/repository/DbService.java create mode 100644 frameworks/Java/muserver/src/main/java/benchmark/repository/JDBCConnectionFactory.java create mode 100644 frameworks/Java/muserver/src/main/java/benchmark/repository/JDBCDbService.java create mode 100644 frameworks/Java/muserver/src/main/resources/environment.properties create mode 100644 frameworks/Java/muserver/src/main/resources/hikari.properties create mode 100644 frameworks/Java/muserver/src/main/resources/templates/fortune.peb diff --git a/frameworks/Java/muserver/README.md b/frameworks/Java/muserver/README.md new file mode 100644 index 00000000000..935dbb5d8c9 --- /dev/null +++ b/frameworks/Java/muserver/README.md @@ -0,0 +1,60 @@ +# muserver Benchmarking Test + +### Test Type Implementation Source Code + +* [JSON](src/main/java/benchmark/TFBBase.java) +* [PLAINTEXT](src/main/java/benchmark/TFBBase.java) +* [DB](src/main/java/benchmark/TFBBase.java) +* [QUERY](src/main/java/benchmark/TFBBase.java) +* [UPDATE](src/main/java/benchmark/TFBBase.java) +* [FORTUNES](src/main/java/benchmark/TFBBase.java) + +## Important Libraries + +The tests were run with: + +* [Java 21](https://jdk.java.net/21/) +* [muserver 2.1.10](https://muserver.io/) +* [Jackson 2.19.0](https://github.com/FasterXML/jackson) +* [Pebble 3.2.4](https://pebbletemplates.io/) +* [Postgres JDBC Driver 42.7.5](https://jdbc.postgresql.org/) +* [HikariCP 6.3.0](https://github.com/brettwooldridge/HikariCP) + +## Test URLs + +For running default test (TFBRest.java), please append "/rest" to the path. + +### JSON + +http://localhost:8080/json + +### PLAINTEXT + +http://localhost:8080/plaintext + +### DB + +http://localhost:8080/db + +### QUERY + +http://localhost:8080/query?queries= + +### CACHED QUERY + +http://localhost:8080/cached_query?queries= + +### UPDATE + +http://localhost:8080/update?queries= + +### FORTUNES + +http://localhost:8080/fortunes + + +## Reference + +The Loom support and IO_URING support are modified from [netty test](https://github.com/TechEmpower/FrameworkBenchmarks/tree/master/frameworks/Java/netty). + +The database connection part is modified from [Javalin test](https://github.com/TechEmpower/FrameworkBenchmarks/tree/master/frameworks/Java/javalin). \ No newline at end of file diff --git a/frameworks/Java/muserver/benchmark_config.json b/frameworks/Java/muserver/benchmark_config.json new file mode 100644 index 00000000000..aad8aa5274b --- /dev/null +++ b/frameworks/Java/muserver/benchmark_config.json @@ -0,0 +1,53 @@ +{ + "framework": "muserver", + "tests": [ + { + "default": { + "json_url": "/rest/json", + "plaintext_url": "/rest/plaintext", + "db_url": "/rest/db", + "query_url": "/rest/queries?queries=", + "update_url": "/rest/updates?queries=", + "fortune_url": "/rest/fortunes", + "port": 8080, + "approach": "Realistic", + "classification": "Micro", + "database": "Postgres", + "framework": "muserver", + "language": "Java", + "flavor": "None", + "orm": "Raw", + "platform": "Netty", + "webserver": "None", + "os": "Linux", + "database_os": "Linux", + "display_name": "mu-rest-pg", + "notes": "Jax-rs rest resource handler implementation", + "versus": "None" + }, + "pg": { + "json_url": "/json", + "plaintext_url": "/plaintext", + "db_url": "/db", + "query_url": "/queries?queries=", + "update_url": "/updates?queries=", + "fortune_url": "/fortunes", + "port": 8080, + "approach": "Realistic", + "classification": "Micro", + "database": "Postgres", + "framework": "muserver", + "language": "Java", + "flavor": "None", + "orm": "Raw", + "platform": "Netty", + "webserver": "None", + "os": "Linux", + "database_os": "Linux", + "display_name": "mu-pg", + "notes": "MuHandler implementation", + "versus": "None" + } + } + ] +} diff --git a/frameworks/Java/muserver/muserver-pg.dockerfile b/frameworks/Java/muserver/muserver-pg.dockerfile new file mode 100644 index 00000000000..17f20f3874c --- /dev/null +++ b/frameworks/Java/muserver/muserver-pg.dockerfile @@ -0,0 +1,15 @@ +FROM maven:3.9.9-eclipse-temurin-21 as maven +WORKDIR /netty +COPY pom.xml pom.xml +COPY src src +RUN mvn compile assembly:single -q + +FROM maven:3.9.9-eclipse-temurin-21 +WORKDIR /netty +COPY --from=maven /netty/target/app.jar app.jar +COPY run-pg.sh run-pg.sh + +EXPOSE 8080 +# see https://github.com/netty/netty/issues/14942 +# remember to run this with --privileged since https://github.com/TechEmpower/FrameworkBenchmarks/blob/c94f7f95bd751f86a57dea8b63fb8f336bdbbde3/toolset/utils/docker_helper.py#L239 does it +ENTRYPOINT "./run-pg.sh" \ No newline at end of file diff --git a/frameworks/Java/muserver/muserver.dockerfile b/frameworks/Java/muserver/muserver.dockerfile new file mode 100644 index 00000000000..75b9397a253 --- /dev/null +++ b/frameworks/Java/muserver/muserver.dockerfile @@ -0,0 +1,15 @@ +FROM maven:3.9.9-eclipse-temurin-21 as maven +WORKDIR /netty +COPY pom.xml pom.xml +COPY src src +RUN mvn compile assembly:single -q + +FROM maven:3.9.9-eclipse-temurin-21 +WORKDIR /netty +COPY --from=maven /netty/target/app.jar app.jar +COPY run.sh run.sh + +EXPOSE 8080 +# see https://github.com/netty/netty/issues/14942 +# remember to run this with --privileged since https://github.com/TechEmpower/FrameworkBenchmarks/blob/c94f7f95bd751f86a57dea8b63fb8f336bdbbde3/toolset/utils/docker_helper.py#L239 does it +ENTRYPOINT "./run.sh" \ No newline at end of file diff --git a/frameworks/Java/muserver/pom.xml b/frameworks/Java/muserver/pom.xml new file mode 100644 index 00000000000..bc4e4156c73 --- /dev/null +++ b/frameworks/Java/muserver/pom.xml @@ -0,0 +1,95 @@ + + + 4.0.0 + + tfb.benchmark + muserver + 0.1 + + + 21 + 21 + + + jar + + + + org.slf4j + slf4j-api + 2.0.17 + + + org.slf4j + slf4j-simple + 2.0.17 + provided + + + io.muserver + mu-server + 2.1.10 + + + org.postgresql + postgresql + 42.7.5 + + + com.zaxxer + HikariCP + 6.3.0 + + + io.pebbletemplates + pebble + 3.2.4 + + + com.fasterxml.jackson.jakarta.rs + jackson-jakarta-rs-json-provider + 2.19.0 + + + + + + + true + org.apache.maven.plugins + maven-compiler-plugin + 3.14.0 + + false + + + + maven-assembly-plugin + 3.7.1 + + app + + + hello.HelloWebServer + + + + jar-with-dependencies + + false + + + + make-assembly + package + + single + + + + + + + diff --git a/frameworks/Java/muserver/run-pg.sh b/frameworks/Java/muserver/run-pg.sh new file mode 100755 index 00000000000..68eb89117d6 --- /dev/null +++ b/frameworks/Java/muserver/run-pg.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +# PROFILING: -XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepoints +JAVA_OPTIONS="-XX:+UseNUMA \ + -XX:+UseZGC \ + -XX:+ZGenerational \ + $@" + +java $JAVA_OPTIONS -cp app.jar benchmark.TFBPg diff --git a/frameworks/Java/muserver/run.sh b/frameworks/Java/muserver/run.sh new file mode 100755 index 00000000000..2b03ca41371 --- /dev/null +++ b/frameworks/Java/muserver/run.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +# PROFILING: -XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepoints +JAVA_OPTIONS="-XX:+UseNUMA \ + -XX:+UseZGC \ + -XX:+ZGenerational \ + $@" + +java $JAVA_OPTIONS -cp app.jar benchmark.TFBRest diff --git a/frameworks/Java/muserver/src/main/java/benchmark/TFBBase.java b/frameworks/Java/muserver/src/main/java/benchmark/TFBBase.java new file mode 100644 index 00000000000..9ef45bfa74b --- /dev/null +++ b/frameworks/Java/muserver/src/main/java/benchmark/TFBBase.java @@ -0,0 +1,183 @@ +package benchmark; + +import benchmark.model.Fortune; +import benchmark.model.World; +import benchmark.repository.DbFactory; +import benchmark.repository.DbService; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.jakarta.rs.json.JacksonJsonProvider; +import io.muserver.Method; +import io.muserver.MuResponse; +import io.muserver.MuServerBuilder; +import io.muserver.Mutils; +import io.muserver.rest.RestHandlerBuilder; +import io.pebbletemplates.pebble.PebbleEngine; +import io.pebbletemplates.pebble.template.PebbleTemplate; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.QueryParam; +import jakarta.ws.rs.core.Context; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import static jakarta.ws.rs.core.MediaType.*; + +public class TFBBase { + public static final Logger LOGGER = LoggerFactory.getLogger(TFBBase.class); + public static PebbleEngine engine = new PebbleEngine.Builder().build(); + public static PebbleTemplate compiledTemplate = engine.getTemplate("templates/fortune.peb"); + + public static MuServerBuilder commonBuilderWithMuHandler() { + var jackson = new ObjectMapper(); + var helloWorld = Map.of("message", "Hello, World!"); + var dbService = DbFactory.INSTANCE.getDbService(DbFactory.DbType.POSTGRES); + return MuServerBuilder.httpServer() + .addHandler((ignore, res) -> { + res.headers().add("Server", "muserver"); + return false; + }) + .addHandler(Method.GET, "/plaintext", (req, res, pp) -> { + var ah = req.handleAsync(); + res.headers().add("content-type", "text/plain"); + ah.write(Mutils.toByteBuffer("Hello, World!"), (optEx) -> ah.complete()); + // res.write("Hello, World!"); + }) + .addHandler(Method.GET, "/json", (req, res, pp) -> { + res.headers().add("content-type", "application/json"); + jackson.writeValue(res.writer(), helloWorld); + }) + .addHandler(Method.GET, "/db", (req, res, pp) -> { + res.headers().add("content-type", "application/json"); + jackson.writeValue(res.writer(), dbService.getWorld(1).get(0)); + }) + .addHandler(Method.GET, "/queries", (req, res, pp) -> { + res.headers().add("content-type", "application/json"); + jackson.writeValue( + res.writer(), + dbService.getWorld(getBoundedRowNumber(req.query().get("queries", ""))) + ); + }) + .addHandler(Method.GET, "/fortunes", (req, res, pp) -> { + res.headers().set("content-type", "text/html;charset=utf-8"); + compiledTemplate.evaluate( + res.writer(), + Map.of("fortunes", dbService.getFortune().stream().map(Fortune::toMap).toList())); + }) + .addHandler(Method.GET, "/updates", (req, res, pp) -> { + res.headers().add("content-type", "application/json"); + jackson.writeValue( + res.writer(), + dbService.updateWorld(getBoundedRowNumber(req.query().get("queries", ""))) + ); + }) + .withInterface("0.0.0.0") + .withHttpPort(8080) + .addShutdownHook(true); + } + + public static MuServerBuilder commonBuilderWithRestHandler() { + return MuServerBuilder.httpServer() + .addHandler((req, res) -> { + res.headers().add("Server", "muserver"); + return false; + }) + + .addHandler(RestHandlerBuilder.restHandler(new TFBResource( + DbFactory.INSTANCE.getDbService(DbFactory.DbType.POSTGRES), + compiledTemplate + )).addCustomWriter(new JacksonJsonProvider())) + .withInterface("0.0.0.0") + .withHttpPort(8080) + .addShutdownHook(true); + } + + @Path("/rest") + public static class TFBResource { + public final DbService dbService; + public final PebbleTemplate template; + public final Map helloWorld = Map.of("message", "Hello, World!"); + + public TFBResource( + DbService dbService, + PebbleTemplate template + ) { + this.dbService = dbService; + this.template = template; + } + + @GET + @Path("/plaintext") + @Produces(TEXT_PLAIN) + public String plaintext() { + return "Hello, World!"; + } + + @GET + @Path("/json") + @Produces(APPLICATION_JSON) + public Map json() { + return helloWorld; + } + + @GET + @Path("/db") + @Produces(APPLICATION_JSON) + public World db() { + return dbService.getWorld(1).get(0); + } + + @GET + @Path("/queries") + @Produces(APPLICATION_JSON) + public List queries( + @QueryParam("queries") + String queries + ) { + int num = getBoundedRowNumber(queries); + return dbService.getWorld(num); + } + + + @GET + @Path("/fortunes") + @Produces(TEXT_HTML) + public void fortunes( + @Context MuResponse res + ) throws IOException { + res.headers().set("content-type", "text/html;charset=utf-8"); + List fortuneList = dbService.getFortune(); + var writer = res.writer(); + // writer will be flushed + template.evaluate(writer, Map.of("fortunes", fortuneList.stream().map(Fortune::toMap).toList())); + } + + @GET + @Path("/updates") + @Produces(APPLICATION_JSON) + public List updates( + @QueryParam("queries") + String queries + ) { + return dbService.updateWorld(getBoundedRowNumber(queries)); + } + } + + + private static final int MIN_QUERIES = 1; + private static final int MAX_QUERIES = 500; + + private static int getBoundedRowNumber(String number) { + int num; + try { + num = Integer.parseInt(number); + } catch (NumberFormatException e) { + num = MIN_QUERIES; + } + return Math.max(MIN_QUERIES, Math.min(num, MAX_QUERIES)); + } +} diff --git a/frameworks/Java/muserver/src/main/java/benchmark/TFBPg.java b/frameworks/Java/muserver/src/main/java/benchmark/TFBPg.java new file mode 100644 index 00000000000..d8a369628eb --- /dev/null +++ b/frameworks/Java/muserver/src/main/java/benchmark/TFBPg.java @@ -0,0 +1,8 @@ +package benchmark; + +public class TFBPg extends TFBBase { + public static void main(String[] args) { + var server = commonBuilderWithMuHandler().start(); + LOGGER.info("Server started at {}", server.uri()); + } +} diff --git a/frameworks/Java/muserver/src/main/java/benchmark/TFBRest.java b/frameworks/Java/muserver/src/main/java/benchmark/TFBRest.java new file mode 100644 index 00000000000..09c3ce9423d --- /dev/null +++ b/frameworks/Java/muserver/src/main/java/benchmark/TFBRest.java @@ -0,0 +1,9 @@ +package benchmark; + +public class TFBRest extends TFBBase { + public static void main(String[] args) { + var server = commonBuilderWithRestHandler().start(); + LOGGER.info("Server started at {}", server.uri()); + } + +} diff --git a/frameworks/Java/muserver/src/main/java/benchmark/model/Fortune.java b/frameworks/Java/muserver/src/main/java/benchmark/model/Fortune.java new file mode 100644 index 00000000000..5d1daced647 --- /dev/null +++ b/frameworks/Java/muserver/src/main/java/benchmark/model/Fortune.java @@ -0,0 +1,12 @@ +package benchmark.model; + +import java.util.Map; + +public record Fortune( + int id, + String message +) { + public Map toMap() { + return Map.of("id", id, "message", message); + } +} diff --git a/frameworks/Java/muserver/src/main/java/benchmark/model/World.java b/frameworks/Java/muserver/src/main/java/benchmark/model/World.java new file mode 100644 index 00000000000..3de4ff1c8b5 --- /dev/null +++ b/frameworks/Java/muserver/src/main/java/benchmark/model/World.java @@ -0,0 +1,12 @@ +package benchmark.model; + +import java.util.Objects; + +public record World( + int id, + int randomNumber +) { + public World copy(Integer id, Integer randomNumber) { + return new World(Objects.requireNonNullElse(id, this.id), Objects.requireNonNullElse(randomNumber, this.randomNumber)); + } +} diff --git a/frameworks/Java/muserver/src/main/java/benchmark/repository/DbFactory.java b/frameworks/Java/muserver/src/main/java/benchmark/repository/DbFactory.java new file mode 100644 index 00000000000..9371c498906 --- /dev/null +++ b/frameworks/Java/muserver/src/main/java/benchmark/repository/DbFactory.java @@ -0,0 +1,62 @@ +package benchmark.repository; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +public enum DbFactory { + + INSTANCE; + + public enum DbType {POSTGRES} + + public DbService getDbService(DbType type) { + + DbService dbService; + + switch (type) { + case POSTGRES: + dbService = new JDBCDbService(); + break; + default: + dbService = null; + } + + return dbService; + } + + public int getMaxPoolSize(DbType dbType) { + + int maxPoolSize; + String env = System.getenv("BENCHMARK_ENV"); + String propertiesFileName = "/environment.properties"; + File propFile = new File(propertiesFileName); + + try (InputStream is = propFile.isFile() ? + new FileInputStream(propFile) : + this.getClass().getResourceAsStream(propertiesFileName)) { + Properties prop = new Properties(); + prop.load(is); + + switch (dbType) { + case POSTGRES: + if (prop.getProperty("physicalTag").equals(env)) { + maxPoolSize = Integer.parseInt(prop.getProperty("postgresPhysicalPoolSize")); + } else if (prop.getProperty("cloudTag").equals(env)) { + maxPoolSize = Integer.parseInt(prop.getProperty("postgresCloudPoolSize")); + } else { + maxPoolSize = Integer.parseInt(prop.getProperty("postgresDefaultPoolSize")); + } + break; + default: + maxPoolSize = 100; + } + } catch (IOException | NumberFormatException e) { + throw new RuntimeException("Failed to read property file " + propertiesFileName); + } + + return maxPoolSize; + } +} diff --git a/frameworks/Java/muserver/src/main/java/benchmark/repository/DbService.java b/frameworks/Java/muserver/src/main/java/benchmark/repository/DbService.java new file mode 100644 index 00000000000..838359969be --- /dev/null +++ b/frameworks/Java/muserver/src/main/java/benchmark/repository/DbService.java @@ -0,0 +1,33 @@ +package benchmark.repository; + +import benchmark.model.Fortune; +import benchmark.model.World; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ThreadLocalRandom; + +public interface DbService { + + int MIN_RANDOM_NUMBER = 1; + int MAX_RANDOM_NUMBER_PLUS_ONE = 10001; + int defaultFortuneId = 0; + String defaultFortuneMessage = "Additional fortune added at request time."; + + + List getWorld(int num); + List getFortune(); + List updateWorld(int num); + + default int getRandomNumber() { + return ThreadLocalRandom.current().nextInt(MIN_RANDOM_NUMBER, MAX_RANDOM_NUMBER_PLUS_ONE); + } + + default Set getRandomNumberSet(int num) { + Set set = new HashSet<>(); + while (set.size() < num) + set.add(getRandomNumber()); + return set; + } +} diff --git a/frameworks/Java/muserver/src/main/java/benchmark/repository/JDBCConnectionFactory.java b/frameworks/Java/muserver/src/main/java/benchmark/repository/JDBCConnectionFactory.java new file mode 100644 index 00000000000..195cbd160b3 --- /dev/null +++ b/frameworks/Java/muserver/src/main/java/benchmark/repository/JDBCConnectionFactory.java @@ -0,0 +1,28 @@ +package benchmark.repository; + +import com.zaxxer.hikari.HikariConfig; +import com.zaxxer.hikari.HikariDataSource; + +import java.sql.Connection; +import java.sql.SQLException; + + +public enum JDBCConnectionFactory { + + INSTANCE; + + private final HikariDataSource ds; + + JDBCConnectionFactory() { + String propertiesFileName = "/hikari.properties"; + HikariConfig config = new HikariConfig(propertiesFileName); + int maxPoolSize = DbFactory.INSTANCE.getMaxPoolSize(DbFactory.DbType.POSTGRES); + + ds = new HikariDataSource(config); + ds.setMaximumPoolSize(maxPoolSize); + } + + public Connection getConnection() throws SQLException { + return ds.getConnection(); + } +} diff --git a/frameworks/Java/muserver/src/main/java/benchmark/repository/JDBCDbService.java b/frameworks/Java/muserver/src/main/java/benchmark/repository/JDBCDbService.java new file mode 100644 index 00000000000..5c9278c42c6 --- /dev/null +++ b/frameworks/Java/muserver/src/main/java/benchmark/repository/JDBCDbService.java @@ -0,0 +1,94 @@ +package benchmark.repository; + +import benchmark.model.Fortune; +import benchmark.model.World; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + +public class JDBCDbService implements DbService { + + @Override + public List getWorld(int num) { + + String select = "select id, randomNumber from World where id = ?"; + List worldList = new ArrayList<>(); + + try (Connection conn = JDBCConnectionFactory.INSTANCE.getConnection(); + PreparedStatement pstm = conn.prepareStatement(select)) { + + for (int randomId : getRandomNumberSet(num)) { + pstm.setInt(1, randomId); + try (ResultSet rs = pstm.executeQuery()) { + rs.next(); + World world = new World(rs.getInt("id"),rs.getInt("randomNumber")); + worldList.add(world); + } + } + } catch (SQLException e) { + throw new RuntimeException(e); + } + + return worldList; + } + + @Override + public List getFortune() { + + String select = "select id, message from Fortune"; + List fortuneList = new ArrayList<>(); + + try (Connection conn = JDBCConnectionFactory.INSTANCE.getConnection(); + PreparedStatement pstm = conn.prepareStatement(select); + ResultSet rs = pstm.executeQuery()) { + + while (rs.next()) { + Fortune fortune = new Fortune(rs.getInt("id"), rs.getString("message")); + fortuneList.add(fortune); + } + fortuneList.add(new Fortune(defaultFortuneId, defaultFortuneMessage)); + } catch (SQLException e) { + throw new RuntimeException(e); + } + + fortuneList.sort(Comparator.comparing(Fortune::message)); + return fortuneList; + } + + @Override + public List updateWorld(int num) { + + String update = "update World set randomNumber = ? where id = ?"; + List worldList = getWorld(num); + List updatedWorldList = new ArrayList<>(num); + + try (Connection conn = JDBCConnectionFactory.INSTANCE.getConnection(); + PreparedStatement pstm = conn.prepareStatement(update)) { + + conn.setAutoCommit(false); + for (World world : worldList) { + int newRandomNumber; + do { + newRandomNumber = getRandomNumber(); + } while (newRandomNumber == world.randomNumber()); + + pstm.setInt(1, newRandomNumber); + pstm.setInt(2, world.id()); + pstm.addBatch(); + + updatedWorldList.add(world.copy(null, newRandomNumber)); + } + pstm.executeBatch(); + conn.commit(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + + return updatedWorldList; + } +} diff --git a/frameworks/Java/muserver/src/main/resources/environment.properties b/frameworks/Java/muserver/src/main/resources/environment.properties new file mode 100644 index 00000000000..2a941e54bf0 --- /dev/null +++ b/frameworks/Java/muserver/src/main/resources/environment.properties @@ -0,0 +1,7 @@ +physicalTag=Citrine +cloudTag=Azure + +postgresPhysicalPoolSize=56 +postgresCloudPoolSize=16 +# test in 2c kvm +postgresDefaultPoolSize=10 diff --git a/frameworks/Java/muserver/src/main/resources/hikari.properties b/frameworks/Java/muserver/src/main/resources/hikari.properties new file mode 100644 index 00000000000..f859c5a8763 --- /dev/null +++ b/frameworks/Java/muserver/src/main/resources/hikari.properties @@ -0,0 +1,7 @@ +dataSourceClassName=org.postgresql.ds.PGSimpleDataSource +dataSource.serverName=tfb-database +dataSource.portNumber=5432 +dataSource.user=benchmarkdbuser +dataSource.password=benchmarkdbpass +dataSource.databaseName=hello_world + diff --git a/frameworks/Java/muserver/src/main/resources/templates/fortune.peb b/frameworks/Java/muserver/src/main/resources/templates/fortune.peb new file mode 100644 index 00000000000..8272a990d66 --- /dev/null +++ b/frameworks/Java/muserver/src/main/resources/templates/fortune.peb @@ -0,0 +1,12 @@ + + +Fortunes + + + +{% for f in fortunes %} + +{% endfor %} +
idmessage
{{f.id}}{{f.message}}
+ + From 2166c19dba3a457feacf8edaae323a301b564e54 Mon Sep 17 00:00:00 2001 From: gyakkun Date: Sat, 24 May 2025 04:21:33 +0800 Subject: [PATCH 2/2] Create object on request to adhere TFB rules. --- .../Java/muserver/src/main/java/benchmark/TFBBase.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/frameworks/Java/muserver/src/main/java/benchmark/TFBBase.java b/frameworks/Java/muserver/src/main/java/benchmark/TFBBase.java index 9ef45bfa74b..f3da599077d 100644 --- a/frameworks/Java/muserver/src/main/java/benchmark/TFBBase.java +++ b/frameworks/Java/muserver/src/main/java/benchmark/TFBBase.java @@ -34,7 +34,6 @@ public class TFBBase { public static MuServerBuilder commonBuilderWithMuHandler() { var jackson = new ObjectMapper(); - var helloWorld = Map.of("message", "Hello, World!"); var dbService = DbFactory.INSTANCE.getDbService(DbFactory.DbType.POSTGRES); return MuServerBuilder.httpServer() .addHandler((ignore, res) -> { @@ -45,11 +44,10 @@ public static MuServerBuilder commonBuilderWithMuHandler() { var ah = req.handleAsync(); res.headers().add("content-type", "text/plain"); ah.write(Mutils.toByteBuffer("Hello, World!"), (optEx) -> ah.complete()); - // res.write("Hello, World!"); }) .addHandler(Method.GET, "/json", (req, res, pp) -> { res.headers().add("content-type", "application/json"); - jackson.writeValue(res.writer(), helloWorld); + jackson.writeValue(res.writer(), Map.of("message", "Hello, World!")); }) .addHandler(Method.GET, "/db", (req, res, pp) -> { res.headers().add("content-type", "application/json"); @@ -86,7 +84,6 @@ public static MuServerBuilder commonBuilderWithRestHandler() { res.headers().add("Server", "muserver"); return false; }) - .addHandler(RestHandlerBuilder.restHandler(new TFBResource( DbFactory.INSTANCE.getDbService(DbFactory.DbType.POSTGRES), compiledTemplate @@ -100,7 +97,6 @@ public static MuServerBuilder commonBuilderWithRestHandler() { public static class TFBResource { public final DbService dbService; public final PebbleTemplate template; - public final Map helloWorld = Map.of("message", "Hello, World!"); public TFBResource( DbService dbService, @@ -121,7 +117,7 @@ public String plaintext() { @Path("/json") @Produces(APPLICATION_JSON) public Map json() { - return helloWorld; + return Map.of("message", "Hello, World!"); } @GET