Explorar o código

Performance improvements to vertx-web-kotlin-dsljson (#10281)

- Refactored core verticles and introduced ServerVerticle for improved request handling
- Enhanced database repositories with optimized query patterns
- Streamlined handlers to reduce overhead
- Updated PeriodicResolver logic and added centralized Properties configuration
- Removed unused profiling scripts and configuration files
- Temporarily disabled fortunes endpoint in benchmark configuration
- Updated Gradle build configuration and dependencies

Verify was called to ensure operability.
Andrew McCloskey hai 1 mes
pai
achega
fe1810bf70
Modificáronse 39 ficheiros con 1165 adicións e 605 borrados
  1. 8 2
      frameworks/Kotlin/vertx-web-kotlin-dsljson/README.md
  2. 0 1
      frameworks/Kotlin/vertx-web-kotlin-dsljson/benchmark_config.json
  3. 90 60
      frameworks/Kotlin/vertx-web-kotlin-dsljson/build.gradle.kts
  4. 7 0
      frameworks/Kotlin/vertx-web-kotlin-dsljson/buildSrc/build.gradle.kts
  5. 58 0
      frameworks/Kotlin/vertx-web-kotlin-dsljson/buildSrc/src/main/kotlin/Helper.kt
  6. 72 25
      frameworks/Kotlin/vertx-web-kotlin-dsljson/configuration/scripts/server.sh
  7. 44 0
      frameworks/Kotlin/vertx-web-kotlin-dsljson/gradle/libs.versions.toml
  8. BIN=BIN
      frameworks/Kotlin/vertx-web-kotlin-dsljson/gradle/wrapper/gradle-wrapper.jar
  9. 1 1
      frameworks/Kotlin/vertx-web-kotlin-dsljson/gradle/wrapper/gradle-wrapper.properties
  10. 4 8
      frameworks/Kotlin/vertx-web-kotlin-dsljson/gradlew
  11. 1 2
      frameworks/Kotlin/vertx-web-kotlin-dsljson/gradlew.bat
  12. 14 0
      frameworks/Kotlin/vertx-web-kotlin-dsljson/settings.gradle.kts
  13. 62 32
      frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/App.kt
  14. 27 37
      frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/BasicVerticle.kt
  15. 45 63
      frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/PostgresVerticle.kt
  16. 91 0
      frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/ServerVerticle.kt
  17. 22 6
      frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/db/FortuneRepository.kt
  18. 63 32
      frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/db/WorldRepository.kt
  19. 37 42
      frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/handlers/AbstractHandler.kt
  20. 10 6
      frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/handlers/DefaultHandler.kt
  21. 27 44
      frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/handlers/FortuneHandler.kt
  22. 10 5
      frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/handlers/MessageHandler.kt
  23. 34 31
      frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/handlers/WorldHandler.kt
  24. 93 69
      frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/helpers/BufferOutputStream.kt
  25. 61 0
      frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/helpers/PeriodicResolver.kt
  26. 171 0
      frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/helpers/Properties.kt
  27. 0 15
      frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/io/JsonResource.kt
  28. 2 2
      frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/models/Fortune.kt
  29. 1 1
      frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/models/Message.kt
  30. 3 5
      frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/models/World.kt
  31. 8 8
      frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/utils/FutureExtensions.kt
  32. 2 3
      frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/utils/JsonExtensions.kt
  33. 0 19
      frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/utils/PeriodicDateResolver.kt
  34. 3 6
      frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/utils/RowSetExtensions.kt
  35. 4 6
      frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/utils/ThrowableExtensions.kt
  36. 0 6
      frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/resources/http-server-options.json
  37. 0 12
      frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/resources/pg-connect-options.json
  38. 45 28
      frameworks/Kotlin/vertx-web-kotlin-dsljson/vertx-web-kotlin-dsljson-postgresql.dockerfile
  39. 45 28
      frameworks/Kotlin/vertx-web-kotlin-dsljson/vertx-web-kotlin-dsljson.dockerfile

+ 8 - 2
frameworks/Kotlin/vertx-web-kotlin-dsljson/README.md

@@ -31,6 +31,12 @@ http://localhost:8080/query?queries=
 
 http://localhost:8080/update?queries=
 
-### FORTUNES
 
-http://localhost:8080/fortunes
+Testing:
+
+```shell
+../../../tfb \
+    --mode benchmark \
+    --test vertx-web-kotlin-dsljson \
+    --type json
+```

+ 0 - 1
frameworks/Kotlin/vertx-web-kotlin-dsljson/benchmark_config.json

@@ -24,7 +24,6 @@
       "postgresql": {
         "db_url": "/db",
         "query_url": "/queries?queries=",
-        "fortune_url": "/fortunes",
         "update_url": "/updates?queries=",
         "port": 8080,
         "approach": "Realistic",

+ 90 - 60
frameworks/Kotlin/vertx-web-kotlin-dsljson/build.gradle.kts

@@ -1,86 +1,116 @@
-import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
 import org.jetbrains.kotlin.gradle.dsl.JvmTarget
 
 plugins {
-    kotlin("jvm") version "1.9.22"
-    kotlin("kapt") version "1.9.22"
+    alias(libs.plugins.kotlin.jvm)
+    alias(libs.plugins.kotlin.kapt)
+    alias(libs.plugins.shadow)
     application
-    id("com.github.johnrengelman.shadow") version "7.1.2"
 }
 
 group = "com.example"
 version = "1.0.0-SNAPSHOT"
 
-repositories {
-    mavenCentral()
-}
-
 java {
     toolchain {
-        languageVersion.set(JavaLanguageVersion.of(21))
+        languageVersion.set(JavaLanguageVersion.of(25))
     }
 }
 
 kotlin {
     compilerOptions {
-        jvmTarget = JvmTarget.JVM_21
+        jvmTarget = JvmTarget.JVM_25
+        apiVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_3)
+        languageVersion.set(org.jetbrains.kotlin.gradle.dsl.KotlinVersion.KOTLIN_2_3)
+        freeCompilerArgs.addAll(listOf(
+            "-Xjvm-default=all",
+            "-Xlambdas=indy",
+            "-Xjdk-release=25"
+        ))
     }
-    jvmToolchain(21)
+    jvmToolchain(25)
 }
 
-val mainClassName = "com.example.starter.App"
-
-val vertxVersion = "4.5.9"
-val nettyVersion = "4.1.112.Final"
-val scramVersion = "2.1"
-val dslJsonVersion = "2.0.2"
-val htmlFlowVersion = "4.6"
-val log4jVersion = "2.23.1"
-
 application {
-    mainClass = mainClassName
+    mainClass = "com.example.starter.AppKt"
 }
 
 dependencies {
-    listOfNotNull(
-        // Kotlin
-        kotlin("stdlib-jdk8"),
-        kotlin("reflect"),
-
-        // Vertx
-        platform("io.vertx:vertx-stack-depchain:$vertxVersion"),
-        "io.vertx:vertx-core",
-        "io.vertx:vertx-web",
-        "io.vertx:vertx-pg-client",
-        "io.vertx:vertx-lang-kotlin",
-        "io.vertx:vertx-lang-kotlin-coroutines",
-
-        // Netty
-        "io.netty:netty-transport-native-epoll:$nettyVersion:linux-x86_64",
-
-        // Postgres
-        "com.ongres.scram:client:$scramVersion",
-
-        // dsljson
-        "com.dslplatform:dsl-json:$dslJsonVersion",
-
-        // HtmlFlow
-        "com.github.xmlet:htmlflow:$htmlFlowVersion",
-
-        // Logging
-        "org.apache.logging.log4j:log4j-core:$log4jVersion",
-        "org.apache.logging.log4j:log4j-api:$log4jVersion",
-        "org.apache.logging.log4j:log4j-api-kotlin:1.4.0",
-        "com.lmax:disruptor:4.0.0",
-    ).map { implementation(it) }
-
-    listOf(
-        "com.dslplatform:dsl-json:$dslJsonVersion",
-        "org.apache.logging.log4j:log4j-core:$log4jVersion",
-    ).map { kapt(it) }
+    // Kotlin
+    implementation(libs.kotlin.stdlib)
+    implementation(libs.kotlin.reflect)
+
+    // Vert.x
+    implementation(platform(libs.vertx.bom))
+    implementation(libs.vertx.core)
+    implementation(libs.vertx.web)
+    implementation(libs.vertx.pg.client)
+    implementation(libs.vertx.lang.kotlin)
+    implementation(libs.vertx.lang.kotlin.coroutines)
+    implementation(libs.vertx.micrometer)
+
+    // Micrometer
+    implementation(libs.micrometer.registry.prometheus)
+
+    // Netty
+    implementation(platform(libs.netty.bom))
+    resolvePlatformSpecificNettyDependencies(libs.versions.netty.get())
+        .forEach { implementation(it) }
+
+    // DSL-JSON
+    implementation(libs.dsl.json)
+    kapt(libs.dsl.json)
+
+    // Log4j
+    implementation(libs.log4j.core)
+    implementation(libs.log4j.api)
+    implementation(libs.log4j.api.kotlin)
+    implementation(libs.disruptor)
 }
 
-tasks.withType<ShadowJar> {
-    archiveClassifier = "fat"
-    mergeServiceFiles()
+tasks {
+    register<JavaExec>("server") {
+        dependsOn([email protected])
+
+        mainClass.set(application.mainClass.get())
+        classpath = sourceSets.main.get().runtimeClasspath
+
+        jvmArgs = listOf(
+            "-server",
+            "--enable-native-access=ALL-UNNAMED",
+            "--add-opens=java.base/java.lang=ALL-UNNAMED",
+            "--sun-misc-unsafe-memory-access=allow",
+            "-Xms2G",
+            "-Xmx2G",
+            "-XX:+AlwaysPreTouch",
+            "-XX:+UseParallelGC",
+            "-XX:InitialCodeCacheSize=512m",
+            "-XX:ReservedCodeCacheSize=512m",
+            "-XX:MaxInlineLevel=20",
+            "-XX:+UseNUMA",
+            "-XX:-UseCodeCacheFlushing",
+            "-XX:AutoBoxCacheMax=10001",
+            "-XX:+UseCompactObjectHeaders",
+            "-XX:+UnlockDiagnosticVMOptions",
+            "-XX:+DebugNonSafepoints",
+            "-Djava.net.preferIPv4Stack=true",
+            "-Dvertx.disableMetrics=true",
+            "-Dvertx.disableWebsockets=true",
+            "-Dvertx.disableContextTimings=true",
+            "-Dvertx.cacheImmutableHttpResponseHeaders=true",
+            "-Dvertx.internCommonHttpRequestHeadersToLowerCase=true",
+            "-Dvertx.disableHttpHeadersValidation=true",
+            "-Dio.netty.noUnsafe=false",
+            "-Dio.netty.buffer.checkBounds=false",
+            "-Dio.netty.buffer.checkAccessible=false",
+            "-Dio.netty.leakDetection.level=disabled",
+            "-Dio.netty.iouring.ringSize=4096",
+            "-Dio.netty.iouring.cqSize=8192",
+            "-Dtfb.type=basic",
+        )
+    }
+
+    shadowJar {
+        archiveClassifier = "fat"
+        mergeServiceFiles()
+    }
 }

+ 7 - 0
frameworks/Kotlin/vertx-web-kotlin-dsljson/buildSrc/build.gradle.kts

@@ -0,0 +1,7 @@
+plugins {
+    `kotlin-dsl`
+}
+
+repositories {
+    mavenCentral()
+}

+ 58 - 0
frameworks/Kotlin/vertx-web-kotlin-dsljson/buildSrc/src/main/kotlin/Helper.kt

@@ -0,0 +1,58 @@
+enum class SystemType {
+    LINUX_X86_64,
+    LINUX_AMD64,
+    LINUX_AARCH64,
+    OSX_X86_64,
+    OSX_AARCH64,
+    WINDOWS_AMD64,
+    WINDOWS_AARCH64,
+    UNKNOWN,
+}
+
+data class SystemInfo(
+    val type: SystemType,
+    val os: OperatingSystem,
+) {
+    data class OperatingSystem(
+        val name: String,
+        val arch: String,
+    )
+}
+
+val CURRENT_SYSTEM_INFO by lazy {
+    val os = System.getProperty("os.name").lowercase()
+    val arch = System.getProperty("os.arch").lowercase()
+    val type = when {
+        os.startsWith("linux") && arch == "x86_64" -> SystemType.LINUX_X86_64
+        os.startsWith("linux") && arch == "amd64" -> SystemType.LINUX_AMD64
+        os.startsWith("linux") && arch == "aarch64" -> SystemType.LINUX_AARCH64
+        os.startsWith("mac") && arch == "x86_64" -> SystemType.OSX_X86_64
+        os.startsWith("mac") && arch == "aarch64" -> SystemType.OSX_AARCH64
+        os.startsWith("windows") && arch == "amd64" -> SystemType.WINDOWS_AMD64
+        os.startsWith("windows") && arch == "aarch64" -> SystemType.WINDOWS_AARCH64
+        else -> SystemType.UNKNOWN
+    }
+
+    SystemInfo(
+        type = type,
+        os = SystemInfo.OperatingSystem(
+            name = os,
+            arch = arch,
+        )
+    )
+}
+
+fun resolvePlatformSpecificNettyDependencies(version: String) = when (CURRENT_SYSTEM_INFO.type) {
+    SystemType.LINUX_X86_64,
+    SystemType.LINUX_AMD64 -> arrayOf(
+        "io.netty:netty-transport-native-io_uring:$version:linux-x86_64",
+    )
+    SystemType.LINUX_AARCH64 -> arrayOf(
+        "io.netty:netty-transport-native-io_uring:$version:linux-aarch_64",
+    )
+    SystemType.OSX_AARCH64 -> arrayOf(
+        "io.netty:netty-transport-native-kqueue:$version:osx-aarch_64",
+        "io.netty:netty-resolver-dns-native-macos:$version:osx-aarch_64",
+    )
+    else -> throw IllegalStateException("Unsupported system: $CURRENT_SYSTEM_INFO")
+}

+ 72 - 25
frameworks/Kotlin/vertx-web-kotlin-dsljson/configuration/scripts/server.sh

@@ -1,45 +1,92 @@
 #!/bin/bash
 
-JVM_OPTS="-server \
+set -euo pipefail
+
+if [ -f /proc/cpuinfo ]; then
+  CPU_COUNT=$(grep --count ^processor /proc/cpuinfo)
+else
+  CPU_COUNT=$(sysctl -n hw.ncpu 2>/dev/null || echo 1)
+fi
+
+JAR_PATH="./build/libs/vertx-web-kotlin-dsljson-benchmark-1.0.0-SNAPSHOT-fat.jar"
+PG_DOCKER_PATH="../../../toolset/databases/postgres"
+
+HAS_DB=false
+while [[ $# -gt 0 ]]; do
+  case $1 in
+    -db)
+      HAS_DB=true
+      ;;
+  esac
+  shift
+done
+
+if [ "$HAS_DB" = true ]; then
+  docker image inspect tfb-postgres >/dev/null 2>&1 || \
+    docker build -f "$PG_DOCKER_PATH/postgres.dockerfile" -t tfb-postgres "$PG_DOCKER_PATH"
+
+  # ensure no old container blocks name reuse
+  docker rm -f tfb-postgres >/dev/null 2>&1 || true
+
+  docker run --rm --name tfb-postgres -p 5432:5432 -d tfb-postgres
+
+  until PGPASSWORD=benchmarkdbpass psql -h "0.0.0.0" -U benchmarkdbuser -d hello_world -c '\dt' > /dev/null 2>&1; do
+    sleep 1
+  done
+fi
+
+cleanup() {
+  echo "Caught termination signal. Cleaning up..."
+  if [ -n "${JAVA_PID:-}" ]; then
+    kill -SIGTERM "$JAVA_PID" 2>/dev/null || true
+    wait "$JAVA_PID" 2>/dev/null || true
+  fi
+
+  if [ "$HAS_DB" = true ]; then
+    echo "Stopping postgres container..."
+    docker stop tfb-postgres >/dev/null 2>&1 || true
+    docker rm -f tfb-postgres >/dev/null 2>&1 || true
+  fi
+}
+
+trap cleanup SIGINT SIGTERM EXIT
+
+java \
+  -server \
+  --enable-native-access=ALL-UNNAMED \
+  --add-opens=java.base/java.lang=ALL-UNNAMED \
   -Xms2G \
   -Xmx2G \
   -XX:+AlwaysPreTouch \
-  -XX:+UseParallelGC \
-  -XX:+PreserveFramePointer \
+  -XX:+UseZGC \
+  -XX:+ZUncommit \
+  -XX:+DisableExplicitGC \
+  -XX:+UseLargePages \
+  -XX:+UseStringDeduplication \
   -XX:+EnableDynamicAgentLoading \
   -XX:InitialCodeCacheSize=512m \
   -XX:ReservedCodeCacheSize=512m \
   -XX:MaxInlineLevel=20 \
   -XX:+UseNUMA \
-  -Djava.lang.Integer.IntegerCache.high=10000 \
+  -XX:-UseCodeCacheFlushing \
+  -XX:AutoBoxCacheMax=10001 \
+  -Djava.net.preferIPv4Stack=true \
   -Dvertx.disableMetrics=true \
-  -Dvertx.disableH2c=true \
+  -Dvertx.disableDnsResolver=true \
   -Dvertx.disableWebsockets=true \
-  -Dvertx.flashPolicyHandler=false \
-  -Dvertx.threadChecks=false \
   -Dvertx.disableContextTimings=true \
-  -Dvertx.disableTCCL=true \
+  -Dvertx.cacheImmutableHttpResponseHeaders=true \
+  -Dvertx.internCommonHttpRequestHeadersToLowerCase=true \
   -Dvertx.disableHttpHeadersValidation=true \
-  -Dvertx.eventLoopPoolSize=$((`grep --count ^processor /proc/cpuinfo`)) \
-  -Dlog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector \
+  -Dio.netty.noUnsafe=false \
   -Dio.netty.buffer.checkBounds=false \
   -Dio.netty.buffer.checkAccessible=false \
-  -Dtfb.hasDB=false"
-
-JAR_PATH="./build/libs/vertx-web-kotlin-dsljson-benchmark-1.0.0-SNAPSHOT-fat.jar"
-
-cleanup() {
-    echo "Caught SIGINT signal. Stopping the Java program..."
-    if [ ! -z "$JAVA_PID" ]; then
-        kill -SIGTERM "$JAVA_PID"
-        wait "$JAVA_PID"
-    fi
-    exit 0
-}
-
-trap cleanup SIGINT
+  -Dio.netty.recycler.maxCapacity.default=0 \
+  -Dio.netty.maxDirectMemory=0 \
+  "-Dtfb.hasDB=$HAS_DB" \
+  "-Dtfb.pgHostOverride=0.0.0.0" \
+  -jar "$JAR_PATH" &
 
-java $JVM_OPTS -jar $JAR_PATH &
 JAVA_PID=$!
 
 echo "Server PID: $JAVA_PID"

+ 44 - 0
frameworks/Kotlin/vertx-web-kotlin-dsljson/gradle/libs.versions.toml

@@ -0,0 +1,44 @@
+[versions]
+kotlin = "2.3.0-Beta2"
+shadow = "9.2.2"
+vertx = "5.0.5"
+micrometer = "1.16.0"
+netty = "4.2.7.Final"
+dsljson = "2.0.2"
+log4j = "2.25.2"
+log4j-kotlin = "1.5.0"
+disruptor = "4.0.0"
+
+[libraries]
+# Kotlin
+kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" }
+kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" }
+
+# Vert.x
+vertx-bom = { module = "io.vertx:vertx-stack-depchain", version.ref = "vertx" }
+vertx-core = { module = "io.vertx:vertx-core" }
+vertx-web = { module = "io.vertx:vertx-web" }
+vertx-pg-client = { module = "io.vertx:vertx-pg-client" }
+vertx-lang-kotlin = { module = "io.vertx:vertx-lang-kotlin" }
+vertx-lang-kotlin-coroutines = { module = "io.vertx:vertx-lang-kotlin-coroutines" }
+vertx-micrometer = { module = "io.vertx:vertx-micrometer-metrics" }
+
+# Prometheus
+micrometer-registry-prometheus = { module = "io.micrometer:micrometer-registry-prometheus", version.ref = "micrometer" }
+
+# Netty
+netty-bom = { module = "io.netty:netty-bom", version.ref = "netty" }
+
+# DSL-JSON
+dsl-json = { module = "com.dslplatform:dsl-json", version.ref = "dsljson" }
+
+# Logging
+log4j-core = { module = "org.apache.logging.log4j:log4j-core", version.ref = "log4j" }
+log4j-api = { module = "org.apache.logging.log4j:log4j-api", version.ref = "log4j" }
+log4j-api-kotlin = { module = "org.apache.logging.log4j:log4j-api-kotlin", version.ref = "log4j-kotlin" }
+disruptor = { module = "com.lmax:disruptor", version.ref = "disruptor" }
+
+[plugins]
+kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
+kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin" }
+shadow = { id = "com.gradleup.shadow", version.ref = "shadow" }

BIN=BIN
frameworks/Kotlin/vertx-web-kotlin-dsljson/gradle/wrapper/gradle-wrapper.jar


+ 1 - 1
frameworks/Kotlin/vertx-web-kotlin-dsljson/gradle/wrapper/gradle-wrapper.properties

@@ -1,6 +1,6 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip
 networkTimeout=10000
 validateDistributionUrl=true
 zipStoreBase=GRADLE_USER_HOME

+ 4 - 8
frameworks/Kotlin/vertx-web-kotlin-dsljson/gradlew

@@ -1,7 +1,7 @@
 #!/bin/sh
 
 #
-# Copyright © 2015-2021 the original authors.
+# Copyright © 2015 the original authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -86,8 +86,7 @@ done
 # shellcheck disable=SC2034
 APP_BASE_NAME=${0##*/}
 # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
-APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
-' "$PWD" ) || exit
+APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
 
 # Use the maximum available, or set MAX_FD != -1 to use that value.
 MAX_FD=maximum
@@ -115,7 +114,6 @@ case "$( uname )" in                #(
   NONSTOP* )        nonstop=true ;;
 esac
 
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
 
 
 # Determine the Java command to use to start the JVM.
@@ -173,7 +171,6 @@ fi
 # For Cygwin or MSYS, switch paths to Windows format before running java
 if "$cygwin" || "$msys" ; then
     APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
-    CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
 
     JAVACMD=$( cygpath --unix "$JAVACMD" )
 
@@ -206,15 +203,14 @@ fi
 DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
 
 # Collect all arguments for the java command:
-#   * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
 #     and any embedded shellness will be escaped.
 #   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
 #     treated as '${Hostname}' itself on the command line.
 
 set -- \
         "-Dorg.gradle.appname=$APP_BASE_NAME" \
-        -classpath "$CLASSPATH" \
-        org.gradle.wrapper.GradleWrapperMain \
+        -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
         "$@"
 
 # Stop when "xargs" is not available.

+ 1 - 2
frameworks/Kotlin/vertx-web-kotlin-dsljson/gradlew.bat

@@ -70,11 +70,10 @@ goto fail
 :execute
 @rem Setup the command line
 
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
 
 
 @rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
 
 :end
 @rem End local scope for the variables with windows NT shell

+ 14 - 0
frameworks/Kotlin/vertx-web-kotlin-dsljson/settings.gradle.kts

@@ -1 +1,15 @@
+pluginManagement {
+    repositories {
+        gradlePluginPortal()
+        mavenCentral()
+    }
+}
+
+dependencyResolutionManagement {
+    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+    repositories {
+        mavenCentral()
+    }
+}
+
 rootProject.name = "vertx-web-kotlin-dsljson-benchmark"

+ 62 - 32
frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/App.kt

@@ -1,50 +1,80 @@
 package com.example.starter
 
-import com.example.starter.utils.PeriodicDateResolver
+import com.example.starter.helpers.PeriodicResolver
+import com.example.starter.helpers.Properties
 import com.example.starter.utils.block
 import io.vertx.core.Vertx
 import io.vertx.kotlin.core.deploymentOptionsOf
 import io.vertx.kotlin.core.vertxOptionsOf
+import io.vertx.kotlin.coroutines.coAwait
+import io.vertx.kotlin.micrometer.micrometerMetricsOptionsOf
+import io.vertx.kotlin.micrometer.vertxPrometheusOptionsOf
 import kotlin.time.Duration.Companion.seconds
-import org.apache.logging.log4j.kotlin.Logging
+import kotlinx.coroutines.runBlocking
+import org.apache.logging.log4j.kotlin.logger
 
-object App : Logging {
-    private const val SERVER_NAME = "Vert.x-Web Benchmark"
+private val LOGGER = logger("App")
 
-    @JvmStatic
-    fun main(args: Array<out String?>?) {
-        val eventLoopPoolSize = System.getProperty("vertx.eventLoopPoolSize")?.toInt()
-            ?: Runtime.getRuntime().availableProcessors()
+fun main(): Unit = runBlocking {
+    val vertx = Vertx.vertx(
+        vertxOptionsOf(
+            eventLoopPoolSize = Properties.EVENT_LOOP_POOL_SIZE,
+            preferNativeTransport = true,
+            disableTCCL = true,
+            blockedThreadCheckInterval = 60_000,
+            metricsOptions = if (Properties.METRICS_ENABLED) {
+                micrometerMetricsOptionsOf(
+                    enabled = true,
+                    jvmMetricsEnabled = true,
+                    nettyMetricsEnabled = true,
+                    prometheusOptions = vertxPrometheusOptionsOf(
+                        enabled = true,
+                    ),
+                )
+            } else null
+        )
+    )
 
-        val vertx = Vertx.vertx(
-            vertxOptionsOf(
-                eventLoopPoolSize = eventLoopPoolSize,
-                preferNativeTransport = true,
-            )
+    if (!vertx.isNativeTransportEnabled) {
+        throw IllegalStateException(
+            "Native transport not enabled; missing required dependencies",
+            vertx.unavailableNativeTransportCause(),
         )
-        vertx.exceptionHandler {
-            logger.error(it) { "Vertx unexpected exception:" }
-            vertx.close().block(5.seconds)
-        }
+    }
+
+    vertx.exceptionHandler {
+        LOGGER.error("Vertx unexpected exception", it)
+    }
 
-        // Add the SIGINT handler
-        Runtime.getRuntime().addShutdownHook(Thread {
+    Runtime.getRuntime().addShutdownHook(
+        Thread {
             vertx.close().block(5.seconds)
-        })
+        }
+    )
 
-        // Initialize the periodic date resolver
-        PeriodicDateResolver.init(vertx)
+    PeriodicResolver.init(vertx)
 
-        // Check the type of test that is being run
-        val hasDb = System.getProperty("tfb.hasDB")?.toBoolean() ?: false
+    val options = deploymentOptionsOf(
+        instances = Properties.EVENT_LOOP_POOL_SIZE,
+    )
 
-        vertx.deployVerticle(
-            { if (hasDb) PostgresVerticle() else BasicVerticle() },
-            deploymentOptionsOf(
-                instances = eventLoopPoolSize,
-            )
+    val deployment = when (Properties.TYPE) {
+        "basic" -> vertx.deployVerticle(
+            BasicVerticle::class.java,
+            options
         )
-
-        logger.info("$SERVER_NAME started.")
+        "postgres" -> vertx.deployVerticle(
+            PostgresVerticle::class.java,
+            options
+        )
+        "all" -> vertx.deployVerticle(
+            ServerVerticle::class.java,
+            options
+        )
+        else -> throw IllegalStateException("Unknown deployment type: ${Properties.TYPE}")
     }
-}
+
+    deployment.coAwait()
+
+    LOGGER.info("${Properties.SERVER_NAME} started.")
+}

+ 27 - 37
frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/BasicVerticle.kt

@@ -2,55 +2,45 @@ package com.example.starter
 
 import com.example.starter.handlers.DefaultHandler
 import com.example.starter.handlers.MessageHandler
-import com.example.starter.io.JsonResource
+import com.example.starter.helpers.Properties
 import com.example.starter.utils.isConnectionReset
-import io.vertx.core.AbstractVerticle
-import io.vertx.core.Promise
-import io.vertx.core.http.HttpServerOptions
-import io.vertx.ext.web.Router
+import io.vertx.kotlin.coroutines.CoroutineVerticle
+import io.vertx.kotlin.coroutines.coAwait
 import org.apache.logging.log4j.kotlin.Logging
 
-class BasicVerticle : AbstractVerticle() {
-    override fun start(startPromise: Promise<Void>) {
+class BasicVerticle : CoroutineVerticle() {
+    override suspend fun start() {
         val defaultHandler = DefaultHandler()
         val messageHandler = MessageHandler()
 
-        val router = Router.router(vertx)
-
-        router
-            .get("/plaintext")
-            .handler(defaultHandler::plaintext)
-
-        router
-            .get("/json")
-            .handler(messageHandler::readDefaultMessage)
-
         val server = vertx
-            .createHttpServer(HTTP_SERVER_OPTIONS)
-            .requestHandler(router)
+            .createHttpServer(Properties.HTTP)
+            .requestHandler {
+                val path = it.path()
+                val code = when (path.length) {
+                    10 -> if (path == PLAINTEXT_PATH) 1 else 0
+                    5  -> if (path == JSON_PATH)      2 else 0
+                    else -> 0
+                }
+                when (code) {
+                    1 -> defaultHandler.plaintext(it)
+                    2 -> messageHandler.readDefaultMessage(it)
+                    else -> it.response().setStatusCode(404).end()
+                }
+            }
             .exceptionHandler {
-                if (it.isConnectionReset()) return@exceptionHandler
-                logger.error(it) { "Exception in HttpServer" }
+                if (!it.isConnectionReset()) {
+                    logger.error("Exception in HttpServer", it)
+                }
             }
-
-        server
             .listen()
-            .onSuccess {
-                logger.info { "HTTP server started on port 8080" }
-                startPromise.complete()
-            }
-            .onFailure {
-                logger.error(it.cause) { "Failed to start" }
-                startPromise.fail(it.cause)
-            }
+            .coAwait()
+
+        logger.info("HTTP server started on port ${server.actualPort()}")
     }
 
     companion object : Logging {
-        private const val HTTP_SERVER_OPTIONS_RESOURCE = "http-server-options.json"
-
-        private val HTTP_SERVER_OPTIONS: HttpServerOptions by lazy {
-            val json = JsonResource.of(HTTP_SERVER_OPTIONS_RESOURCE)
-            HttpServerOptions(json)
-        }
+        private const val PLAINTEXT_PATH = "/plaintext"
+        private const val JSON_PATH = "/json"
     }
 }

+ 45 - 63
frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/PostgresVerticle.kt

@@ -4,78 +4,60 @@ import com.example.starter.db.FortuneRepository
 import com.example.starter.db.WorldRepository
 import com.example.starter.handlers.FortuneHandler
 import com.example.starter.handlers.WorldHandler
-import com.example.starter.io.JsonResource
+import com.example.starter.helpers.Properties
 import com.example.starter.utils.isConnectionReset
-import io.vertx.core.AbstractVerticle
-import io.vertx.core.Promise
-import io.vertx.core.http.HttpServerOptions
-import io.vertx.ext.web.Router
-import io.vertx.pgclient.PgConnectOptions
+import io.vertx.kotlin.coroutines.CoroutineVerticle
+import io.vertx.kotlin.coroutines.coAwait
 import io.vertx.pgclient.PgConnection
 import org.apache.logging.log4j.kotlin.Logging
 
-class PostgresVerticle : AbstractVerticle() {
-    override fun start(startPromise: Promise<Void>) {
-        PgConnection.connect(vertx, PG_CONNECT_OPTIONS)
-            .onSuccess { conn ->
-                val fortuneHandler = FortuneHandler(FortuneRepository(conn))
-                val worldHandler = WorldHandler(WorldRepository(conn))
-
-                val router = Router.router(vertx)
-
-                router
-                    .get("/fortunes")
-                    .handler(fortuneHandler::templateFortunes)
-
-                router
-                    .get("/db")
-                    .handler(worldHandler::readRandomWorld)
-
-                router
-                    .get("/queries")
-                    .handler(worldHandler::readRandomWorlds)
-
-                router
-                    .get("/updates")
-                    .handler(worldHandler::updateRandomWorlds)
-
-                val server = vertx
-                    .createHttpServer(HTTP_SERVER_OPTIONS)
-                    .requestHandler(router)
-                    .exceptionHandler {
-                        if (it.isConnectionReset()) return@exceptionHandler
-                        logger.error(it) { "Exception in HttpServer" }
-                    }
-
-                server
-                    .listen()
-                    .onSuccess {
-                        logger.info { "HTTP server started on port 8080" }
-                        startPromise.complete()
-                    }
-                    .onFailure {
-                        logger.error(it) { "Failed to start" }
-                        startPromise.fail(it)
+class PostgresVerticle : CoroutineVerticle() {
+    override suspend fun start() {
+        val conn = PgConnection.connect(vertx, Properties.PG_CONNECT).coAwait()
+
+        val fortuneRepository = FortuneRepository.init(conn)
+        val worldRepository = WorldRepository.init(conn)
+
+        val fortuneHandler = FortuneHandler(fortuneRepository.coAwait())
+        val worldHandler = WorldHandler(worldRepository.coAwait())
+
+        val server = vertx
+            .createHttpServer(Properties.HTTP)
+            .requestHandler {
+                val path = it.path()
+                val code = when (path.length) {
+                    9 -> if (path == FORTUNES_PATH) 1 else 0
+                    3 -> if (path == DB_PATH)       2 else 0
+                    8 -> when (path) {
+                        QUERIES_PATH -> 3
+                        UPDATES_PATH -> 4
+                        else         -> 0
                     }
+                    else -> 0
+                }
+                when (code) {
+                    1 -> fortuneHandler.templateFortunes(it)
+                    2 -> worldHandler.readRandomWorld(it)
+                    3 -> worldHandler.readRandomWorlds(it)
+                    4 -> worldHandler.updateRandomWorlds(it)
+                    else -> it.response().setStatusCode(404).end()
+                }
             }
-            .onFailure {
-                logger.error(it) { "Failed to start" }
-                startPromise.fail(it)
+            .exceptionHandler {
+                if (!it.isConnectionReset()) {
+                    logger.error("Exception in HttpServer", it)
+                }
             }
+            .listen()
+            .coAwait()
+
+        logger.info("HTTP server started on port ${server.actualPort()}")
     }
 
     companion object : Logging {
-        private const val HTTP_SERVER_OPTIONS_RESOURCE = "http-server-options.json"
-        private const val PG_CONNECT_OPTIONS_RESOURCE = "pg-connect-options.json"
-
-        private val HTTP_SERVER_OPTIONS: HttpServerOptions by lazy {
-            val json = JsonResource.of(HTTP_SERVER_OPTIONS_RESOURCE)
-            HttpServerOptions(json)
-        }
-
-        private val PG_CONNECT_OPTIONS: PgConnectOptions by lazy {
-            val json = JsonResource.of(PG_CONNECT_OPTIONS_RESOURCE)
-            PgConnectOptions(json)
-        }
+        private const val FORTUNES_PATH = "/fortunes"
+        private const val DB_PATH = "/db"
+        private const val QUERIES_PATH = "/queries"
+        private const val UPDATES_PATH = "/updates"
     }
 }

+ 91 - 0
frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/ServerVerticle.kt

@@ -0,0 +1,91 @@
+package com.example.starter
+
+import com.example.starter.db.FortuneRepository
+import com.example.starter.db.WorldRepository
+import com.example.starter.handlers.DefaultHandler
+import com.example.starter.handlers.FortuneHandler
+import com.example.starter.handlers.MessageHandler
+import com.example.starter.handlers.WorldHandler
+import com.example.starter.helpers.Properties
+import com.example.starter.utils.isConnectionReset
+import io.micrometer.prometheusmetrics.PrometheusMeterRegistry
+import io.vertx.core.Handler
+import io.vertx.core.http.HttpHeaders.CONTENT_TYPE
+import io.vertx.core.http.HttpServerRequest
+import io.vertx.kotlin.coroutines.CoroutineVerticle
+import io.vertx.kotlin.coroutines.coAwait
+import io.vertx.micrometer.backends.BackendRegistries
+import io.vertx.pgclient.PgConnection
+import org.apache.logging.log4j.kotlin.Logging
+
+class ServerVerticle() : CoroutineVerticle() {
+    override suspend fun start() {
+        val conn = PgConnection.connect(vertx, Properties.PG_CONNECT).coAwait()
+
+        val fortuneRepository = FortuneRepository.init(conn)
+        val worldRepository = WorldRepository.init(conn)
+
+        val fortuneHandler = FortuneHandler(fortuneRepository.coAwait())
+        val worldHandler = WorldHandler(worldRepository.coAwait())
+
+        val defaultHandler = DefaultHandler()
+        val messageHandler = MessageHandler()
+
+        val metricsHandler = if (Properties.METRICS_ENABLED) {
+            val registry = BackendRegistries.getDefaultNow() as PrometheusMeterRegistry
+            Handler<HttpServerRequest> {
+                it.response()
+                    .putHeader(CONTENT_TYPE, "text/plain; version=0.0.4; charset=utf-8")
+                    .end(registry.scrape())
+            }
+        } else null
+
+        val server = vertx
+            .createHttpServer(Properties.HTTP)
+            .requestHandler {
+                val path = it.path()
+                val code = when (path.length) {
+                    10 -> if (path == PLAINTEXT_PATH) 1 else 0
+                    5  -> if (path == JSON_PATH)      2 else 0
+                    9  -> if (path == FORTUNES_PATH)  3 else 0
+                    3  -> if (path == DB_PATH)        4 else 0
+                    8  -> when (path) {
+                        QUERIES_PATH -> 5
+                        UPDATES_PATH -> 6
+                        METRICS_PATH -> 7
+                        else         -> 0
+                    }
+                    else -> 0
+                }
+                when (code) {
+                    1 -> defaultHandler.plaintext(it)
+                    2 -> messageHandler.readDefaultMessage(it)
+                    3 -> fortuneHandler.templateFortunes(it)
+                    4 -> worldHandler.readRandomWorld(it)
+                    5 -> worldHandler.readRandomWorlds(it)
+                    6 -> worldHandler.updateRandomWorlds(it)
+                    7 -> metricsHandler?.handle(it) ?: it.response().setStatusCode(404).end()
+                    else -> it.response().setStatusCode(404).end()
+                }
+            }
+            .exceptionHandler {
+                if (!it.isConnectionReset()) {
+                    logger.error("Exception in HttpServer", it)
+                }
+            }
+            .listen()
+            .coAwait()
+
+        logger.info("HTTP server started on port ${server.actualPort()}")
+    }
+
+    companion object : Logging {
+        private const val METRICS_PATH = "/metrics"
+        private const val PLAINTEXT_PATH = "/plaintext"
+        private const val JSON_PATH = "/json"
+        private const val FORTUNES_PATH = "/fortunes"
+        private const val DB_PATH = "/db"
+        private const val QUERIES_PATH = "/queries"
+        private const val UPDATES_PATH = "/updates"
+    }
+}

+ 22 - 6
frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/db/FortuneRepository.kt

@@ -5,19 +5,35 @@ import com.example.starter.utils.mapToArray
 
 import io.vertx.core.Future
 import io.vertx.pgclient.PgConnection
+import io.vertx.sqlclient.PreparedQuery
 import io.vertx.sqlclient.Row
+import io.vertx.sqlclient.RowSet
 
-class FortuneRepository(conn: PgConnection) : AbstractRepository<Fortune>(conn) {
-    private val selectFortuneQuery = this.conn.preparedQuery(SELECT_FORTUNE_SQL)
+@Suppress("NOTHING_TO_INLINE")
+class FortuneRepository(
+    conn: PgConnection,
+    private val selectFortunesQuery: PreparedQuery<RowSet<Row>>,
+) : AbstractRepository<Fortune>(conn) {
 
-    fun selectFortunes(): Future<Array<Fortune>> = selectFortuneQuery
+    fun selectFortunes(): Future<Array<Fortune>> = selectFortunesQuery
         .execute()
         .map { it.mapToArray(FortuneRepository::map) }
 
     companion object {
-        private const val SELECT_FORTUNE_SQL = "SELECT id, message FROM fortune"
+        private const val SELECT_FORTUNES_SQL = "SELECT id, message FROM fortune"
 
-        @Suppress("NOTHING_TO_INLINE")
-        private inline fun map(row: Row): Fortune = Fortune(row.getInteger(0), row.getString(1))
+        private inline fun map(row: Row): Fortune = Fortune(
+            row.getInteger(0),
+            row.getString(1),
+        )
+
+        fun init(conn: PgConnection): Future<FortuneRepository> = conn.let { conn ->
+            conn.prepare(SELECT_FORTUNES_SQL).map { ps ->
+                FortuneRepository(
+                    conn,
+                    ps.query(),
+                )
+            }
+        }
     }
 }

+ 63 - 32
frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/db/WorldRepository.kt

@@ -3,38 +3,48 @@ package com.example.starter.db
 import com.example.starter.models.World
 import io.vertx.core.Future
 import io.vertx.core.Promise
+import io.vertx.core.impl.future.CompositeFutureImpl
 import io.vertx.pgclient.PgConnection
 import io.vertx.sqlclient.PreparedQuery
 import io.vertx.sqlclient.Row
 import io.vertx.sqlclient.RowSet
 import io.vertx.sqlclient.Tuple
-import io.vertx.sqlclient.impl.ArrayTuple
 import io.vertx.sqlclient.impl.SqlClientInternal
+import io.vertx.sqlclient.internal.ArrayTuple
 import java.util.concurrent.ThreadLocalRandom
 import java.util.concurrent.atomic.AtomicInteger
 
 @Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST")
-class WorldRepository(conn: PgConnection) : AbstractRepository<World>(conn) {
-    private val selectWorldQuery = this.conn.preparedQuery(SELECT_WORLD_SQL)
-    private val updateWorldQueries = generateQueries(this.conn)
-
+class WorldRepository private constructor(
+    conn: PgConnection,
+    private val selectWorldQuery: PreparedQuery<RowSet<Row>>,
+    private val updateWorldQueries: Array<PreparedQuery<RowSet<Row>>>,
+) : AbstractRepository<World>(conn) {
     fun selectRandomWorld(): Future<World> = selectWorldQuery
         .execute(Tuple.of(randomWorld()))
-        .map { map(it.iterator().next()) }
+        .map { map(it.first()) }
 
     fun selectRandomWorlds(numWorlds: Int): Future<Array<World>> {
         val promise = Promise.promise<Array<World>>()
         val arr = arrayOfNulls<World>(numWorlds)
         val count = AtomicInteger(0)
         (this.conn as SqlClientInternal).group { c ->
-            repeat(numWorlds) {
-                c.preparedQuery(SELECT_WORLD_SQL).execute(Tuple.of(randomWorld())) { ar ->
-                    val index = count.getAndIncrement()
-                    arr[index] = map(ar.result().iterator().next())
-                    if (index == numWorlds - 1) {
-                        promise.complete(arr as Array<World>)
+            val query = c.preparedQuery(SELECT_WORLD_SQL)
+            repeat(numWorlds) { _ ->
+                query.execute(Tuple.of(randomWorld()))
+                    .onComplete { ar ->
+                        when {
+                            ar.succeeded() -> {
+                                val result = ar.result()
+                                val index = count.getAndIncrement()
+                                arr[index] = map(result.iterator().next())
+                                if (index == numWorlds - 1) {
+                                    promise.complete(arr as Array<World>)
+                                }
+                            }
+                            else -> promise.fail(ar.cause())
+                        }
                     }
-                }
             }
         }
         return promise.future()
@@ -57,28 +67,49 @@ class WorldRepository(conn: PgConnection) : AbstractRepository<World>(conn) {
     companion object {
         private const val SELECT_WORLD_SQL = "SELECT id, randomnumber FROM world WHERE id = $1"
 
-        private inline fun randomWorld(): Int = 1 + ThreadLocalRandom.current().nextInt(10000)
-        private inline fun map(row: Row): World = World(row.getInteger(0), row.getInteger(1))
+        private inline fun randomWorld(): Int = 1 + ThreadLocalRandom.current().nextInt(10_000)
+        private inline fun map(row: Row): World = World(
+            row.getInteger(0),
+            row.getInteger(1),
+        )
 
-        private fun generateQueries(conn: PgConnection): Array<PreparedQuery<RowSet<Row>>> {
-            val arr = arrayOfNulls<PreparedQuery<RowSet<Row>>>(500)
-            for (num in 1..500) {
-                var paramIndex = 1
-                val sb = StringBuilder()
-                sb.append("UPDATE world SET randomnumber = CASE id ")
-                for (i in 1..num) {
-                    sb.append("WHEN \$$paramIndex THEN \$${paramIndex + 1} ")
-                    paramIndex += 2
-                }
-                sb.append("ELSE randomnumber END WHERE id IN (")
-                for (i in 1..num) {
-                    sb.append("\$$paramIndex,")
-                    paramIndex += 1
+        fun init(conn: PgConnection): Future<WorldRepository> = conn.let { conn ->
+            val selectWorldQuery = conn.prepare(SELECT_WORLD_SQL).map { ps -> ps.query() }
+            val updateWorldQueries = run {
+                val queries = arrayOfNulls<PreparedQuery<RowSet<Row>>>(500)
+                Array(500) { num ->
+                    val count = num + 1
+                    var paramIndex = 1
+                    val sb = StringBuilder()
+                    sb.append("UPDATE world SET randomnumber = CASE id ")
+                    repeat(count) {
+                        sb.append("WHEN $${paramIndex++} THEN $${paramIndex++} ")
+                    }
+                    sb.append("ELSE randomnumber END WHERE id IN (")
+                    repeat(count) {
+                        sb.append("$${paramIndex++},")
+                    }
+                    sb[sb.length - 1] = ')'
+
+                    conn
+                        .prepare(sb.toString())
+                        .map { ps ->
+                            queries[num] = ps.query()
+                        }
+                }.let {
+                    CompositeFutureImpl.all(*it).map {
+                        queries as Array<PreparedQuery<RowSet<Row>>>
+                    }
                 }
-                sb[sb.length - 1] = ')'
-                arr[num - 1] = conn.preparedQuery(sb.toString())
             }
-            return arr as Array<PreparedQuery<RowSet<Row>>>
+
+            CompositeFutureImpl.all(selectWorldQuery, updateWorldQueries).map {
+                WorldRepository(
+                    conn,
+                    selectWorldQuery.result(),
+                    updateWorldQueries.result(),
+                )
+            }
         }
     }
 }

+ 37 - 42
frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/handlers/AbstractHandler.kt

@@ -1,57 +1,52 @@
 package com.example.starter.handlers
 
-import com.example.starter.utils.PeriodicDateResolver
-import io.vertx.core.AsyncResult
-import io.vertx.core.Handler
+import com.example.starter.helpers.PeriodicResolver
+import com.example.starter.helpers.Properties
+import io.netty.handler.codec.http.HttpHeaderNames
+import io.netty.handler.codec.http.HttpHeaderValues
+import io.vertx.core.Future
 import io.vertx.core.MultiMap
+import io.vertx.core.buffer.Buffer
 import io.vertx.core.http.HttpHeaders
+import io.vertx.core.http.HttpServerRequest
 import io.vertx.core.http.HttpServerResponse
-import io.vertx.ext.web.RoutingContext
+import io.vertx.core.internal.buffer.BufferInternal
 
 @Suppress("NOTHING_TO_INLINE")
 abstract class AbstractHandler {
     protected companion object {
-        val NULL_HANDLER: Handler<AsyncResult<Void>>? = null
-
-        const val SOMETHING_WENT_WRONG = "Something went wrong"
+        val SOMETHING_WENT_WRONG: Buffer = BufferInternal.buffer("Something went wrong")
 
         // Headers
-        val SERVER: CharSequence = HttpHeaders.createOptimized("Vert.x-Web")
-        val APPLICATION_JSON: CharSequence = HttpHeaders.createOptimized("application/json")
-        val TEXT_PLAIN: CharSequence = HttpHeaders.createOptimized("text/plain")
-        val TEXT_HTML: CharSequence = HttpHeaders.createOptimized("text/html; charset=utf-8")
+        val SERVER: CharSequence = HttpHeaders.createOptimized(Properties.SERVER_NAME)
 
         inline fun MultiMap.common(): MultiMap = this
-            .add(HttpHeaders.SERVER, SERVER)
-            .add(HttpHeaders.DATE, PeriodicDateResolver.current)
-
-        inline fun RoutingContext.json(): HttpServerResponse {
-            val response = this.response()
-            val headers = response.headers()
-            headers
-                .common()
-                .add(HttpHeaders.CONTENT_TYPE, APPLICATION_JSON)
-            return response
-        }
-
-        inline fun RoutingContext.text(): HttpServerResponse {
-            val response = this.response()
-            val headers = response.headers()
-            headers
-                .common()
-                .add(HttpHeaders.CONTENT_TYPE, TEXT_PLAIN)
-            return response
-        }
-
-        inline fun RoutingContext.html(): HttpServerResponse {
-            val response = this.response()
-            val headers = response.headers()
-            headers
-                .common()
-                .add(HttpHeaders.CONTENT_TYPE, TEXT_HTML)
-            return response
-        }
-
-        inline fun RoutingContext.error(): Unit = this.text().end(SOMETHING_WENT_WRONG, NULL_HANDLER)
+            .add(HttpHeaderNames.SERVER, SERVER)
+            .add(HttpHeaderNames.DATE, PeriodicResolver.current)
+
+        inline fun HttpServerRequest.json(): HttpServerResponse = response()
+            .apply {
+                headers()
+                    .common()
+                    .add(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON)
+            }
+
+        inline fun HttpServerRequest.plaintext(): HttpServerResponse = response()
+            .apply {
+                headers()
+                    .common()
+                    .add(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN)
+            }
+
+        inline fun HttpServerRequest.html(): HttpServerResponse = response()
+            .apply {
+                headers()
+                    .common()
+                    .add(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_HTML)
+            }
+
+        inline fun HttpServerRequest.error(): Future<Void> = plaintext()
+            .setStatusCode(500)
+            .end(SOMETHING_WENT_WRONG)
     }
 }

+ 10 - 6
frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/handlers/DefaultHandler.kt

@@ -1,15 +1,19 @@
 package com.example.starter.handlers
 
+import com.example.starter.helpers.PeriodicResolver
+import io.vertx.core.Future
 import io.vertx.core.buffer.Buffer
-import io.vertx.ext.web.RoutingContext
+import io.vertx.core.http.HttpServerRequest
 
 class DefaultHandler : AbstractHandler() {
-    fun plaintext(ctx: RoutingContext) {
-        ctx.text().end(MESSAGE_BUFFER, NULL_HANDLER)
-    }
+    fun plaintext(req: HttpServerRequest): Future<Void> = req
+        .response().apply {
+            headers().setAll(PeriodicResolver.plaintext)
+        }
+        .end(MESSAGE_BUFFER)
 
     companion object {
-        private const val MESSAGE = "Hello, World!"
-        private val MESSAGE_BUFFER = Buffer.buffer(MESSAGE, "UTF-8")
+        const val MESSAGE: String = "Hello, World!"
+        val MESSAGE_BUFFER: Buffer = Buffer.buffer(MESSAGE, "UTF-8")
     }
 }

+ 27 - 44
frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/handlers/FortuneHandler.kt

@@ -2,56 +2,39 @@ package com.example.starter.handlers
 
 import com.example.starter.db.FortuneRepository
 import com.example.starter.models.Fortune
-import htmlflow.HtmlFlow
-import htmlflow.HtmlView
-import io.vertx.ext.web.RoutingContext
+import io.vertx.core.http.HttpServerRequest
+import java.lang.Exception
 import org.apache.logging.log4j.kotlin.Logging
 
 class FortuneHandler(private val repository: FortuneRepository) : AbstractHandler() {
-    fun templateFortunes(ctx: RoutingContext) {
+    fun templateFortunes(req: HttpServerRequest) {
         repository
             .selectFortunes()
-            .onSuccess {
-                val updatedFortunes = it.plus(Fortune(0, "Additional fortune added at request time."))
-                updatedFortunes.sort()
-                ctx.html().end(TEMPLATE.render(updatedFortunes), NULL_HANDLER)
-            }
-            .onFailure {
-                logger.error(it) { SOMETHING_WENT_WRONG }
-                ctx.error()
+            .onComplete { ar ->
+                when {
+                    ar.succeeded() -> {
+                        try {
+                            val updatedFortunes = ar.result().plus(Fortune(0, "Additional fortune added at request time."))
+                            updatedFortunes.sort()
+
+                            val html = renderFortunes(updatedFortunes)
+                            req.html().end(html)
+                        } catch (ex: Exception) {
+                            logger.error(SOMETHING_WENT_WRONG, ex)
+                            req.error()
+                        }
+                    }
+                    else -> {
+                        logger.error(SOMETHING_WENT_WRONG, ar.cause())
+                        req.error()
+                    }
+                }
             }
     }
 
-    companion object : Logging {
-        private val TEMPLATE: HtmlView<Any> = HtmlFlow
-            .view<Any> { page ->
-                page
-                    .html()
-                        .head()
-                            .title()
-                                .text("Fortunes")
-                            .`__`() // title
-                        .`__`() // head
-                        .body()
-                            .table()
-                                .tr()
-                                    .th().text("id").`__`()
-                                    .th().text("message").`__`()
-                                .`__`() // tr
-                                .dynamic<Array<Fortune>> { container, fortunes ->
-                                    fortunes.forEach {
-                                        container
-                                            .tr()
-                                                .td().text(it.id.toString()).`__`()
-                                                .td().text(it.message).`__`()
-                                            .`__`() // tr
-                                    }
-                                }
-                            .`__`() // table
-                        .`__`() // body
-                    .`__`() // html
-            }
-            .setIndented(false)
-            .threadSafe()
+    private companion object : Logging {
+        private fun renderFortunes(fortunes: Array<Fortune>): String {
+            throw NotImplementedError("Not implemented")
+        }
     }
-}
+}

+ 10 - 5
frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/handlers/MessageHandler.kt

@@ -1,15 +1,20 @@
 package com.example.starter.handlers
 
+import com.example.starter.helpers.PeriodicResolver
 import com.example.starter.models.Message
 import com.example.starter.utils.serialize
-import io.vertx.ext.web.RoutingContext
+import io.vertx.core.Future
+import io.vertx.core.http.HttpServerRequest
 
 class MessageHandler : AbstractHandler() {
-    fun readDefaultMessage(ctx: RoutingContext) {
-        ctx.json().end(DEFAULT_MESSAGE.serialize(), NULL_HANDLER)
-    }
+    fun readDefaultMessage(req: HttpServerRequest): Future<Void> = req
+        .response().apply {
+            headers().setAll(PeriodicResolver.json)
+        }
+        .end(Message(MESSAGE).serialize())
 
     companion object {
-        private val DEFAULT_MESSAGE = Message("Hello, World!")
+        const val MESSAGE: String = "Hello, World!"
+        val DEFAULT_MESSAGE = Message(MESSAGE)
     }
 }

+ 34 - 31
frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/handlers/WorldHandler.kt

@@ -2,55 +2,58 @@ package com.example.starter.handlers
 
 import com.example.starter.db.WorldRepository
 import com.example.starter.utils.serialize
-import io.vertx.ext.web.RoutingContext
+import io.vertx.core.http.HttpServerRequest
 import org.apache.logging.log4j.kotlin.Logging
 
 class WorldHandler(private val repository: WorldRepository) : AbstractHandler() {
-    fun readRandomWorld(ctx: RoutingContext) {
+    fun readRandomWorld(req: HttpServerRequest) {
         repository
             .selectRandomWorld()
-            .onSuccess {
-                ctx.json().end(it.serialize(), NULL_HANDLER)
-            }
-            .onFailure {
-                logger.error(it) { SOMETHING_WENT_WRONG }
-                ctx.error()
+            .onComplete { ar ->
+                when {
+                    ar.succeeded() -> req.json().end(ar.result().serialize())
+                    else -> {
+                        logger.error(SOMETHING_WENT_WRONG, ar.cause())
+                        req.error()
+                    }
+                }
             }
     }
 
-    fun readRandomWorlds(ctx: RoutingContext) {
-        val queries = ctx.queries()
+    fun readRandomWorlds(req: HttpServerRequest) {
         repository
-            .selectRandomWorlds(queries)
-            .onSuccess {
-                ctx.json().end(it.serialize(), NULL_HANDLER)
-            }
-            .onFailure {
-                logger.error(it) { SOMETHING_WENT_WRONG }
-                ctx.error()
+            .selectRandomWorlds(req.queries())
+            .onComplete { ar ->
+                when {
+                    ar.succeeded() -> req.json().end(ar.result().serialize())
+                    else -> {
+                        logger.error(SOMETHING_WENT_WRONG, ar.cause())
+                        req.error()
+                    }
+                }
             }
     }
 
-    fun updateRandomWorlds(ctx: RoutingContext) {
-        val queries = ctx.queries()
+    fun updateRandomWorlds(req: HttpServerRequest) {
         repository
-            .updateRandomWorlds(queries)
-            .onSuccess {
-                ctx.json().end(it.serialize(), NULL_HANDLER)
-            }
-            .onFailure {
-                logger.error(it) { SOMETHING_WENT_WRONG }
-                ctx.error()
+            .updateRandomWorlds(req.queries())
+            .onComplete { ar ->
+                when {
+                    ar.succeeded() -> req.json().end(ar.result().serialize())
+                    else -> {
+                        logger.error(SOMETHING_WENT_WRONG, ar.cause())
+                        req.error()
+                    }
+                }
             }
     }
 
-    companion object : Logging {
+    private companion object : Logging {
         private const val QUERIES_PARAM_NAME = "queries"
 
         @Suppress("NOTHING_TO_INLINE")
-        private inline fun RoutingContext.queries(): Int {
-            val queriesParam = this.request().getParam(QUERIES_PARAM_NAME)
-            return queriesParam?.toIntOrNull()?.coerceIn(1, 500) ?: 1
-        }
+        private inline fun HttpServerRequest.queries(): Int = getParam(QUERIES_PARAM_NAME)
+            ?.toIntOrNull()
+            ?.coerceIn(1, 500) ?: 1
     }
 }

+ 93 - 69
frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/io/BufferOutputStream.kt → frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/helpers/BufferOutputStream.kt

@@ -1,7 +1,8 @@
-package com.example.starter.io
+package com.example.starter.helpers
 
 import io.netty.buffer.ByteBuf
 import io.vertx.core.buffer.Buffer
+import io.vertx.core.internal.buffer.BufferInternal
 import io.vertx.core.json.JsonArray
 import io.vertx.core.json.JsonObject
 import java.io.IOException
@@ -9,8 +10,8 @@ import java.io.OutputStream
 import java.nio.ByteBuffer
 import java.nio.charset.Charset
 
-class BufferOutputStream(initialSizeHint: Int = 0) : Buffer, OutputStream() {
-    private val buffer: Buffer = Buffer.buffer(initialSizeHint)
+class BufferOutputStream(initialSizeHint: Int = 256) : BufferInternal, OutputStream() {
+    private val buffer: BufferInternal = BufferInternal.buffer(initialSizeHint)
 
     @Throws(IOException::class)
     override fun write(b: Int) {
@@ -27,23 +28,15 @@ class BufferOutputStream(initialSizeHint: Int = 0) : Buffer, OutputStream() {
         buffer.appendBytes(bytes, offset, len)
     }
 
-    override fun toString(enc: String?): String {
+    override fun toString(enc: String): String {
         return buffer.toString(enc)
     }
 
-    override fun toString(enc: Charset?): String {
+    override fun toString(enc: Charset): String {
         return buffer.toString(enc)
     }
 
-    override fun writeToBuffer(other: Buffer?) {
-        buffer.writeToBuffer(other)
-    }
-
-    override fun readFromBuffer(pos: Int, other: Buffer?): Int {
-        return buffer.readFromBuffer(pos, other)
-    }
-
-    override fun copy(): Buffer {
+    override fun copy(): BufferInternal {
         return buffer.copy()
     }
 
@@ -91,10 +84,18 @@ class BufferOutputStream(initialSizeHint: Int = 0) : Buffer, OutputStream() {
         return buffer.getDouble(pos)
     }
 
+    override fun getDoubleLE(pos: Int): Double {
+        return buffer.getDoubleLE(pos)
+    }
+
     override fun getFloat(pos: Int): Float {
         return buffer.getFloat(pos)
     }
 
+    override fun getFloatLE(pos: Int): Float {
+        return buffer.getFloatLE(pos)
+    }
+
     override fun getShort(pos: Int): Short {
         return buffer.getShort(pos)
     }
@@ -135,19 +136,19 @@ class BufferOutputStream(initialSizeHint: Int = 0) : Buffer, OutputStream() {
         return buffer.getBytes(start, end)
     }
 
-    override fun getBytes(dst: ByteArray?): Buffer {
+    override fun getBytes(dst: ByteArray): Buffer {
         return buffer.getBytes(dst)
     }
 
-    override fun getBytes(dst: ByteArray?, dstIndex: Int): Buffer {
+    override fun getBytes(dst: ByteArray, dstIndex: Int): Buffer {
         return buffer.getBytes(dst, dstIndex)
     }
 
-    override fun getBytes(start: Int, end: Int, dst: ByteArray?): Buffer {
+    override fun getBytes(start: Int, end: Int, dst: ByteArray): Buffer {
         return buffer.getBytes(start, end, dst)
     }
 
-    override fun getBytes(start: Int, end: Int, dst: ByteArray?, dstIndex: Int): Buffer {
+    override fun getBytes(start: Int, end: Int, dst: ByteArray, dstIndex: Int): Buffer {
         return buffer.getBytes(start, end, dst, dstIndex)
     }
 
@@ -155,7 +156,7 @@ class BufferOutputStream(initialSizeHint: Int = 0) : Buffer, OutputStream() {
         return buffer.getBuffer(start, end)
     }
 
-    override fun getString(start: Int, end: Int, enc: String?): String {
+    override fun getString(start: Int, end: Int, enc: String): String {
         return buffer.getString(start, end, enc)
     }
 
@@ -163,183 +164,199 @@ class BufferOutputStream(initialSizeHint: Int = 0) : Buffer, OutputStream() {
         return buffer.getString(start, end)
     }
 
-    override fun appendBuffer(buff: Buffer?): Buffer {
-        return buffer.appendBuffer(buff)
+    override fun appendBuffer(b: Buffer): BufferInternal {
+        return buffer.appendBuffer(b)
     }
 
-    override fun appendBuffer(buff: Buffer?, offset: Int, len: Int): Buffer {
-        return buffer.appendBuffer(buff, offset, len)
+    override fun appendBuffer(b: Buffer, offset: Int, len: Int): BufferInternal {
+        return buffer.appendBuffer(b, offset, len)
     }
 
-    override fun appendBytes(bytes: ByteArray?): Buffer {
+    override fun appendBytes(bytes: ByteArray): BufferInternal {
         return buffer.appendBytes(bytes)
     }
 
-    override fun appendBytes(bytes: ByteArray?, offset: Int, len: Int): Buffer {
+    override fun appendBytes(bytes: ByteArray, offset: Int, len: Int): BufferInternal {
         return buffer.appendBytes(bytes, offset, len)
     }
 
-    override fun appendByte(b: Byte): Buffer {
+    override fun appendByte(b: Byte): BufferInternal {
         return buffer.appendByte(b)
     }
 
-    override fun appendUnsignedByte(b: Short): Buffer {
+    override fun appendUnsignedByte(b: Short): BufferInternal {
         return buffer.appendUnsignedByte(b)
     }
 
-    override fun appendInt(i: Int): Buffer {
+    override fun appendInt(i: Int): BufferInternal {
         return buffer.appendInt(i)
     }
 
-    override fun appendIntLE(i: Int): Buffer {
+    override fun appendIntLE(i: Int): BufferInternal {
         return buffer.appendIntLE(i)
     }
 
-    override fun appendUnsignedInt(i: Long): Buffer {
+    override fun appendUnsignedInt(i: Long): BufferInternal {
         return buffer.appendUnsignedInt(i)
     }
 
-    override fun appendUnsignedIntLE(i: Long): Buffer {
+    override fun appendUnsignedIntLE(i: Long): BufferInternal {
         return buffer.appendUnsignedIntLE(i)
     }
 
-    override fun appendMedium(i: Int): Buffer {
+    override fun appendMedium(i: Int): BufferInternal {
         return buffer.appendMedium(i)
     }
 
-    override fun appendMediumLE(i: Int): Buffer {
+    override fun appendMediumLE(i: Int): BufferInternal {
         return buffer.appendMediumLE(i)
     }
 
-    override fun appendLong(l: Long): Buffer {
+    override fun appendLong(l: Long): BufferInternal {
         return buffer.appendLong(l)
     }
 
-    override fun appendLongLE(l: Long): Buffer {
+    override fun appendLongLE(l: Long): BufferInternal {
         return buffer.appendLongLE(l)
     }
 
-    override fun appendShort(s: Short): Buffer {
+    override fun appendShort(s: Short): BufferInternal {
         return buffer.appendShort(s)
     }
 
-    override fun appendShortLE(s: Short): Buffer {
+    override fun appendShortLE(s: Short): BufferInternal {
         return buffer.appendShortLE(s)
     }
 
-    override fun appendUnsignedShort(s: Int): Buffer {
+    override fun appendUnsignedShort(s: Int): BufferInternal {
         return buffer.appendUnsignedShort(s)
     }
 
-    override fun appendUnsignedShortLE(s: Int): Buffer {
+    override fun appendUnsignedShortLE(s: Int): BufferInternal {
         return buffer.appendUnsignedShortLE(s)
     }
 
-    override fun appendFloat(f: Float): Buffer {
+    override fun appendFloat(f: Float): BufferInternal {
         return buffer.appendFloat(f)
     }
 
-    override fun appendDouble(d: Double): Buffer {
+    override fun appendFloatLE(f: Float): BufferInternal {
+        return buffer.appendFloatLE(f)
+    }
+
+    override fun appendDouble(d: Double): BufferInternal {
         return buffer.appendDouble(d)
     }
 
-    override fun appendString(str: String?, enc: String?): Buffer {
+    override fun appendDoubleLE(d: Double): BufferInternal {
+        return buffer.appendDoubleLE(d)
+    }
+
+    override fun appendString(str: String, enc: String): BufferInternal {
         return buffer.appendString(str, enc)
     }
 
-    override fun appendString(str: String?): Buffer {
+    override fun appendString(str: String): BufferInternal {
         return buffer.appendString(str)
     }
 
-    override fun setByte(pos: Int, b: Byte): Buffer {
+    override fun setByte(pos: Int, b: Byte): BufferInternal {
         return buffer.setByte(pos, b)
     }
 
-    override fun setUnsignedByte(pos: Int, b: Short): Buffer {
+    override fun setUnsignedByte(pos: Int, b: Short): BufferInternal {
         return buffer.setUnsignedByte(pos, b)
     }
 
-    override fun setInt(pos: Int, i: Int): Buffer {
+    override fun setInt(pos: Int, i: Int): BufferInternal {
         return buffer.setInt(pos, i)
     }
 
-    override fun setIntLE(pos: Int, i: Int): Buffer {
+    override fun setIntLE(pos: Int, i: Int): BufferInternal {
         return buffer.setIntLE(pos, i)
     }
 
-    override fun setUnsignedInt(pos: Int, i: Long): Buffer {
+    override fun setUnsignedInt(pos: Int, i: Long): BufferInternal {
         return buffer.setUnsignedInt(pos, i)
     }
 
-    override fun setUnsignedIntLE(pos: Int, i: Long): Buffer {
+    override fun setUnsignedIntLE(pos: Int, i: Long): BufferInternal {
         return buffer.setUnsignedIntLE(pos, i)
     }
 
-    override fun setMedium(pos: Int, i: Int): Buffer {
+    override fun setMedium(pos: Int, i: Int): BufferInternal {
         return buffer.setMedium(pos, i)
     }
 
-    override fun setMediumLE(pos: Int, i: Int): Buffer {
+    override fun setMediumLE(pos: Int, i: Int): BufferInternal {
         return buffer.setMediumLE(pos, i)
     }
 
-    override fun setLong(pos: Int, l: Long): Buffer {
+    override fun setLong(pos: Int, l: Long): BufferInternal {
         return buffer.setLong(pos, l)
     }
 
-    override fun setLongLE(pos: Int, l: Long): Buffer {
+    override fun setLongLE(pos: Int, l: Long): BufferInternal {
         return buffer.setLongLE(pos, l)
     }
 
-    override fun setDouble(pos: Int, d: Double): Buffer {
+    override fun setDouble(pos: Int, d: Double): BufferInternal {
         return buffer.setDouble(pos, d)
     }
 
-    override fun setFloat(pos: Int, f: Float): Buffer {
+    override fun setDoubleLE(pos: Int, d: Double): BufferInternal {
+        return buffer.setDoubleLE(pos, d)
+    }
+
+    override fun setFloat(pos: Int, f: Float): BufferInternal {
         return buffer.setFloat(pos, f)
     }
 
-    override fun setShort(pos: Int, s: Short): Buffer {
+    override fun setFloatLE(pos: Int, f: Float): BufferInternal {
+        return buffer.setFloatLE(pos, f)
+    }
+
+    override fun setShort(pos: Int, s: Short): BufferInternal {
         return buffer.setShort(pos, s)
     }
 
-    override fun setShortLE(pos: Int, s: Short): Buffer {
+    override fun setShortLE(pos: Int, s: Short): BufferInternal {
         return buffer.setShortLE(pos, s)
     }
 
-    override fun setUnsignedShort(pos: Int, s: Int): Buffer {
+    override fun setUnsignedShort(pos: Int, s: Int): BufferInternal {
         return buffer.setUnsignedShort(pos, s)
     }
 
-    override fun setUnsignedShortLE(pos: Int, s: Int): Buffer {
+    override fun setUnsignedShortLE(pos: Int, s: Int): BufferInternal {
         return buffer.setUnsignedShortLE(pos, s)
     }
 
-    override fun setBuffer(pos: Int, b: Buffer?): Buffer {
+    override fun setBuffer(pos: Int, b: Buffer): BufferInternal {
         return buffer.setBuffer(pos, b)
     }
 
-    override fun setBuffer(pos: Int, b: Buffer?, offset: Int, len: Int): Buffer {
+    override fun setBuffer(pos: Int, b: Buffer, offset: Int, len: Int): BufferInternal {
         return buffer.setBuffer(pos, b, offset, len)
     }
 
-    override fun setBytes(pos: Int, b: ByteBuffer?): Buffer {
+    override fun setBytes(pos: Int, b: ByteBuffer): BufferInternal {
         return buffer.setBytes(pos, b)
     }
 
-    override fun setBytes(pos: Int, b: ByteArray?): Buffer {
+    override fun setBytes(pos: Int, b: ByteArray): BufferInternal {
         return buffer.setBytes(pos, b)
     }
 
-    override fun setBytes(pos: Int, b: ByteArray?, offset: Int, len: Int): Buffer {
+    override fun setBytes(pos: Int, b: ByteArray, offset: Int, len: Int): BufferInternal {
         return buffer.setBytes(pos, b, offset, len)
     }
 
-    override fun setString(pos: Int, str: String?): Buffer {
+    override fun setString(pos: Int, str: String): BufferInternal {
         return buffer.setString(pos, str)
     }
 
-    override fun setString(pos: Int, str: String?, enc: String?): Buffer {
+    override fun setString(pos: Int, str: String, enc: String): BufferInternal {
         return buffer.setString(pos, str, enc)
     }
 
@@ -347,16 +364,23 @@ class BufferOutputStream(initialSizeHint: Int = 0) : Buffer, OutputStream() {
         return buffer.length()
     }
 
-    override fun slice(): Buffer {
+    override fun slice(): BufferInternal {
         return buffer.slice()
     }
 
-    override fun slice(start: Int, end: Int): Buffer {
+    override fun slice(start: Int, end: Int): BufferInternal {
         return buffer.slice(start, end)
     }
 
-    @Deprecated("Deprecated in Java")
     override fun getByteBuf(): ByteBuf {
         return buffer.byteBuf
     }
+
+    override fun writeToBuffer(b: Buffer) {
+        return buffer.writeToBuffer(b)
+    }
+
+    override fun readFromBuffer(pos: Int, b: Buffer): Int {
+        return buffer.readFromBuffer(pos, b)
+    }
 }

+ 61 - 0
frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/helpers/PeriodicResolver.kt

@@ -0,0 +1,61 @@
+package com.example.starter.helpers
+
+import com.example.starter.handlers.DefaultHandler
+import com.example.starter.handlers.MessageHandler
+import com.example.starter.utils.serialize
+import io.vertx.core.MultiMap
+import io.vertx.core.Vertx
+import io.vertx.core.http.HttpHeaders
+import java.time.Instant
+import java.time.ZoneOffset
+import java.time.format.DateTimeFormatter
+
+private val FORMATTER = DateTimeFormatter.RFC_1123_DATE_TIME.withZone(ZoneOffset.UTC)
+private val SERVER: CharSequence = HttpHeaders.createOptimized(Properties.SERVER_NAME)
+private val CONTENT_TYPE_TEXT_PLAIN: CharSequence = HttpHeaders.createOptimized("text/plain")
+private val CONTENT_TYPE_APPLICATION_JSON: CharSequence = HttpHeaders.createOptimized("application/json")
+private val PLAINTEXT_CONTENT_LENGTH: CharSequence= HttpHeaders.createOptimized(DefaultHandler.MESSAGE.length.toString())
+private val JSON_CONTENT_LENGTH: CharSequence = HttpHeaders.createOptimized(MessageHandler.DEFAULT_MESSAGE.serialize().length().toString())
+
+@Suppress("NOTHING_TO_INLINE")
+object PeriodicResolver {
+    @Volatile
+    var current: CharSequence = next()
+        private set
+
+    @Volatile
+    var plaintext: MultiMap = nextPlaintext()
+        private set
+
+    @Volatile
+    var json: MultiMap = nextJson()
+        private set
+
+    fun init(vertx: Vertx) {
+        vertx.setPeriodic(1_000L) {
+            current = next()
+            plaintext = nextPlaintext()
+            json = nextJson()
+        }
+    }
+
+    private fun next(): CharSequence = HttpHeaders.createOptimized(
+        FORMATTER.format(Instant.now())
+    )
+
+    private fun nextPlaintext(): MultiMap = HttpHeaders
+        .headers()
+        .add(HttpHeaders.SERVER, SERVER)
+        .add(HttpHeaders.DATE, current)
+        .add(HttpHeaders.CONTENT_TYPE, CONTENT_TYPE_TEXT_PLAIN)
+        .add(HttpHeaders.CONTENT_LENGTH, PLAINTEXT_CONTENT_LENGTH)
+        .copy(false)
+
+    private fun nextJson(): MultiMap = HttpHeaders
+        .headers()
+        .add(HttpHeaders.SERVER, SERVER)
+        .add(HttpHeaders.DATE, current)
+        .add(HttpHeaders.CONTENT_TYPE, CONTENT_TYPE_APPLICATION_JSON)
+        .add(HttpHeaders.CONTENT_LENGTH, JSON_CONTENT_LENGTH)
+        .copy(false)
+}

+ 171 - 0
frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/helpers/Properties.kt

@@ -0,0 +1,171 @@
+package com.example.starter.helpers
+
+import io.netty.util.internal.SystemPropertyUtil
+import io.vertx.core.impl.cpu.CpuCoreSensor
+import io.vertx.core.tracing.TracingPolicy
+import io.vertx.kotlin.core.http.httpServerOptionsOf
+import io.vertx.kotlin.pgclient.pgConnectOptionsOf
+
+object Properties {
+
+    /**
+     * The server name (used in headers and logging).
+     * Default: `Vert.x-Web`
+     */
+    val SERVER_NAME: String = SystemPropertyUtil.get("tfb.serverName", "Vert.x-Web")
+
+    /**
+     * Type of verticle to deploy.
+     * Default: all ([com.example.starter.ServerVerticle])
+     */
+    val TYPE: String = SystemPropertyUtil.get("tfb.type", "all")
+
+    /**
+     * Number of event loop threads.
+     * Default: [io.vertx.core.impl.cpu.CpuCoreSensor.availableProcessors]
+     */
+    val EVENT_LOOP_POOL_SIZE: Int = SystemPropertyUtil.getInt("tfb.eventLoopPoolSize", CpuCoreSensor.availableProcessors())
+
+    /**
+     * Whether metrics are enabled.
+     * Default: Reverse of `vertx.disableMetrics` (defaults to false)
+     */
+    val METRICS_ENABLED: Boolean = SystemPropertyUtil.getBoolean("vertx.disableMetrics", false).not()
+
+    /**
+     * Port the HTTP server listens on.
+     * Default: 8080 (tfb.http.port)
+     */
+    val HTTP_PORT: Int = SystemPropertyUtil.getInt("tfb.http.port", 8080)
+
+    /**
+     * Size of TCP send buffer for HTTP connections, in bytes.
+     * Default: 32768 (tfb.http.sendBufferSize)
+     */
+    val HTTP_SEND_BUFFER_SIZE: Int = SystemPropertyUtil.getInt("tfb.http.sendBufferSize", 16 * 1024)
+
+    /**
+     * Size of TCP receive buffer for HTTP connections, in bytes.
+     * Default: 32768 (tfb.http.receiveBufferSize)
+     */
+    val HTTP_RECEIVE_BUFFER_SIZE: Int = SystemPropertyUtil.getInt("tfb.http.receiveBufferSize", 16 * 1024)
+
+    /**
+     * Enables TCP Fast Open on the HTTP server.
+     * Default: true (tfb.http.tcpFastOpen)
+     */
+    val HTTP_TCP_FASTOPEN: Boolean = SystemPropertyUtil.getBoolean("tfb.http.tcpFastOpen", true)
+
+    /**
+     * Enables TCP_NODELAY (disables Nagle) on HTTP connections.
+     * Default: true (tfb.http.tcpNoDelay)
+     */
+    val HTTP_TCP_NODELAY: Boolean = SystemPropertyUtil.getBoolean("tfb.http.tcpNoDelay", true)
+
+    /**
+     * Idle timeout for HTTP connections in seconds.
+     * 0 disables idle timeout.
+     * Default: 0 (tfb.http.idleTimeout)
+     */
+    val HTTP_IDLE_TIMEOUT: Int = SystemPropertyUtil.getInt("tfb.http.idleTimeout", 0)
+
+    /**
+     * Enables SO_REUSEADDR on the HTTP server socket.
+     * Default: true (tfb.http.reuseAddress)
+     */
+    val HTTP_REUSE_ADDRESS: Boolean = SystemPropertyUtil.getBoolean("tfb.http.reuseAddress", true)
+
+    /**
+     * Enables SO_REUSEPORT on the HTTP server socket.
+     * Default: true (tfb.http.reusePort)
+     */
+    val HTTP_REUSE_PORT: Boolean = SystemPropertyUtil.getBoolean("tfb.http.reusePort", true)
+
+    /**
+     * Size of the TCP accept backlog for the HTTP server.
+     * Default: 8192 (tfb.http.acceptBacklog)
+     */
+    val HTTP_ACCEPT_BACKLOG: Int = SystemPropertyUtil.getInt("tfb.http.acceptBacklog", 8192)
+
+    /**
+     * PostgreSQL username used for connections.
+     * Default: benchmarkdbuser (tfb.pg.user)
+     */
+    val PG_USER: String = SystemPropertyUtil.get("tfb.pg.user", "benchmarkdbuser")
+
+    /**
+     * PostgreSQL password used for connections.
+     * Default: benchmarkdbpass (tfb.pg.password)
+     */
+    val PG_PASSWORD: String = SystemPropertyUtil.get("tfb.pg.password", "benchmarkdbpass")
+
+    /**
+     * PostgreSQL host used for connections.
+     * Default: tfb.pgHostOverride system property, otherwise "tfb-database".
+     * Property: tfb.pg.host
+     */
+    val PG_HOST: String = SystemPropertyUtil.get("tfb.pg.host", System.getProperty("tfb.pgHostOverride") ?: "tfb-database")
+
+    /**
+     * PostgreSQL port used for connections.
+     * Default: 5432 (tfb.pg.port)
+     */
+    val PG_PORT: Int = SystemPropertyUtil.getInt("tfb.pg.port", 5432)
+
+    /**
+     * PostgreSQL database name used for connections.
+     * Default: hello_world (tfb.pg.database)
+     */
+    val PG_DATABASE: String = SystemPropertyUtil.get("tfb.pg.database", "hello_world")
+
+    /**
+     * Enables prepared statement caching on the PostgreSQL client.
+     * Default: true (tfb.pg.cachePreparedStatements)
+     */
+    val PG_CACHE_PREPARED_STATEMENTS: Boolean = SystemPropertyUtil.getBoolean("tfb.pg.cachePreparedStatements", true)
+
+    /**
+     * Maximum size of the prepared statement cache.
+     * Default: 1024 (tfb.pg.preparedStatementCacheMaxSize)
+     */
+    val PG_PREPARED_STATEMENT_CACHE_MAX_SIZE: Int =
+        SystemPropertyUtil.getInt("tfb.pg.preparedStatementCacheMaxSize", 1024)
+
+    /**
+     * Max number of in-flight pipelined requests per connection.
+     * Default: 1024 (tfb.pg.pipeliningLimit)
+     */
+    val PG_PIPELINING_LIMIT: Int = SystemPropertyUtil.getInt("tfb.pg.pipeliningLimit", 1024)
+
+    val HTTP by lazy {
+        httpServerOptionsOf(
+            port = HTTP_PORT,
+            sendBufferSize = HTTP_SEND_BUFFER_SIZE,
+            receiveBufferSize = HTTP_RECEIVE_BUFFER_SIZE,
+            tcpFastOpen = HTTP_TCP_FASTOPEN,
+            tcpNoDelay = HTTP_TCP_NODELAY,
+            idleTimeout = HTTP_IDLE_TIMEOUT,
+            reuseAddress = HTTP_REUSE_ADDRESS,
+            reusePort = HTTP_REUSE_PORT,
+            acceptBacklog = HTTP_ACCEPT_BACKLOG,
+            compressionSupported = false,
+            tracingPolicy = TracingPolicy.IGNORE,
+            http2ClearTextEnabled = false,
+            strictThreadMode = true,
+        )
+    }
+
+    val PG_CONNECT by lazy {
+        pgConnectOptionsOf(
+            user = PG_USER,
+            password = PG_PASSWORD,
+            host = PG_HOST,
+            port = PG_PORT,
+            database = PG_DATABASE,
+            cachePreparedStatements = PG_CACHE_PREPARED_STATEMENTS,
+            preparedStatementCacheMaxSize = PG_PREPARED_STATEMENT_CACHE_MAX_SIZE,
+            tracingPolicy = TracingPolicy.IGNORE,
+            pipeliningLimit = PG_PIPELINING_LIMIT
+        )
+    }
+}

+ 0 - 15
frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/io/JsonResource.kt

@@ -1,15 +0,0 @@
-package com.example.starter.io
-
-import io.vertx.core.json.JsonObject
-
-object JsonResource {
-    fun of(resource: String): JsonObject {
-        val classLoader = ClassLoader.getSystemClassLoader()
-        classLoader.getResourceAsStream(resource)?.use { input ->
-            val output = BufferOutputStream()
-            output.write(input.readAllBytes())
-            return output.toJsonObject()
-        }
-        throw IllegalStateException("$resource not found")
-    }
-}

+ 2 - 2
frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/models/Fortune.kt

@@ -5,8 +5,8 @@ import com.dslplatform.json.JsonAttribute
 
 @CompiledJson
 class Fortune(
-    @JsonAttribute(nullable = false) val id: Int,
-    @JsonAttribute(nullable = false) val message: String
+    @field:JsonAttribute(nullable = false) val id: Int,
+    @field:JsonAttribute(nullable = false) val message: String,
 ) : Comparable<Fortune> {
     override fun compareTo(other: Fortune): Int {
         return message.compareTo(other.message)

+ 1 - 1
frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/models/Message.kt

@@ -4,4 +4,4 @@ import com.dslplatform.json.CompiledJson
 import com.dslplatform.json.JsonAttribute
 
 @CompiledJson
-class Message(@JsonAttribute(nullable = false) val message: String)
+class Message(@field:JsonAttribute(nullable = false) val message: String)

+ 3 - 5
frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/models/World.kt

@@ -5,10 +5,8 @@ import com.dslplatform.json.JsonAttribute
 
 @CompiledJson
 class World(
-    @JsonAttribute(nullable = false) val id: Int,
-    @JsonAttribute(nullable = false) var randomNumber: Int
+    @field:JsonAttribute(nullable = false) val id: Int,
+    @field:JsonAttribute(nullable = false) var randomNumber: Int,
 ) : Comparable<World> {
-    override fun compareTo(other: World): Int {
-        return id.compareTo(other.id)
-    }
+    override fun compareTo(other: World): Int = id - other.id
 }

+ 8 - 8
frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/utils/FutureExtensions.kt

@@ -1,14 +1,14 @@
 package com.example.starter.utils
 
-import io.vertx.core.CompositeFuture
 import io.vertx.core.Future
-import java.util.concurrent.TimeUnit
+import io.vertx.kotlin.coroutines.coAwait
 import kotlin.time.Duration
-
-inline fun <reified T> CompositeFuture.array(): Array<T> = Array(this.size()) { this.resultAt(it) }
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.withTimeout
 
 @Suppress("NOTHING_TO_INLINE")
-inline fun <R, T: Future<R>> T.block(duration: Duration): R = this
-    .toCompletionStage()
-    .toCompletableFuture()
-    .get(duration.inWholeMilliseconds, TimeUnit.MILLISECONDS)
+inline fun <R, T: Future<R>> T.block(duration: Duration): R = runBlocking {
+    withTimeout(duration) {
+        coAwait()
+    }
+}

+ 2 - 3
frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/utils/JsonExtensions.kt

@@ -2,17 +2,16 @@ package com.example.starter.utils
 
 import com.dslplatform.json.DslJson
 import com.dslplatform.json.runtime.Settings
-import com.example.starter.io.BufferOutputStream
+import com.example.starter.helpers.BufferOutputStream
 import io.vertx.core.buffer.Buffer
 
 val DSL_JSON: DslJson<Any> = DslJson(
     Settings.withRuntime<Any>()
         .includeServiceLoader()
-        .useStringValuesCache(DslJson.SimpleStringCache())
 )
 
 @Suppress("NOTHING_TO_INLINE")
-inline fun <T> T.serialize(initialSizeHint: Int = 0): Buffer {
+inline fun <T> T.serialize(initialSizeHint: Int = 128): Buffer {
     val output = BufferOutputStream(initialSizeHint)
     DSL_JSON.serialize(this, output)
     return output

+ 0 - 19
frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/utils/PeriodicDateResolver.kt

@@ -1,19 +0,0 @@
-package com.example.starter.utils
-
-import io.vertx.core.Vertx
-import io.vertx.core.http.HttpHeaders
-import java.time.ZonedDateTime
-import java.time.format.DateTimeFormatter
-
-object PeriodicDateResolver {
-    var current: CharSequence = next()
-
-    fun init(vertx: Vertx) {
-        vertx.setPeriodic(1000L) { current = next() }
-    }
-
-    @Suppress("NOTHING_TO_INLINE")
-    private inline fun next(): CharSequence = HttpHeaders.createOptimized(
-        DateTimeFormatter.RFC_1123_DATE_TIME.format(ZonedDateTime.now())
-    )
-}

+ 3 - 6
frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/utils/RowSetExtensions.kt

@@ -5,11 +5,8 @@ import io.vertx.sqlclient.RowSet
 
 // This extension relies on the assumption the mapper never returns null, as it is defined. Otherwise,
 // we prevent the overhead from having to do another iteration over the loop for a `filterNotNull` check.
-@Suppress("UNCHECKED_CAST")
+@Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE")
 inline fun <reified U> RowSet<Row>.mapToArray(mapper: (Row) -> U): Array<U> {
-    val arr = arrayOfNulls<U>(this.size())
-    val iterator = this.iterator()
-    var index = 0
-    while (iterator.hasNext()) arr[index++] = mapper(iterator.next())
-    return arr as Array<U>
+    val iterator = iterator()
+    return Array(size()) { mapper(iterator.next()) }
 }

+ 4 - 6
frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/kotlin/com/example/starter/utils/ThrowableExtensions.kt

@@ -7,10 +7,8 @@ import java.net.SocketException
 const val CONNECTION_RESET_MESSAGE = "Connection reset"
 
 @Suppress("NOTHING_TO_INLINE")
-inline fun Throwable.isConnectionReset(): Boolean {
-    return when {
-        this is NativeIoException && this.expectedErr() == Errors.ERRNO_ECONNRESET_NEGATIVE -> true
-        this is SocketException && this.message == CONNECTION_RESET_MESSAGE -> true
-        else -> false
-    }
+inline fun Throwable.isConnectionReset(): Boolean = when (this) {
+    is NativeIoException if this.expectedErr() == Errors.ERRNO_ECONNRESET_NEGATIVE -> true
+    is SocketException if this.message == CONNECTION_RESET_MESSAGE -> true
+    else -> false
 }

+ 0 - 6
frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/resources/http-server-options.json

@@ -1,6 +0,0 @@
-{
-  "port": 8080,
-  "tcpFastOpen": true,
-  "receiveBufferSize": 262144,
-  "sendBufferSize": 262144
-}

+ 0 - 12
frameworks/Kotlin/vertx-web-kotlin-dsljson/src/main/resources/pg-connect-options.json

@@ -1,12 +0,0 @@
-{
-  "user": "benchmarkdbuser",
-  "password": "benchmarkdbpass",
-  "host": "tfb-database",
-  "port": 5432,
-  "database": "hello_world",
-  "cachePreparedStatements": true,
-  "preparedStatementCacheMaxSize": 1024,
-  "pipeliningLimit": 100000,
-  "receiveBufferSize": 262144,
-  "sendBufferSize": 262144
-}

+ 45 - 28
frameworks/Kotlin/vertx-web-kotlin-dsljson/vertx-web-kotlin-dsljson-postgresql.dockerfile

@@ -1,39 +1,56 @@
-FROM gradle:8.9-jdk21 as gradle
+# --- Build stage with JDK 25 ---
+FROM gradle:9.2-jdk25-corretto AS builder
 
 WORKDIR /vertx-web-kotlin-dsljson
 
 COPY src src
+COPY buildSrc buildSrc
 COPY build.gradle.kts build.gradle.kts
-COPY gradle.properties gradle.properties
 COPY settings.gradle.kts settings.gradle.kts
+COPY gradle.properties gradle.properties
+COPY gradle/libs.versions.toml gradle/libs.versions.toml
+
+RUN gradle shadowJar --no-daemon
+
+# --- Runtime stage using Amazon Corretto 25 ---
+FROM amazoncorretto:25
+
+WORKDIR /app
 
-RUN gradle shadowJar
+COPY --from=builder \
+  /vertx-web-kotlin-dsljson/build/libs/vertx-web-kotlin-dsljson-benchmark-1.0.0-SNAPSHOT-fat.jar \
+  vertx-web-kotlin-dsljson.jar
 
 EXPOSE 8080
 
 CMD java \
-    -server \
-    -Xms2G \
-    -Xmx2G \
-    -XX:+AlwaysPreTouch \
-    -XX:+UseParallelGC \
-    -XX:InitialCodeCacheSize=512m \
-    -XX:ReservedCodeCacheSize=512m \
-    -XX:MaxInlineLevel=20 \
-    -XX:+UseNUMA \
-    -Djava.lang.Integer.IntegerCache.high=10000 \
-    -Dvertx.disableMetrics=true \
-    -Dvertx.disableH2c=true \
-    -Dvertx.disableWebsockets=true \
-    -Dvertx.flashPolicyHandler=false \
-    -Dvertx.threadChecks=false \
-    -Dvertx.disableContextTimings=true \
-    -Dvertx.disableTCCL=true \
-    -Dvertx.disableHttpHeadersValidation=true \
-    -Dvertx.eventLoopPoolSize=$((`grep --count ^processor /proc/cpuinfo`)) \
-    -Dlog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector \
-    -Dio.netty.buffer.checkBounds=false \
-    -Dio.netty.buffer.checkAccessible=false \
-    -Dtfb.hasDB=true \
-    -jar \
-    build/libs/vertx-web-kotlin-dsljson-benchmark-1.0.0-SNAPSHOT-fat.jar
+  -server \
+  --enable-native-access=ALL-UNNAMED \
+  --add-opens=java.base/java.lang=ALL-UNNAMED \
+  --sun-misc-unsafe-memory-access=allow \
+  -Xms2G \
+  -Xmx2G \
+  -XX:+AlwaysPreTouch \
+  -XX:+UseParallelGC \
+  -XX:InitialCodeCacheSize=512m \
+  -XX:ReservedCodeCacheSize=512m \
+  -XX:MaxInlineLevel=20 \
+  -XX:+UseNUMA \
+  -XX:-UseCodeCacheFlushing \
+  -XX:AutoBoxCacheMax=10001 \
+  -XX:+UseCompactObjectHeaders \
+  -Djava.net.preferIPv4Stack=true \
+  -Dvertx.disableMetrics=true \
+  -Dvertx.disableWebsockets=true \
+  -Dvertx.disableContextTimings=true \
+  -Dvertx.cacheImmutableHttpResponseHeaders=true \
+  -Dvertx.internCommonHttpRequestHeadersToLowerCase=true \
+  -Dvertx.disableHttpHeadersValidation=true \
+  -Dio.netty.noUnsafe=false \
+  -Dio.netty.buffer.checkBounds=false \
+  -Dio.netty.buffer.checkAccessible=false \
+  -Dio.netty.leakDetection.level=disabled \
+  -Dio.netty.iouring.ringSize=4096 \
+  -Dio.netty.iouring.cqSize=8192 \
+  -Dtfb.type=postgres \
+  -jar /app/vertx-web-kotlin-dsljson.jar

+ 45 - 28
frameworks/Kotlin/vertx-web-kotlin-dsljson/vertx-web-kotlin-dsljson.dockerfile

@@ -1,39 +1,56 @@
-FROM gradle:8.9-jdk21 as gradle
+# --- Build stage with JDK 25 ---
+FROM gradle:9.2-jdk25-corretto AS builder
 
 WORKDIR /vertx-web-kotlin-dsljson
 
 COPY src src
+COPY buildSrc buildSrc
 COPY build.gradle.kts build.gradle.kts
-COPY gradle.properties gradle.properties
 COPY settings.gradle.kts settings.gradle.kts
+COPY gradle.properties gradle.properties
+COPY gradle/libs.versions.toml gradle/libs.versions.toml
+
+RUN gradle shadowJar --no-daemon
+
+# --- Runtime stage using Amazon Corretto 25 ---
+FROM amazoncorretto:25
+
+WORKDIR /app
 
-RUN gradle shadowJar
+COPY --from=builder \
+  /vertx-web-kotlin-dsljson/build/libs/vertx-web-kotlin-dsljson-benchmark-1.0.0-SNAPSHOT-fat.jar \
+  vertx-web-kotlin-dsljson.jar
 
 EXPOSE 8080
 
 CMD java \
-    -server \
-    -Xms2G \
-    -Xmx2G \
-    -XX:+AlwaysPreTouch \
-    -XX:+UseParallelGC \
-    -XX:InitialCodeCacheSize=512m \
-    -XX:ReservedCodeCacheSize=512m \
-    -XX:MaxInlineLevel=20 \
-    -XX:+UseNUMA \
-    -Djava.lang.Integer.IntegerCache.high=10000 \
-    -Dvertx.disableMetrics=true \
-    -Dvertx.disableH2c=true \
-    -Dvertx.disableWebsockets=true \
-    -Dvertx.flashPolicyHandler=false \
-    -Dvertx.threadChecks=false \
-    -Dvertx.disableContextTimings=true \
-    -Dvertx.disableTCCL=true \
-    -Dvertx.disableHttpHeadersValidation=true \
-    -Dvertx.eventLoopPoolSize=$((`grep --count ^processor /proc/cpuinfo`)) \
-    -Dlog4j2.contextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector \
-    -Dio.netty.buffer.checkBounds=false \
-    -Dio.netty.buffer.checkAccessible=false \
-    -Dtfb.hasDB=false \
-    -jar \
-    build/libs/vertx-web-kotlin-dsljson-benchmark-1.0.0-SNAPSHOT-fat.jar
+  -server \
+  --enable-native-access=ALL-UNNAMED \
+  --add-opens=java.base/java.lang=ALL-UNNAMED \
+  --sun-misc-unsafe-memory-access=allow \
+  -Xms2G \
+  -Xmx2G \
+  -XX:+AlwaysPreTouch \
+  -XX:+UseParallelGC \
+  -XX:InitialCodeCacheSize=512m \
+  -XX:ReservedCodeCacheSize=512m \
+  -XX:MaxInlineLevel=20 \
+  -XX:+UseNUMA \
+  -XX:-UseCodeCacheFlushing \
+  -XX:AutoBoxCacheMax=10001 \
+  -XX:+UseCompactObjectHeaders \
+  -Djava.net.preferIPv4Stack=true \
+  -Dvertx.disableMetrics=true \
+  -Dvertx.disableWebsockets=true \
+  -Dvertx.disableContextTimings=true \
+  -Dvertx.cacheImmutableHttpResponseHeaders=true \
+  -Dvertx.internCommonHttpRequestHeadersToLowerCase=true \
+  -Dvertx.disableHttpHeadersValidation=true \
+  -Dio.netty.noUnsafe=false \
+  -Dio.netty.buffer.checkBounds=false \
+  -Dio.netty.buffer.checkAccessible=false \
+  -Dio.netty.leakDetection.level=disabled \
+  -Dio.netty.iouring.ringSize=4096 \
+  -Dio.netty.iouring.cqSize=8192 \
+  -Dtfb.type=basic \
+  -jar /app/vertx-web-kotlin-dsljson.jar