Browse Source

Adding Quarkus (https://quarkus.io) framework to the benchmark suite.

joel.ringuette 6 years ago
parent
commit
d6a817da21

+ 1 - 0
.travis.yml

@@ -70,6 +70,7 @@ env:
     - "TESTDIR=Java/play1"
     - "TESTDIR=Java/play1"
     - "TESTDIR=Java/play2-java"
     - "TESTDIR=Java/play2-java"
     - "TESTDIR=Java/proteus"
     - "TESTDIR=Java/proteus"
+    - "TESTDIR=Java/quarkus"
     - "TESTDIR=Java/rapidoid"
     - "TESTDIR=Java/rapidoid"
     - "TESTDIR=Java/ratpack"
     - "TESTDIR=Java/ratpack"
     - "TESTDIR=Java/redkale"
     - "TESTDIR=Java/redkale"

+ 61 - 0
frameworks/Java/quarkus/README.md

@@ -0,0 +1,61 @@
+# Quarkus Benchmarking Test
+
+This is the Quarkus portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
+
+There is currently one repository implementation.
+* [WorldRepository](src/main/java/io/quarkus/benchmark/repository/WorldRepository.java) is using JPA.
+
+### Plaintext Test
+
+* [Plaintext test source](src/main/java/io/quarkus/benchmark/resource/PlainTextResource.java)
+
+### JSON Serialization Test
+
+* [JSON test source](src/main/java/io/quarkus/benchmark/resource/JsonResource.java)
+
+### Database Query Test
+
+* [Database Query test source](src/main/java/io/quarkus/benchmark/resource/DbResource.java)
+
+### Database Queries Test
+
+* [Database Queries test source](src/main/java/io/quarkus/benchmark/resource/DbResource.java)
+
+### Database Update Test
+
+* [Database Update test source](src/main/java/io/quarkus/benchmark/resource/DbResource.java)
+
+### Template rendering Test
+
+* [Template rendering test source](src/main/java/io/quarkus/benchmark/resource/FortuneResource.java)
+
+## Versions
+
+* [Java OpenJDK 11](http://openjdk.java.net/)
+* [Quarkus 0.16.0](https://quarkus.io)
+
+## Test URLs
+
+### Plaintext Test
+
+    http://localhost:8080/plaintext
+
+### JSON Encoding Test
+
+    http://localhost:8080/json
+
+### Database Query Test
+
+    http://localhost:8080/db
+
+### Database Queries Test
+
+    http://localhost:8080/queries?queries=5
+
+### Database Update Test
+
+    http://localhost:8080/updates?queries=5
+
+### Template rendering Test
+
+    http://localhost:8080/fortunes

+ 30 - 0
frameworks/Java/quarkus/benchmark_config.json

@@ -0,0 +1,30 @@
+{
+  "framework": "quarkus",
+  "tests": [
+    {
+      "default": {
+        "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": "fullstack",
+        "database": "Postgres",
+        "framework": "Quarkus",
+        "language": "Java",
+        "flavor": "None",
+        "orm": "full",
+        "platform": "JAX-RS",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "Quarkus",
+        "notes": "",
+        "versus": "Netty"
+      }
+    }
+  ]
+}

+ 121 - 0
frameworks/Java/quarkus/pom.xml

@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project>
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>io.quarkus</groupId>
+    <artifactId>benchmark</artifactId>
+    <version>1.0-SNAPSHOT</version>
+
+    <properties>
+        <quarkus.version>0.16.0</quarkus.version>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <maven.compiler.source>11</maven.compiler.source>
+        <maven.compiler.target>11</maven.compiler.target>
+    </properties>
+
+    <dependencyManagement>
+        <dependencies>
+            <dependency>
+                <groupId>io.quarkus</groupId>
+                <artifactId>quarkus-bom</artifactId>
+                <version>${quarkus.version}</version>
+                <type>pom</type>
+                <scope>import</scope>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
+    <dependencies>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-resteasy</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-resteasy-jsonb</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-arc</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-hibernate-orm</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.quarkus</groupId>
+            <artifactId>quarkus-jdbc-postgresql</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.github.spullara.mustache.java</groupId>
+            <artifactId>compiler</artifactId>
+            <version>0.9.6</version>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <version>3.8.1</version>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <version>3.1.1</version>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-resources-plugin</artifactId>
+                <version>3.1.0</version>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>3.1.1</version>
+            </plugin>
+            <plugin>
+                <groupId>io.quarkus</groupId>
+                <artifactId>quarkus-maven-plugin</artifactId>
+                <version>${quarkus.version}</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>build</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+    <profiles>
+        <profile>
+            <id>native</id>
+            <activation>
+                <property>
+                    <name>native</name>
+                </property>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>io.quarkus</groupId>
+                        <artifactId>quarkus-maven-plugin</artifactId>
+                        <version>${quarkus.version}</version>
+                        <executions>
+                            <execution>
+                                <goals>
+                                    <goal>native-image</goal>
+                                </goals>
+                                <configuration>
+                                    <enableHttpUrlHandler>true</enableHttpUrlHandler>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+</project>

+ 12 - 0
frameworks/Java/quarkus/quarkus.dockerfile

@@ -0,0 +1,12 @@
+FROM maven:3.6-jdk-11-slim as maven
+WORKDIR /quarkus
+COPY pom.xml pom.xml
+RUN mvn dependency:go-offline -q
+COPY src src
+RUN mvn package -q
+
+FROM openjdk:11-jre-slim
+WORKDIR /quarkus
+COPY --from=maven /quarkus/target/lib lib
+COPY --from=maven /quarkus/target/benchmark-1.0-SNAPSHOT-runner.jar app.jar
+CMD ["java", "-server", "-XX:+UseNUMA", "-XX:+UseParallelGC", "-jar", "app.jar"]

+ 15 - 0
frameworks/Java/quarkus/src/main/java/io/quarkus/benchmark/filter/ServerHeaderFilter.java

@@ -0,0 +1,15 @@
+package io.quarkus.benchmark.filter;
+
+import javax.ws.rs.container.ContainerRequestContext;
+import javax.ws.rs.container.ContainerResponseContext;
+import javax.ws.rs.container.ContainerResponseFilter;
+import javax.ws.rs.ext.Provider;
+
+@Provider
+public class ServerHeaderFilter implements ContainerResponseFilter {
+
+    @Override
+    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) {
+        responseContext.getHeaders().add("Server", "Quarkus");
+    }
+}

+ 53 - 0
frameworks/Java/quarkus/src/main/java/io/quarkus/benchmark/model/Fortune.java

@@ -0,0 +1,53 @@
+package io.quarkus.benchmark.model;
+
+import java.util.Objects;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+@Entity
+public final class Fortune {
+
+    @Id
+    private int id;
+    private String message;
+
+    public Fortune() {}
+
+    public Fortune(int id, String message) {
+        this.id = id;
+        this.message = message;
+    }
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        Fortune fortune = (Fortune) o;
+        return id == fortune.id &&
+                Objects.equals(message, fortune.message);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(id, message);
+    }
+}

+ 46 - 0
frameworks/Java/quarkus/src/main/java/io/quarkus/benchmark/model/World.java

@@ -0,0 +1,46 @@
+package io.quarkus.benchmark.model;
+
+import java.util.Objects;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+@Entity
+public class World {
+
+    @Id
+    private int id;
+    private int randomNumber;
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public int getRandomNumber() {
+        return randomNumber;
+    }
+
+    public void setRandomNumber(int randomNumber) {
+        this.randomNumber = randomNumber;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        World world = (World) o;
+        return id == world.id &&
+                randomNumber == world.randomNumber;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(id, randomNumber);
+    }
+}

+ 25 - 0
frameworks/Java/quarkus/src/main/java/io/quarkus/benchmark/repository/FortuneRepository.java

@@ -0,0 +1,25 @@
+package io.quarkus.benchmark.repository;
+
+import java.util.List;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+import javax.transaction.Transactional;
+
+import io.quarkus.benchmark.model.Fortune;
+import io.quarkus.benchmark.model.World;
+
+@ApplicationScoped
+public class FortuneRepository {
+
+    @Inject
+    EntityManager em;
+
+    @Transactional
+    public List<Fortune> findAll() {
+        Query query = em.createQuery("SELECT f FROM Fortune f");
+        return query.getResultList();
+    }
+}

+ 27 - 0
frameworks/Java/quarkus/src/main/java/io/quarkus/benchmark/repository/WorldRepository.java

@@ -0,0 +1,27 @@
+package io.quarkus.benchmark.repository;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.persistence.EntityManager;
+import javax.transaction.Transactional;
+
+import io.quarkus.benchmark.model.World;
+
+@ApplicationScoped
+public class WorldRepository {
+
+    @Inject
+    EntityManager em;
+
+    @Transactional
+    public World find(int id) {
+        return em.find(World.class, id);
+    }
+
+    @Transactional
+    public void update(World[] worlds) {
+        for (World world : worlds) {
+            em.merge(world);
+        }
+    }
+}

+ 76 - 0
frameworks/Java/quarkus/src/main/java/io/quarkus/benchmark/resource/DbResource.java

@@ -0,0 +1,76 @@
+package io.quarkus.benchmark.resource;
+
+import java.util.Arrays;
+import java.util.concurrent.ThreadLocalRandom;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.MediaType;
+
+import io.quarkus.benchmark.model.World;
+import io.quarkus.benchmark.repository.WorldRepository;
+
+@ApplicationScoped
+@Path("/")
+@Produces(MediaType.APPLICATION_JSON)
+@Consumes(MediaType.APPLICATION_JSON)
+public class DbResource {
+
+    @Inject
+    WorldRepository worldRepository;
+
+    @GET
+    @Path("/db")
+    public World db() {
+        return randomWorld();
+    }
+
+    @GET
+    @Path("/queries")
+    public World[] queries(@QueryParam("queries") String queries) {
+        var worlds = new World[parseQueryCount(queries)];
+        Arrays.setAll(worlds, i -> randomWorld());
+        return worlds;
+    }
+
+    @GET
+    @Path("/updates")
+    public World[] updates(@QueryParam("queries") String queries) {
+        var worlds = new World[parseQueryCount(queries)];
+        Arrays.setAll(worlds, i -> {
+            World world = randomWorld();
+            world.setRandomNumber(randomWorldNumber());
+            return world;
+        });
+
+        worldRepository.update(worlds);
+
+        return worlds;
+    }
+
+    private World randomWorld() {
+        return worldRepository.find(randomWorldNumber());
+    }
+
+    private int randomWorldNumber() {
+        return 1 + ThreadLocalRandom.current().nextInt(10000);
+    }
+
+    private int parseQueryCount(String textValue) {
+        if (textValue == null) {
+            return 1;
+        }
+        int parsedValue;
+        try {
+            parsedValue = Integer.parseInt(textValue);
+        } catch (NumberFormatException e) {
+            return 1;
+        }
+        return Math.min(500, Math.max(1, parsedValue));
+    }
+}

+ 56 - 0
frameworks/Java/quarkus/src/main/java/io/quarkus/benchmark/resource/FortuneResource.java

@@ -0,0 +1,56 @@
+package io.quarkus.benchmark.resource;
+
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+
+import javax.enterprise.context.ApplicationScoped;
+import javax.inject.Inject;
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+
+import com.github.mustachejava.DefaultMustacheFactory;
+import com.github.mustachejava.Mustache;
+import com.github.mustachejava.MustacheFactory;
+
+import io.quarkus.benchmark.model.Fortune;
+import io.quarkus.benchmark.repository.FortuneRepository;
+
+@ApplicationScoped
+@Path("/")
+@Produces(MediaType.TEXT_HTML)
+@Consumes(MediaType.APPLICATION_JSON)
+public class FortuneResource {
+
+    @Inject
+    FortuneRepository repository;
+
+    private final Mustache template;
+
+    public FortuneResource() {
+        MustacheFactory mf = new DefaultMustacheFactory();
+        template = mf.compile("fortunes.mustache");
+    }
+
+    @GET
+    @Path("/fortunes")
+    public CompletionStage<String> fortunes() {
+        return CompletableFuture.supplyAsync(() -> {
+            List<Fortune> fortunes = new ArrayList<>(repository.findAll());
+            fortunes.add(new Fortune(0, "Additional fortune added at request time."));
+            fortunes.sort(Comparator.comparing(fortune -> fortune.getMessage()));
+
+            StringWriter writer = new StringWriter();
+            template.execute(writer, Collections.singletonMap("fortunes", fortunes));
+
+            return writer.toString();
+        });
+    }
+}

+ 21 - 0
frameworks/Java/quarkus/src/main/java/io/quarkus/benchmark/resource/JsonResource.java

@@ -0,0 +1,21 @@
+package io.quarkus.benchmark.resource;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+
+@Path("/json")
+public class JsonResource {
+    private static final String MESSAGE = "message";
+    private static final String HELLO = "Hello, World!";
+
+    @GET
+    @Produces(MediaType.APPLICATION_JSON)
+    public CompletionStage<Map<String, String>> json() {
+        return CompletableFuture.supplyAsync(() -> Map.of(MESSAGE, HELLO));
+    }
+}

+ 19 - 0
frameworks/Java/quarkus/src/main/java/io/quarkus/benchmark/resource/PlaintextResource.java

@@ -0,0 +1,19 @@
+package io.quarkus.benchmark.resource;
+
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionStage;
+
+@Path("/plaintext")
+public class PlaintextResource {
+    private static final String HELLO = "Hello, World!";
+
+    @GET
+    @Produces(MediaType.TEXT_PLAIN)
+    public CompletionStage<String> plaintext() {
+        return CompletableFuture.supplyAsync(() -> HELLO);
+    }
+}

+ 17 - 0
frameworks/Java/quarkus/src/main/resources/application.properties

@@ -0,0 +1,17 @@
+quarkus.datasource.url=jdbc:postgresql://tfb-database:5432/hello_world
+quarkus.datasource.driver=org.postgresql.Driver
+quarkus.datasource.username=benchmarkdbuser
+quarkus.datasource.password=benchmarkdbpass
+quarkus.datasource.max-size=64
+quarkus.datasource.min-size=16
+
+quarkus.log.console.enable=true
+quarkus.log.console.level=DEBUG
+quarkus.log.file.enable=false
+quarkus.log.level=INFO
+
+quarkus.log.category."org.hibernate".level=INFO
+
+quarkus.hibernate-orm.log.sql=false
+
+quarkus.thread-pool.queue-size=2147483647

+ 20 - 0
frameworks/Java/quarkus/src/main/resources/fortunes.mustache

@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>Fortunes</title>
+</head>
+<body>
+<table>
+    <tr>
+        <th>id</th>
+        <th>message</th>
+    </tr>
+    {{#fortunes}}
+        <tr>
+            <td>{{id}}</td>
+            <td>{{message}}</td>
+        </tr>
+    {{/fortunes}}
+</table>
+</body>
+</html>