Browse Source

Few Quarkus Tweaks (#8265)

* Disable Arc context propagation

* Disable Arc context propagation on Reactive side

* Change from ApplicationScoped to Singleton

* Reactive Filter is singleton

* Singleton Filter

* Optimized Fortune Rocker

* Leverage on modern compact Strings intrinsics

* Use unpooled heap Buffers for plaintext
Francesco Nigro 2 years ago
parent
commit
06c85efce5
14 changed files with 183 additions and 31 deletions
  1. 6 0
      frameworks/Java/quarkus/pom.xml
  2. 12 0
      frameworks/Java/quarkus/quarkus-benchmark-common/pom.xml
  3. 78 0
      frameworks/Java/quarkus/quarkus-benchmark-common/src/main/java/io/quarkus/benchmark/utils/rocker/HtmlUtf8BufferRockerOutput.java
  4. 6 0
      frameworks/Java/quarkus/resteasy-reactive-hibernate-reactive/pom.xml
  5. 3 1
      frameworks/Java/quarkus/resteasy-reactive-hibernate-reactive/src/main/java/io/quarkus/benchmark/filter/ServerHeaderFilter.java
  6. 2 2
      frameworks/Java/quarkus/resteasy-reactive-hibernate-reactive/src/main/java/io/quarkus/benchmark/repository/FortuneRepository.java
  7. 26 4
      frameworks/Java/quarkus/resteasy-reactive-hibernate-reactive/src/main/java/io/quarkus/benchmark/resource/FortuneResource.java
  8. 2 9
      frameworks/Java/quarkus/resteasy-reactive-hibernate-reactive/src/main/java/io/quarkus/benchmark/resource/PlaintextResource.java
  9. 2 0
      frameworks/Java/quarkus/resteasy-reactive-hibernate-reactive/src/main/resources/application.properties
  10. 14 0
      frameworks/Java/quarkus/resteasy-reactive-hibernate/pom.xml
  11. 3 1
      frameworks/Java/quarkus/resteasy-reactive-hibernate/src/main/java/io/quarkus/benchmark/filter/ServerHeaderFilter.java
  12. 26 4
      frameworks/Java/quarkus/resteasy-reactive-hibernate/src/main/java/io/quarkus/benchmark/resource/FortuneResource.java
  13. 2 9
      frameworks/Java/quarkus/resteasy-reactive-hibernate/src/main/java/io/quarkus/benchmark/resource/PlaintextResource.java
  14. 1 1
      frameworks/Java/quarkus/resteasy-reactive-hibernate/src/main/resources/application.properties

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

@@ -19,6 +19,7 @@
         <surefire-plugin.version>3.0.0</surefire-plugin.version>
         <netty.io_uring.version>0.0.21.Final</netty.io_uring.version>
         <vertx.version>4.4.2</vertx.version>
+        <rocker.version>1.3.0</rocker.version>
     </properties>
 
     <modules>
@@ -46,6 +47,11 @@
                 <type>pom</type>
                 <scope>import</scope>
             </dependency>
+            <dependency>
+                <groupId>com.fizzed</groupId>
+                <artifactId>rocker-runtime</artifactId>
+                <version>${rocker.version}</version>
+            </dependency>
         </dependencies>
     </dependencyManagement>
 

+ 12 - 0
frameworks/Java/quarkus/quarkus-benchmark-common/pom.xml

@@ -21,6 +21,18 @@
             <groupId>io.netty</groupId>
             <artifactId>netty-common</artifactId>
         </dependency>
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-buffer</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fizzed</groupId>
+            <artifactId>rocker-runtime</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.vertx</groupId>
+            <artifactId>vertx-core</artifactId>
+        </dependency>
     </dependencies>
     <profiles>
         <profile>

+ 78 - 0
frameworks/Java/quarkus/quarkus-benchmark-common/src/main/java/io/quarkus/benchmark/utils/rocker/HtmlUtf8BufferRockerOutput.java

@@ -0,0 +1,78 @@
+package io.quarkus.benchmark.utils.rocker;
+
+import com.fizzed.rocker.ContentType;
+import com.fizzed.rocker.RockerOutput;
+import com.fizzed.rocker.RockerOutputFactory;
+import io.netty.buffer.ByteBuf;
+import io.netty.util.concurrent.FastThreadLocal;
+import io.vertx.core.buffer.Buffer;
+import io.vertx.core.buffer.impl.BufferImpl;
+import io.vertx.core.buffer.impl.VertxByteBufAllocator;
+
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+public class HtmlUtf8BufferRockerOutput implements RockerOutput<HtmlUtf8BufferRockerOutput> {
+
+    private static final FastThreadLocal<HtmlUtf8BufferRockerOutput> SCRATCH_ROCKER_OUTPUT = new FastThreadLocal<>() {
+        @Override
+        protected HtmlUtf8BufferRockerOutput initialValue() {
+            return new HtmlUtf8BufferRockerOutput(ContentType.HTML);
+        }
+
+        @Override
+        protected void onRemoval(final HtmlUtf8BufferRockerOutput value) {
+            value.buff.release();
+        }
+    };
+
+    public static RockerOutputFactory<HtmlUtf8BufferRockerOutput> threadLocalFactory() {
+        return (_contentType, charsetName) -> SCRATCH_ROCKER_OUTPUT.get().reset();
+    }
+
+    private final ByteBuf buff = VertxByteBufAllocator.DEFAULT.heapBuffer();
+    private final Buffer vertxBuff = BufferImpl.buffer(buff);
+    private final ContentType contentType;
+
+    HtmlUtf8BufferRockerOutput(ContentType contentType) {
+        this.contentType = contentType;
+    }
+
+    @Override
+    public HtmlUtf8BufferRockerOutput w(byte[] bytes) {
+        buff.writeBytes(bytes);
+        return this;
+    }
+
+    @Override
+    public HtmlUtf8BufferRockerOutput w(String s) {
+        // UGLY!!! we trust vectorized & informed intrinsics for Strings here!
+        buff.writeBytes(s.getBytes(StandardCharsets.UTF_8));
+        return this;
+    }
+
+    @Override
+    public ContentType getContentType() {
+        return contentType;
+    }
+
+    @Override
+    public Charset getCharset() {
+        return StandardCharsets.UTF_8;
+    }
+
+    @Override
+    public int getByteLength() {
+        return buff.readableBytes();
+    }
+
+    public Buffer buffer() {
+        return vertxBuff;
+    }
+
+    public HtmlUtf8BufferRockerOutput reset() {
+        buff.clear();
+        return this;
+    }
+
+}

+ 6 - 0
frameworks/Java/quarkus/resteasy-reactive-hibernate-reactive/pom.xml

@@ -40,6 +40,12 @@
             <groupId>io.vertx</groupId>
             <artifactId>vertx-web-templ-rocker</artifactId>
         </dependency>
+        <!-- this is necessary to enable rocker to use GuavaHtmlStringify.java -->
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>32.0.0-jre</version>
+        </dependency>
         <dependency>
             <groupId>io.netty</groupId>
             <artifactId>netty-transport-native-epoll</artifactId>

+ 3 - 1
frameworks/Java/quarkus/resteasy-reactive-hibernate-reactive/src/main/java/io/quarkus/benchmark/filter/ServerHeaderFilter.java

@@ -4,6 +4,7 @@ import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 
 import jakarta.annotation.PostConstruct;
+import jakarta.inject.Singleton;
 
 import org.jboss.resteasy.reactive.server.ServerResponseFilter;
 
@@ -12,6 +13,7 @@ import io.vertx.core.MultiMap;
 import io.vertx.core.http.HttpHeaders;
 import io.vertx.core.http.HttpServerResponse;
 
+@Singleton
 public class ServerHeaderFilter {
 
     private static final CharSequence SERVER_HEADER_NAME = HttpHeaders.createOptimized("Server");
@@ -37,4 +39,4 @@ public class ServerHeaderFilter {
         headers.add(DATE_HEADER_NAME, date);
     }
 
-}
+}

+ 2 - 2
frameworks/Java/quarkus/resteasy-reactive-hibernate-reactive/src/main/java/io/quarkus/benchmark/repository/FortuneRepository.java

@@ -2,12 +2,12 @@ package io.quarkus.benchmark.repository;
 
 import java.util.List;
 
-import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.inject.Singleton;
 
 import io.quarkus.benchmark.model.Fortune;
 import io.smallrye.mutiny.Uni;
 
-@ApplicationScoped
+@Singleton
 public class FortuneRepository extends BaseRepository {
 
     public Uni<List<Fortune>> findAll() {

+ 26 - 4
frameworks/Java/quarkus/resteasy-reactive-hibernate-reactive/src/main/java/io/quarkus/benchmark/resource/FortuneResource.java

@@ -1,20 +1,36 @@
 package io.quarkus.benchmark.resource;
 
+import com.fizzed.rocker.RenderingException;
+import com.fizzed.rocker.runtime.DefaultHtmlStringify;
+import com.fizzed.rocker.runtime.DefaultRockerTemplate;
+import com.fizzed.rocker.runtime.GuavaHtmlStringify;
 import io.quarkus.benchmark.model.Fortune;
 import io.quarkus.benchmark.repository.FortuneRepository;
+import io.quarkus.benchmark.utils.rocker.HtmlUtf8BufferRockerOutput;
 import io.smallrye.mutiny.Uni;
 import io.vertx.core.buffer.Buffer;
-import io.vertx.ext.web.templ.rocker.impl.VertxBufferOutput;
 
 import jakarta.inject.Inject;
 import jakarta.ws.rs.GET;
 import jakarta.ws.rs.Path;
 import jakarta.ws.rs.Produces;
+import views.Fortunes;
+
 import java.util.Comparator;
 
 @Path("/fortunes")
 public class FortuneResource  {
 
+    private static final DefaultHtmlStringify STRINGIFY = new GuavaHtmlStringify();
+
+    private static class CustomFortunesTemplate extends Fortunes.Template {
+
+        public CustomFortunesTemplate(Fortunes model) {
+            super(model);
+            __internal.setStringify(STRINGIFY);
+        }
+    }
+
     @Inject
     FortuneRepository repository;
 
@@ -27,9 +43,15 @@ public class FortuneResource  {
                 .map(fortunes -> {
                     fortunes.add(new Fortune(0, "Additional fortune added at request time."));
                     fortunes.sort(fortuneComparator);
-                    return views.Fortunes.template(fortunes)
-                            .render(VertxBufferOutput.FACTORY)
-                            .getBuffer();
+                    return new Fortunes() {
+
+                        @Override
+                        protected DefaultRockerTemplate buildTemplate() throws RenderingException {
+                            return new CustomFortunesTemplate(this);
+                        }
+                    }.fortunes(fortunes)
+                            .render(HtmlUtf8BufferRockerOutput.threadLocalFactory())
+                            .buffer();
                 });
     }
 }

+ 2 - 9
frameworks/Java/quarkus/resteasy-reactive-hibernate-reactive/src/main/java/io/quarkus/benchmark/resource/PlaintextResource.java

@@ -7,21 +7,14 @@ import jakarta.ws.rs.Path;
 import jakarta.ws.rs.Produces;
 import jakarta.ws.rs.core.MediaType;
 
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufAllocator;
 import io.smallrye.common.annotation.NonBlocking;
 import io.vertx.core.buffer.Buffer;
 
 @Path("/plaintext")
 public class PlaintextResource {
-    private static final String HELLO_WORLD = "Hello, world!";
-    private static final Buffer HELLO_WORLD_BUFFER;
 
-    static {
-        ByteBuf nettyBuffer = ByteBufAllocator.DEFAULT.directBuffer();
-        nettyBuffer.writeBytes(HELLO_WORLD.getBytes(StandardCharsets.UTF_8));
-        HELLO_WORLD_BUFFER = Buffer.buffer(nettyBuffer);
-    }
+    // We prefer an heap Buffer, because Resteasy-Reactive would perform Buffer::getBytes on it
+    private static final Buffer HELLO_WORLD_BUFFER = Buffer.buffer("Hello, world!".getBytes(StandardCharsets.UTF_8));
 
     @Produces(MediaType.TEXT_PLAIN)
     @GET

+ 2 - 0
frameworks/Java/quarkus/resteasy-reactive-hibernate-reactive/src/main/resources/application.properties

@@ -26,3 +26,5 @@ quarkus.vertx.prefer-native-transport=true
 
 # Resteasy-reactive config to always send Content-Length on HTTP res and avoid chunked res
 quarkus.resteasy-reactive.output-buffer-size=20480
+# Disable Arc context propagation
+quarkus.arc.context-propagation.enabled=false

+ 14 - 0
frameworks/Java/quarkus/resteasy-reactive-hibernate/pom.xml

@@ -40,6 +40,12 @@
             <groupId>io.vertx</groupId>
             <artifactId>vertx-web-templ-rocker</artifactId>
         </dependency>
+        <!-- this is necessary to enable rocker to use GuavaHtmlStringify.java -->
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+            <version>32.0.0-jre</version>
+        </dependency>
         <dependency>
             <groupId>io.netty</groupId>
             <artifactId>netty-transport-native-epoll</artifactId>
@@ -68,6 +74,14 @@
                         </configuration>
                     </execution>
                 </executions>
+                <dependencies>
+                    <!-- this is necessary to enable rocker to use GuavaHtmlStringify.java -->
+                    <dependency>
+                        <groupId>com.google.guava</groupId>
+                        <artifactId>guava</artifactId>
+                        <version>32.0.0-jre</version>
+                    </dependency>
+                </dependencies>
             </plugin>
         </plugins>
 

+ 3 - 1
frameworks/Java/quarkus/resteasy-reactive-hibernate/src/main/java/io/quarkus/benchmark/filter/ServerHeaderFilter.java

@@ -4,6 +4,7 @@ import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 
 import jakarta.annotation.PostConstruct;
+import jakarta.inject.Singleton;
 
 import org.jboss.resteasy.reactive.server.ServerResponseFilter;
 
@@ -12,6 +13,7 @@ import io.vertx.core.MultiMap;
 import io.vertx.core.http.HttpHeaders;
 import io.vertx.core.http.HttpServerResponse;
 
+@Singleton
 public class ServerHeaderFilter {
 
     private static final CharSequence SERVER_HEADER_NAME = HttpHeaders.createOptimized("Server");
@@ -37,4 +39,4 @@ public class ServerHeaderFilter {
         headers.add(DATE_HEADER_NAME, date);
     }
 
-}
+}

+ 26 - 4
frameworks/Java/quarkus/resteasy-reactive-hibernate/src/main/java/io/quarkus/benchmark/resource/FortuneResource.java

@@ -1,9 +1,13 @@
 package io.quarkus.benchmark.resource;
 
+import com.fizzed.rocker.RenderingException;
+import com.fizzed.rocker.runtime.DefaultHtmlStringify;
+import com.fizzed.rocker.runtime.DefaultRockerTemplate;
+import com.fizzed.rocker.runtime.GuavaHtmlStringify;
 import io.quarkus.benchmark.model.Fortune;
 import io.quarkus.benchmark.repository.FortuneRepository;
+import io.quarkus.benchmark.utils.rocker.HtmlUtf8BufferRockerOutput;
 import io.vertx.core.buffer.Buffer;
-import io.vertx.ext.web.templ.rocker.impl.VertxBufferOutput;
 
 import jakarta.inject.Inject;
 import jakarta.inject.Singleton;
@@ -12,6 +16,8 @@ import jakarta.ws.rs.GET;
 import jakarta.ws.rs.Path;
 import jakarta.ws.rs.Produces;
 import jakarta.ws.rs.core.MediaType;
+import views.Fortunes;
+
 import java.util.Comparator;
 import java.util.List;
 
@@ -21,6 +27,16 @@ import java.util.List;
 @Consumes(MediaType.APPLICATION_JSON)
 public class FortuneResource {
 
+    private static final DefaultHtmlStringify STRINGIFY = new GuavaHtmlStringify();
+
+    private static class CustomFortunesTemplate extends Fortunes.Template {
+
+        public CustomFortunesTemplate(Fortunes model) {
+            super(model);
+            __internal.setStringify(STRINGIFY);
+        }
+    }
+
     @Inject
     FortuneRepository repository;
 
@@ -32,9 +48,15 @@ public class FortuneResource {
         List<Fortune> fortunes = repository.findAllStateless();
         fortunes.add(new Fortune(0, "Additional fortune added at request time."));
         fortunes.sort(fortuneComparator);
-        return views.Fortunes.template(fortunes)
-                .render(VertxBufferOutput.FACTORY)
-                .getBuffer();
+        return new Fortunes() {
+
+            @Override
+            protected DefaultRockerTemplate buildTemplate() throws RenderingException {
+                return new CustomFortunesTemplate(this);
+            }
+        }.fortunes(fortunes)
+                .render(HtmlUtf8BufferRockerOutput.threadLocalFactory())
+                .buffer();
     }
 
 }

+ 2 - 9
frameworks/Java/quarkus/resteasy-reactive-hibernate/src/main/java/io/quarkus/benchmark/resource/PlaintextResource.java

@@ -1,7 +1,5 @@
 package io.quarkus.benchmark.resource;
 
-import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufAllocator;
 import io.smallrye.common.annotation.NonBlocking;
 import io.vertx.core.buffer.Buffer;
 
@@ -13,14 +11,9 @@ import java.nio.charset.StandardCharsets;
 
 @Path("/plaintext")
 public class PlaintextResource {
-    private static final String HELLO_WORLD = "Hello, world!";
-    private static final Buffer HELLO_WORLD_BUFFER;
 
-    static {
-        ByteBuf nettyBuffer = ByteBufAllocator.DEFAULT.directBuffer();
-        nettyBuffer.writeBytes(HELLO_WORLD.getBytes(StandardCharsets.UTF_8));
-        HELLO_WORLD_BUFFER = Buffer.buffer(nettyBuffer);
-    }
+    // We prefer an heap Buffer, because Resteasy-Reactive would perform Buffer::getBytes on it
+    private static final Buffer HELLO_WORLD_BUFFER = Buffer.buffer("Hello, world!".getBytes(StandardCharsets.UTF_8));
 
     @Produces(MediaType.TEXT_PLAIN)
     @GET

+ 1 - 1
frameworks/Java/quarkus/resteasy-reactive-hibernate/src/main/resources/application.properties

@@ -30,5 +30,5 @@ quarkus.hibernate-orm.log.sql=false
 
 # Resteasy-reactive config to always send Content-Length on HTTP res and avoid chunked res
 quarkus.resteasy-reactive.output-buffer-size=20480
-
+quarkus.arc.context-propagation.enabled=false