Просмотр исходного кода

Add tapir & kyo frameworks (#9816)

* Add tapir framework and its variants

* upgrade zio-http to 3.2.0

* fix: update zio-http to adhere to the json test requirement

That is "For each request, an object mapping the key message to Hello, World! must be instantiated."

* upgrade zio-http to use Java 21

* Add kyo-scheduler variants of zio-http and http4s

* Upgrade kyo to 0.18.0

* Move kyo-tapir into the tapir folder
Alpha Ho 4 месяцев назад
Родитель
Сommit
1e8b3c4df2
46 измененных файлов с 1236 добавлено и 101 удалено
  1. 24 0
      frameworks/Scala/kyo-scheduler/README.md
  2. 49 0
      frameworks/Scala/kyo-scheduler/benchmark_config.json
  3. 54 0
      frameworks/Scala/kyo-scheduler/build.sbt
  4. 32 0
      frameworks/Scala/kyo-scheduler/config.toml
  5. 4 0
      frameworks/Scala/kyo-scheduler/http4s/src/main/resources/application.properties
  6. 14 0
      frameworks/Scala/kyo-scheduler/http4s/src/main/resources/logback.xml
  7. 71 0
      frameworks/Scala/kyo-scheduler/http4s/src/main/scala/http4s/techempower/benchmark/DatabaseService.scala
  8. 118 0
      frameworks/Scala/kyo-scheduler/http4s/src/main/scala/http4s/techempower/benchmark/WebServer.scala
  9. 14 0
      frameworks/Scala/kyo-scheduler/http4s/src/main/twirl/index.scala.html
  10. 24 0
      frameworks/Scala/kyo-scheduler/kyo-scheduler-http4s.dockerfile
  11. 10 0
      frameworks/Scala/kyo-scheduler/kyo-scheduler.dockerfile
  12. 0 0
      frameworks/Scala/kyo-scheduler/project/build.properties
  13. 2 0
      frameworks/Scala/kyo-scheduler/project/plugins.sbt
  14. 2 4
      frameworks/Scala/kyo-scheduler/zio-http/src/main/scala/Payload.scala
  15. 45 0
      frameworks/Scala/kyo-scheduler/zio-http/src/main/scala/ZioHttp.scala
  16. 0 21
      frameworks/Scala/kyo-tapir/README.md
  17. 0 26
      frameworks/Scala/kyo-tapir/benchmark_config.json
  18. 0 17
      frameworks/Scala/kyo-tapir/build.sbt
  19. 0 15
      frameworks/Scala/kyo-tapir/config.toml
  20. 0 10
      frameworks/Scala/kyo-tapir/kyo-tapir.dockerfile
  21. 28 0
      frameworks/Scala/tapir/README.md
  22. 140 0
      frameworks/Scala/tapir/benchmark_config.json
  23. 100 0
      frameworks/Scala/tapir/build.sbt
  24. 1 0
      frameworks/Scala/tapir/common/src/main/scala/Payload.scala
  25. 93 0
      frameworks/Scala/tapir/config.toml
  26. 55 0
      frameworks/Scala/tapir/http4s-server-zio/src/main/scala/TapirHttp4sServerZioBenchmark.scala
  27. 50 0
      frameworks/Scala/tapir/http4s-server/src/main/scala/TapirHttp4sServerBenchmark.scala
  28. 54 0
      frameworks/Scala/tapir/netty-cats-server/src/main/scala/TapirNettyCatsServerBenchmark.scala
  29. 6 2
      frameworks/Scala/tapir/netty-kyo-server/src/main/scala/TapirNettyKyoServerBenchmark.scala
  30. 57 0
      frameworks/Scala/tapir/netty-zio-server/src/main/scala/TapirNettyZioServerBenchmark.scala
  31. 42 0
      frameworks/Scala/tapir/pekko-http-server/src/main/scala/Main.scala
  32. 1 0
      frameworks/Scala/tapir/project/build.properties
  33. 0 0
      frameworks/Scala/tapir/project/plugins.sbt
  34. 11 0
      frameworks/Scala/tapir/tapir-http4s-zio.dockerfile
  35. 11 0
      frameworks/Scala/tapir/tapir-http4s.dockerfile
  36. 11 0
      frameworks/Scala/tapir/tapir-netty-cats.dockerfile
  37. 11 0
      frameworks/Scala/tapir/tapir-netty-kyo.dockerfile
  38. 11 0
      frameworks/Scala/tapir/tapir-netty-zio.dockerfile
  39. 11 0
      frameworks/Scala/tapir/tapir-pekko-http.dockerfile
  40. 11 0
      frameworks/Scala/tapir/tapir.dockerfile
  41. 56 0
      frameworks/Scala/tapir/zio-http-server/src/main/scala/TapirZioHttpServerBenchmark.scala
  42. 1 1
      frameworks/Scala/zio-http/README.md
  43. 2 2
      frameworks/Scala/zio-http/build.sbt
  44. 3 2
      frameworks/Scala/zio-http/src/main/scala/Main.scala
  45. 6 0
      frameworks/Scala/zio-http/src/main/scala/Payload.scala
  46. 1 1
      frameworks/Scala/zio-http/zio-http.dockerfile

+ 24 - 0
frameworks/Scala/kyo-scheduler/README.md

@@ -0,0 +1,24 @@
+# kyo-scheduler Benchmarking Test
+
+This is a simple test to benchmark the performance of the kyo-scheduler libraries along with different backends in Scala.
+
+### Test Type Implementation Source Code
+
+* JSON
+* PLAINTEXT
+
+## Software Versions
+
+* [Java OpenJDK 21](https://adoptium.net/temurin/releases/)
+* [Kyo 0.17.0](https://github.com/getkyo/kyo)
+* [Scala 3.6.4 and Scala 2.13.16](https://www.scala-lang.org/) 
+
+### Server Implementations
+
+* [ZIO Http](https://zio.dev/zio-http/)
+* [http4s](https://http4s.org/)
+
+## Test URLs
+
+* JSON - http://localhost:8080/json
+* PLAINTEXT - http://localhost:8080/plaintext

+ 49 - 0
frameworks/Scala/kyo-scheduler/benchmark_config.json

@@ -0,0 +1,49 @@
+{
+  "framework": "kyo-scheduler",
+  "tests": [
+    {
+      "default": {
+        "plaintext_url": "/plaintext",
+        "json_url": "/json",
+        "port": 8080,
+        "database": "None",
+        "approach": "Realistic",
+        "classification": "Micro",
+        "framework": "zio-http",
+        "language": "Scala",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "Netty",
+        "webserver": "None",
+        "database_os": "Linux",
+        "os": "Linux",
+        "display_name": "zio-http with kyo-scheduler",
+        "notes": "https://zio.dev/zio-http/",
+        "versus": "None"
+      },
+      "http4s": {
+        "orm": "Raw",
+        "database_os": "Linux",
+        "json_url": "/json",
+        "plaintext_url": "/plaintext",
+        "query_url": "/queries?queries=",
+        "update_url": "/updates?queries=",
+        "fortune_url": "/fortunes",
+        "port": 8080,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "Postgres",
+        "db_url": "/db",
+        "framework": "http4s",
+        "language": "Scala",
+        "platform": "NIO2",
+        "webserver": "blaze",
+        "os": "Linux",
+        "display_name": "http4s with kyo-scheduler",
+        "notes": "https://http4s.org/",
+        "flavor": "None",
+        "versus": "None"
+      }
+    }
+  ]
+}

+ 54 - 0
frameworks/Scala/kyo-scheduler/build.sbt

@@ -0,0 +1,54 @@
+name := "kyo-scheduler-benchmark"
+
+ThisBuild / version := "1.0.0"
+
+val kyoVersion = "0.18.0"
+
+val commonAssemblySettings = assembly / assemblyMergeStrategy := {
+  case x if x.contains("io.netty.versions.properties") => MergeStrategy.discard
+  case x if x.contains("module-info.class") => MergeStrategy.discard
+  case x =>
+    val oldStrategy = (assembly / assemblyMergeStrategy).value
+    oldStrategy(x)
+}
+
+// based on the framework/Scala/zio-http implementation
+lazy val `zio-http` = (project in file("zio-http"))
+  .settings(
+    scalaVersion := "3.6.4",
+    name := "zio-http-kyo-scheduler-benchmark",
+    libraryDependencies ++= Seq(
+      "dev.zio" %% "zio-http" % "3.2.0",
+      "io.getkyo" %% "kyo-scheduler-zio" % kyoVersion,
+    ),
+    commonAssemblySettings
+  )
+
+val http4sVersion = "0.23.22"
+val http4sBlazeVersion = "0.23.15"
+val http4sTwirlVersion = "0.23.17"
+
+// based on the framework/Scala/http4s implementation
+lazy val http4s = (project in file("http4s"))
+  .settings(
+    scalaVersion := "2.13.16",
+    name := "http4s-kyo-scheduler-benchmark",
+    libraryDependencies ++= Seq(
+      "org.http4s" %% "http4s-blaze-server" % http4sBlazeVersion,
+      "org.http4s" %% "http4s-dsl" % http4sVersion,
+      "org.http4s" %% "http4s-twirl" % http4sTwirlVersion,
+      "org.http4s" %% "http4s-circe" % http4sVersion,
+      // Optional for auto-derivation of JSON codecs
+      "io.circe" %% "circe-generic" % "0.14.5",
+      "org.typelevel" %% "cats-effect" % "3.5.1",
+      "co.fs2" %% "fs2-core" % "3.7.0",
+      "co.fs2" %% "fs2-io" % "3.7.0",
+      "io.getquill" %% "quill-jasync-postgres" % "3.19.0",
+      "io.getquill" %% "quill-jasync" % "3.19.0",
+      "ch.qos.logback" % "logback-classic" % "1.4.8",
+      "io.getkyo" %% "kyo-scheduler-cats" % kyoVersion,
+    ),
+    commonAssemblySettings,
+    addCompilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.1")
+  )
+  .enablePlugins(SbtTwirl)

+ 32 - 0
frameworks/Scala/kyo-scheduler/config.toml

@@ -0,0 +1,32 @@
+[framework]
+name = "kyo-scheduler"
+
+[main]
+urls.plaintext = "/plaintext"
+urls.json = "/json"
+approach = "Realistic"
+classification = "Micro"
+database = "None"
+database_os = "Linux"
+os = "Linux"
+orm = "Raw"
+platform = "Netty"
+webserver = "None"
+versus = "None"
+
+[http4s]
+urls.plaintext = "/plaintext"
+urls.json = "/json"
+urls.db = "/db"
+urls.query = "/queries?queries="
+urls.update = "/updates?queries="
+urls.fortune = "/fortunes"
+approach = "Realistic"
+classification = "Micro"
+database = "Postgres"
+database_os = "Linux"
+os = "Linux"
+orm = "Raw"
+platform = "NIO2"
+webserver = "blaze"
+versus = "None"

+ 4 - 0
frameworks/Scala/kyo-scheduler/http4s/src/main/resources/application.properties

@@ -0,0 +1,4 @@
+ctx.port=5432
+ctx.username=benchmarkdbuser
+ctx.password=benchmarkdbpass
+ctx.database=hello_world

+ 14 - 0
frameworks/Scala/kyo-scheduler/http4s/src/main/resources/logback.xml

@@ -0,0 +1,14 @@
+<configuration>
+
+  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+    <!-- encoders are assigned the type
+         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
+    <encoder>
+      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
+    </encoder>
+  </appender>
+
+  <root level="error">
+    <appender-ref ref="STDOUT" />
+  </root>
+</configuration>

+ 71 - 0
frameworks/Scala/kyo-scheduler/http4s/src/main/scala/http4s/techempower/benchmark/DatabaseService.scala

@@ -0,0 +1,71 @@
+package http4s.techempower.benchmark
+
+import java.util.concurrent.{Executor, ThreadLocalRandom}
+
+import scala.concurrent.{ExecutionContext, ExecutionContextExecutor}
+import cats.effect.{IO => CatsIO}
+import cats.syntax.all._
+import io.getquill._
+
+class DatabaseService(ctx: PostgresJAsyncContext[LowerCase.type], executor: Executor) {
+  implicit val dbExecutionContext: ExecutionContextExecutor = ExecutionContext.fromExecutor(executor)
+  import ctx._
+
+  def close(): CatsIO[Unit] = {
+    CatsIO(ctx.close())
+  }
+
+  // Provide a random number between 1 and 10000 (inclusive)
+  private def randomWorldId() =
+    CatsIO(ThreadLocalRandom.current().nextInt(1, 10001))
+
+  // Update the randomNumber field with a random number
+  def updateRandomNumber(world: World): CatsIO[World] =
+    for {
+      randomId <- randomWorldId()
+    } yield world.copy(randomNumber = randomId)
+
+  // Select a World object from the database by ID
+  def selectWorld(id: Int): CatsIO[World] =
+    CatsIO.fromFuture(
+      CatsIO.delay(
+        ctx
+          .run(quote {
+            query[World].filter(_.id == lift(id))
+          })
+          .map(rq => rq.head)
+      )
+    )
+
+  // Select a random World object from the database
+  def selectRandomWorld(): CatsIO[World] =
+    for {
+      randomId <- randomWorldId()
+      world <- selectWorld(randomId)
+    } yield world
+
+  // Select a specified number of random World objects from the database
+  def getWorlds(numQueries: Int): CatsIO[List[World]] =
+    (0 until numQueries).toList.traverse(_ => selectRandomWorld())
+
+  // Update the randomNumber field with a new random number, for a list of World objects
+  def getNewWorlds(worlds: List[World]): CatsIO[List[World]] =
+    worlds.map(updateRandomNumber).sequence
+
+  // Update the randomNumber column in the database for a specified set of World objects,
+  // this uses a batch update SQL call.
+  def updateWorlds(newWorlds: List[World]): CatsIO[Int] = {
+    val u = quote {
+      liftQuery(newWorlds).foreach { world =>
+        query[World]
+          .filter(_.id == world.id)
+          .update(_.randomNumber -> world.randomNumber)
+      }
+    }
+    CatsIO.fromFuture(CatsIO.delay(ctx.run(u).map(_.length)))
+  }
+
+  // Retrieve all fortunes from the database
+  def getFortunes(): CatsIO[List[Fortune]] =
+    CatsIO.fromFuture(CatsIO.delay(ctx.run(query[Fortune]).map(_.toList)))
+}

+ 118 - 0
frameworks/Scala/kyo-scheduler/http4s/src/main/scala/http4s/techempower/benchmark/WebServer.scala

@@ -0,0 +1,118 @@
+package http4s.techempower.benchmark
+
+
+import java.util.concurrent.Executors
+import cats.effect.{ExitCode, IO, Resource}
+import com.typesafe.config.ConfigValueFactory
+import io.circe.generic.auto._
+import io.circe.syntax._
+import io.getquill.util.LoadConfig
+import io.getquill.LowerCase
+import io.getquill.PostgresJAsyncContext
+import org.http4s._
+import org.http4s.dsl._
+import org.http4s.circe._
+import org.http4s.implicits._
+import org.http4s.blaze.server.BlazeServerBuilder
+import org.http4s.headers.Server
+import org.http4s.twirl._
+
+final case class Message(message: String)
+final case class World(id: Int, randomNumber: Int)
+final case class Fortune(id: Int, message: String)
+
+// Extract queries parameter (with default and min/maxed)
+object Queries {
+  def unapply(params: Map[String, Seq[String]]): Option[Int] =
+    Some(params.getOrElse("queries", Nil).headOption match {
+      case None => 1
+      case Some(x) =>
+        Math.max(1, Math.min(500, scala.util.Try(x.toInt).getOrElse(1)))
+    })
+}
+
+// based on the framework/Scala/http4s implementation
+object WebServer extends kyo.KyoSchedulerIOApp with Http4sDsl[IO] {
+  def makeDatabaseService(
+      host: String,
+      poolSize: Int
+  ): Resource[IO, DatabaseService] = {
+    for {
+      executor <- Resource(IO {
+        val pool = Executors.newFixedThreadPool(poolSize)
+        (pool, IO(pool.shutdown()))
+      })
+      ctx <- Resource.fromAutoCloseable(IO(new PostgresJAsyncContext(
+        LowerCase,
+        LoadConfig("ctx")
+          .withValue("host", ConfigValueFactory.fromAnyRef(host))
+          .withValue(
+            "maxActiveConnections",
+            ConfigValueFactory.fromAnyRef(poolSize)
+          )
+      )))
+    } yield new DatabaseService(ctx, executor)
+  }
+
+  // Add a new fortune to an existing list, and sort by message.
+  def getSortedFortunes(old: List[Fortune]): List[Fortune] = {
+    val newFortune = Fortune(0, "Additional fortune added at request time.")
+    (newFortune :: old).sortBy(_.message)
+  }
+
+  // Add Server header container server address
+  def addServerHeader(service: HttpRoutes[IO]): HttpRoutes[IO] =
+    cats.data.Kleisli { req: Request[IO] =>
+      service.run(req).map(_.putHeaders(server))
+    }
+
+  val server = Server(ProductId("http4s", None))
+
+  // HTTP service definition
+  def service(db: DatabaseService) =
+    addServerHeader(HttpRoutes.of[IO] {
+      case GET -> Root / "plaintext" =>
+        Ok("Hello, World!")
+
+      case GET -> Root / "json" =>
+        Ok(Message("Hello, World!").asJson)
+
+      case GET -> Root / "db" =>
+        Ok(db.selectRandomWorld().map(_.asJson))
+
+      case GET -> Root / "queries" :? Queries(numQueries) =>
+        Ok(db.getWorlds(numQueries).map(_.asJson))
+
+      case GET -> Root / "fortunes" =>
+        Ok(for {
+          oldFortunes <- db.getFortunes()
+          newFortunes = getSortedFortunes(oldFortunes)
+        } yield html.index(newFortunes))
+
+      case GET -> Root / "updates" :? Queries(numQueries) =>
+        Ok(for {
+          worlds <- db.getWorlds(numQueries)
+          newWorlds <- db.getNewWorlds(worlds)
+          _ <- db.updateWorlds(newWorlds)
+        } yield newWorlds.asJson)
+    })
+
+  // Given a fully constructed HttpService, start the server and wait for completion
+  def startServer(service: HttpRoutes[IO]) =
+    BlazeServerBuilder[IO]
+      .bindHttp(8080, "0.0.0.0")
+      .withHttpApp(service.orNotFound)
+      .withSocketKeepAlive(true)
+      .resource
+
+  // Entry point when starting service
+  override def run(args: List[String]): IO[ExitCode] =
+    (for {
+      db <- makeDatabaseService(
+        args.headOption.getOrElse("localhost"),
+        sys.env.get("DB_POOL_SIZE").map(_.toInt).getOrElse(64)
+      )
+      server <- startServer(service(db))
+    } yield server)
+      .use(_ => IO.never)
+}

+ 14 - 0
frameworks/Scala/kyo-scheduler/http4s/src/main/twirl/index.scala.html

@@ -0,0 +1,14 @@
+@import http4s.techempower.benchmark.Fortune
+@(fortunes: Seq[Fortune])
+<!DOCTYPE html>
+<html>
+<head><title>Fortunes</title></head>
+<body>
+<table>
+    <tr><th>id</th><th>message</th></tr>
+    @for(fortune <- fortunes) {
+    <tr><td>@fortune.id</td><td>@fortune.message</td></tr>
+    }
+</table>
+</body>
+</html>

+ 24 - 0
frameworks/Scala/kyo-scheduler/kyo-scheduler-http4s.dockerfile

@@ -0,0 +1,24 @@
+FROM sbtscala/scala-sbt:eclipse-temurin-21.0.6_7_1.10.11_2.13.16
+
+WORKDIR /kyo-scheduler-benchmark
+COPY http4s http4s
+COPY project project
+COPY build.sbt build.sbt
+RUN sbt http4s/assembly
+
+EXPOSE 8080
+
+CMD java \
+      -server \
+      -Xms2g \
+      -Xmx2g \
+      -XX:NewSize=1g \
+      -XX:MaxNewSize=1g \
+      -XX:InitialCodeCacheSize=256m \
+      -XX:ReservedCodeCacheSize=256m \
+      -XX:+UseParallelGC \
+      -XX:+AlwaysPreTouch \
+      -Dcats.effect.stackTracingMode=disabled \
+      -jar \
+      /kyo-scheduler-benchmark/http4s/target/scala-2.13/http4s-kyo-scheduler-benchmark-assembly-1.0.0.jar \
+      tfb-database

+ 10 - 0
frameworks/Scala/kyo-scheduler/kyo-scheduler.dockerfile

@@ -0,0 +1,10 @@
+FROM sbtscala/scala-sbt:eclipse-temurin-21.0.6_7_1.10.11_3.6.4
+
+WORKDIR /kyo-scheduler-benchmark
+COPY zio-http zio-http
+COPY project project
+COPY build.sbt build.sbt
+RUN sbt zio-http/assembly
+
+EXPOSE 8080
+CMD ["java", "-Xms2G", "-Xmx2G", "-server", "-Dio.netty.leakDetection.level=disabled", "-Dio.netty.recycler.maxCapacityPerThread=0", "-jar", "/kyo-scheduler-benchmark/zio-http/target/scala-3.6.4/zio-http-kyo-scheduler-benchmark-assembly-1.0.0.jar"]

+ 0 - 0
frameworks/Scala/kyo-tapir/project/build.properties → frameworks/Scala/kyo-scheduler/project/build.properties


+ 2 - 0
frameworks/Scala/kyo-scheduler/project/plugins.sbt

@@ -0,0 +1,2 @@
+addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.3.1")
+addSbtPlugin("com.typesafe.play" % "sbt-twirl" % "1.6.1")

+ 2 - 4
frameworks/Scala/kyo-tapir/src/main/scala/Payload.scala → frameworks/Scala/kyo-scheduler/zio-http/src/main/scala/Payload.scala

@@ -1,8 +1,6 @@
-import sttp.tapir.Schema
-import zio.json.*
+import zio.json.{DeriveJsonCodec, JsonCodec}
 
 case class Payload(message: String)
 object Payload {
   given JsonCodec[Payload] = DeriveJsonCodec.gen
-  given Schema[Payload] = Schema.derived
-}
+}

+ 45 - 0
frameworks/Scala/kyo-scheduler/zio-http/src/main/scala/ZioHttp.scala

@@ -0,0 +1,45 @@
+import zio._
+import zio.http._
+import zio.http.netty.NettyConfig
+import zio.http.netty.NettyConfig.LeakDetectionLevel
+import zio.json.*
+
+import java.lang.{Runtime => JRuntime}
+
+// based on the framework/Scala/zio-http implementation
+object ZioHttp extends kyo.KyoSchedulerZIOAppDefault {
+
+  private val plainTextMessage: String = "hello, world!"
+
+  private val STATIC_SERVER_NAME = "zio-http"
+  private val NUM_PROCESSORS     = JRuntime.getRuntime.availableProcessors()
+
+  val app: Routes[Any, Response] = Routes(
+    Method.GET / "/plaintext" ->
+      Handler.fromResponse(
+        Response
+          .text(plainTextMessage)
+          .addHeader(Header.Server(STATIC_SERVER_NAME)),
+      ),
+    Method.GET / "/json"      ->
+      Handler.fromResponse(
+        Response
+          .json(Payload(plainTextMessage).toJson)
+          .addHeader(Header.Server(STATIC_SERVER_NAME)),
+      ),
+  )
+
+  private val config = Server.Config.default
+    .port(8080)
+    .enableRequestStreaming
+
+  private val nettyConfig = NettyConfig.default
+    .leakDetection(LeakDetectionLevel.DISABLED)
+    .maxThreads(NUM_PROCESSORS)
+
+  private val configLayer      = ZLayer.succeed(config)
+  private val nettyConfigLayer = ZLayer.succeed(nettyConfig)
+
+  val run: UIO[ExitCode] =
+    Server.serve(app).provide(configLayer, nettyConfigLayer, Server.customized).exitCode
+}

+ 0 - 21
frameworks/Scala/kyo-tapir/README.md

@@ -1,21 +0,0 @@
-# Kyo Tapir Benchmarking Test
-
-This is a simple test to benchmark the performance of the Kyo and Tapir libraries in Scala.
-
-### Test Type Implementation Source Code
-
-* [JSON](src/main/scala/Main.scala)
-* [PLAINTEXT](src/main/scala/Main.scala)
-
-## Software Versions
-
-* [Java OpenJDK 21](https://adoptium.net/temurin/releases/)
-* [Scala 3.6.3](https://www.scala-lang.org/)
-* [Kyo 0.16.2](https://github.com/getkyo/kyo)
-* [Tapir 1.11.15](https://tapir.softwaremill.com)
-* [ZIO Json 0.7.32](https://zio.dev/zio-json/)
-
-## Test URLs
-
-* JSON - http://localhost:9999/json
-* PLAINTEXT - http://localhost:9999/plaintext

+ 0 - 26
frameworks/Scala/kyo-tapir/benchmark_config.json

@@ -1,26 +0,0 @@
-{
-  "framework": "kyo-tapir",
-  "tests": [
-    {
-      "default": {
-        "plaintext_url": "/plaintext",
-        "json_url": "/json",
-        "port": 9999,
-        "database": "None",
-        "approach": "Realistic",
-        "classification": "Micro",
-        "framework": "kyo-tapir",
-        "language": "Scala",
-        "flavor": "None",
-        "orm": "Raw",
-        "platform": "Netty",
-        "webserver": "None",
-        "database_os": "Linux",
-        "os": "Linux",
-        "display_name": "kyo-tapir",
-        "notes": "",
-        "versus": "None"
-      }
-    }
-  ]
-}

+ 0 - 17
frameworks/Scala/kyo-tapir/build.sbt

@@ -1,17 +0,0 @@
-name := "kyo-tapir"
-version := "1.0.0"
-scalaVersion := "3.6.3"
-lazy val root = (project in file("."))
-  .settings(
-    libraryDependencies ++= Seq(
-      "io.getkyo" %% "kyo-tapir" % "0.16.2",
-      "com.softwaremill.sttp.tapir" %% "tapir-json-zio" % "1.11.15",
-      "dev.zio" %% "zio-json" % "0.7.32"
-    ),
-    assembly / assemblyMergeStrategy  := {
-      case x if x.contains("io.netty.versions.properties") => MergeStrategy.discard
-      case x =>
-        val oldStrategy = (assembly / assemblyMergeStrategy).value
-        oldStrategy(x)
-    }
-  )

+ 0 - 15
frameworks/Scala/kyo-tapir/config.toml

@@ -1,15 +0,0 @@
-[framework]
-name = "kyo-tapir"
-
-[main]
-urls.plaintext = "/plaintext"
-urls.json = "/json"
-approach = "Realistic"
-classification = "Micro"
-database = "None"
-database_os = "Linux"
-os = "Linux"
-orm = "Raw"
-platform = "Netty"
-webserver = "None"
-versus = "None"

+ 0 - 10
frameworks/Scala/kyo-tapir/kyo-tapir.dockerfile

@@ -1,10 +0,0 @@
-FROM sbtscala/scala-sbt:eclipse-temurin-21.0.6_7_1.10.7_3.6.3
-
-WORKDIR /kyo-tapir
-COPY src src
-COPY project project
-COPY build.sbt build.sbt
-RUN sbt assembly
-
-EXPOSE 9999
-CMD ["java", "-Xms2G", "-Xmx2G", "-server", "-Dio.netty.leakDetection.level=disabled", "-Dio.netty.recycler.maxCapacityPerThread=0", "-jar", "/kyo-tapir/target/scala-3.6.3/kyo-tapir-assembly-1.0.0.jar"]

+ 28 - 0
frameworks/Scala/tapir/README.md

@@ -0,0 +1,28 @@
+# Tapir Benchmarking Test
+
+This is a simple test to benchmark the performance of the Tapir libraries along with different backends in Scala.
+
+### Test Type Implementation Source Code
+
+* JSON
+* PLAINTEXT
+
+## Software Versions
+
+* [Java OpenJDK 21](https://adoptium.net/temurin/releases/)
+* [Scala 3.6.4](https://www.scala-lang.org/)
+* [Tapir 1.11.24](https://tapir.softwaremill.com)
+
+### Backend Implementations
+* [Tapir running as zio-http server](https://tapir.softwaremill.com/en/latest/server/ziohttp.html)
+* [Tapir running as http4s server](https://tapir.softwaremill.com/en/latest/server/http4s.html)
+* [as an http4s server using ZIO](https://tapir.softwaremill.com/en/latest/server/zio-http4s.html)
+* [Tapir running as Netty-based server(Cats)](https://tapir.softwaremill.com/en/latest/server/netty.html)
+* [Tapir running as Netty-based server(Kyo)](https://getkyo.io/#/?id=routes-http-server-via-tapir)
+* [Tapir running as Netty-based server(ZIO)](https://tapir.softwaremill.com/en/latest/server/netty.html)
+* [Tapir running as pekko-http server](https://tapir.softwaremill.com/en/latest/server/pekkohttp.html)
+
+## Test URLs
+
+* JSON - http://localhost:8080/json
+* PLAINTEXT - http://localhost:8080/plaintext

+ 140 - 0
frameworks/Scala/tapir/benchmark_config.json

@@ -0,0 +1,140 @@
+{
+  "framework": "tapir",
+  "tests": [
+    {
+      "default": {
+        "plaintext_url": "/plaintext",
+        "json_url": "/json",
+        "port": 8080,
+        "database": "None",
+        "approach": "Realistic",
+        "classification": "Micro",
+        "framework": "tapir",
+        "language": "Scala",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "Netty",
+        "webserver": "None",
+        "database_os": "Linux",
+        "os": "Linux",
+        "display_name": "tapir as zio-http server",
+        "notes": "https://tapir.softwaremill.com/en/latest/server/ziohttp.html",
+        "versus": "None"
+      },
+      "http4s": {
+        "plaintext_url": "/plaintext",
+        "json_url": "/json",
+        "port": 8080,
+        "database": "None",
+        "approach": "Realistic",
+        "classification": "Micro",
+        "framework": "tapir",
+        "language": "Scala",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "Netty",
+        "webserver": "None",
+        "database_os": "Linux",
+        "os": "Linux",
+        "display_name": "tapir as http4s server",
+        "notes": "https://tapir.softwaremill.com/en/latest/server/http4s.html",
+        "versus": "None"
+      },
+      "http4s-zio": {
+        "plaintext_url": "/plaintext",
+        "json_url": "/json",
+        "port": 8080,
+        "database": "None",
+        "approach": "Realistic",
+        "classification": "Micro",
+        "framework": "tapir",
+        "language": "Scala",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "Netty",
+        "webserver": "None",
+        "database_os": "Linux",
+        "os": "Linux",
+        "display_name": "tapir as http4s server using ZIO",
+        "notes": "https://tapir.softwaremill.com/en/latest/server/zio-http4s.html",
+        "versus": "None"
+      },
+      "netty-cats": {
+        "plaintext_url": "/plaintext",
+        "json_url": "/json",
+        "port": 8080,
+        "database": "None",
+        "approach": "Realistic",
+        "classification": "Micro",
+        "framework": "tapir",
+        "language": "Scala",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "Netty",
+        "webserver": "None",
+        "database_os": "Linux",
+        "os": "Linux",
+        "display_name": "tapir as netty(cats) server",
+        "notes": "https://tapir.softwaremill.com/en/latest/server/netty.html",
+        "versus": "None"
+      },
+      "netty-kyo": {
+        "plaintext_url": "/plaintext",
+        "json_url": "/json",
+        "port": 8080,
+        "database": "None",
+        "approach": "Realistic",
+        "classification": "Micro",
+        "framework": "tapir",
+        "language": "Scala",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "Netty",
+        "webserver": "None",
+        "database_os": "Linux",
+        "os": "Linux",
+        "display_name": "tapir as netty(kyo) server",
+        "notes": "https://getkyo.io/#/?id=routes-http-server-via-tapir",
+        "versus": "None"
+      },
+      "netty-zio": {
+        "plaintext_url": "/plaintext",
+        "json_url": "/json",
+        "port": 8080,
+        "database": "None",
+        "approach": "Realistic",
+        "classification": "Micro",
+        "framework": "tapir",
+        "language": "Scala",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "Netty",
+        "webserver": "None",
+        "database_os": "Linux",
+        "os": "Linux",
+        "display_name": "tapir as netty(zio) server",
+        "notes": "https://tapir.softwaremill.com/en/latest/server/netty.html",
+        "versus": "None"
+      },
+      "pekko-http": {
+        "plaintext_url": "/plaintext",
+        "json_url": "/json",
+        "port": 8080,
+        "database": "None",
+        "approach": "Realistic",
+        "classification": "Micro",
+        "framework": "tapir",
+        "language": "Scala",
+        "flavor": "None",
+        "orm": "Raw",
+        "platform": "Netty",
+        "webserver": "None",
+        "database_os": "Linux",
+        "os": "Linux",
+        "display_name": "tapir as pekko-http server",
+        "notes": "https://tapir.softwaremill.com/en/latest/server/pekkohttp.html",
+        "versus": "None"
+      }
+    }
+  ]
+}

+ 100 - 0
frameworks/Scala/tapir/build.sbt

@@ -0,0 +1,100 @@
+name := "tapir-benchmark"
+
+ThisBuild / version := "1.0.0"
+ThisBuild / scalaVersion := "3.6.4"
+
+val tapirVersion = "1.11.24"
+
+val commonAssemblySettings = assembly / assemblyMergeStrategy := {
+  case x if x.contains("io.netty.versions.properties") => MergeStrategy.discard
+  case x if x.contains("module-info.class") => MergeStrategy.discard
+  case x =>
+    val oldStrategy = (assembly / assemblyMergeStrategy).value
+    oldStrategy(x)
+}
+
+lazy val common = (project in file("common"))
+  .settings(
+    name := "tapir-benchmark-common"
+  )
+
+lazy val `zio-http-server` = (project in file("zio-http-server"))
+  .dependsOn(common)
+  .settings(
+    name := "tapir-zio-http-server",
+    libraryDependencies ++= Seq(
+      "com.softwaremill.sttp.tapir" %% "tapir-zio-http-server" % tapirVersion,
+      "com.softwaremill.sttp.tapir" %% "tapir-json-zio" % tapirVersion
+    ),
+    commonAssemblySettings
+  )
+
+lazy val `http4s-server` = (project in file("http4s-server"))
+  .dependsOn(common)
+  .settings(
+    name := "tapir-http4s-server",
+    libraryDependencies ++= Seq(
+      "com.softwaremill.sttp.tapir" %% "tapir-http4s-server" % tapirVersion,
+      "com.softwaremill.sttp.tapir" %% "tapir-json-circe" % tapirVersion,
+      "org.http4s" %% "http4s-blaze-server" % "0.23.17",
+    ),
+    commonAssemblySettings
+  )
+
+lazy val `http4s-server-zio` = (project in file("http4s-server-zio"))
+  .dependsOn(common)
+  .settings(
+    name := "tapir-http4s-server-zio",
+    libraryDependencies ++= Seq(
+      "com.softwaremill.sttp.tapir" %% "tapir-http4s-server-zio" % tapirVersion,
+      "com.softwaremill.sttp.tapir" %% "tapir-json-circe" % tapirVersion,
+      "org.http4s" %% "http4s-blaze-server" % "0.23.17",
+      "dev.zio" %% "zio-interop-cats" % "23.1.0.5"
+    ),
+    commonAssemblySettings
+  )
+
+lazy val `netty-kyo-server` = (project in file("netty-kyo-server"))
+  .dependsOn(common)
+  .settings(
+    name := "tapir-netty-kyo-server",
+    libraryDependencies ++= Seq(
+      "io.getkyo" %% "kyo-tapir" % "0.18.0",
+      "com.softwaremill.sttp.tapir" %% "tapir-json-zio" % tapirVersion
+    ),
+    commonAssemblySettings
+  )
+
+lazy val `netty-zio-server` = (project in file("netty-zio-server"))
+  .dependsOn(common)
+  .settings(
+    name := "tapir-netty-zio-server",
+    libraryDependencies ++= Seq(
+      "com.softwaremill.sttp.tapir" %% "tapir-netty-server-zio" % tapirVersion,
+      "com.softwaremill.sttp.tapir" %% "tapir-json-zio" % tapirVersion,
+    ),
+    commonAssemblySettings
+  )
+
+lazy val `netty-cats-server` = (project in file("netty-cats-server"))
+  .dependsOn(common)
+  .settings(
+    name := "tapir-netty-cats-server",
+    libraryDependencies ++= Seq(
+      "com.softwaremill.sttp.tapir" %% "tapir-netty-server-cats" % tapirVersion,
+      "com.softwaremill.sttp.tapir" %% "tapir-json-circe" % tapirVersion,
+      "org.http4s" %% "http4s-blaze-server" % "0.23.17",
+    ),
+    commonAssemblySettings
+  )
+
+lazy val `pekko-http-server` = (project in file("pekko-http-server"))
+  .dependsOn(common)
+  .settings(
+    name := "tapir-pekko-http-server",
+    libraryDependencies ++= Seq(
+      "com.softwaremill.sttp.tapir" %% "tapir-pekko-http-server" % tapirVersion,
+      "com.softwaremill.sttp.tapir" %% "tapir-json-circe" % tapirVersion,
+    ),
+    commonAssemblySettings
+  )

+ 1 - 0
frameworks/Scala/tapir/common/src/main/scala/Payload.scala

@@ -0,0 +1 @@
+case class Payload(message: String)

+ 93 - 0
frameworks/Scala/tapir/config.toml

@@ -0,0 +1,93 @@
+[framework]
+name = "tapir"
+
+[main]
+urls.plaintext = "/plaintext"
+urls.json = "/json"
+approach = "Realistic"
+classification = "Micro"
+database = "None"
+database_os = "Linux"
+os = "Linux"
+orm = "Raw"
+platform = "Netty"
+webserver = "None"
+versus = "None"
+
+[http4s]
+urls.plaintext = "/plaintext"
+urls.json = "/json"
+approach = "Realistic"
+classification = "Micro"
+database = "None"
+database_os = "Linux"
+os = "Linux"
+orm = "Raw"
+platform = "Netty"
+webserver = "None"
+versus = "None"
+
+[http4s-zio]
+urls.plaintext = "/plaintext"
+urls.json = "/json"
+approach = "Realistic"
+classification = "Micro"
+database = "None"
+database_os = "Linux"
+os = "Linux"
+orm = "Raw"
+platform = "Netty"
+webserver = "None"
+versus = "None"
+
+[netty-cats]
+urls.plaintext = "/plaintext"
+urls.json = "/json"
+approach = "Realistic"
+classification = "Micro"
+database = "None"
+database_os = "Linux"
+os = "Linux"
+orm = "Raw"
+platform = "Netty"
+webserver = "None"
+versus = "None"
+
+[netty-kyo]
+urls.plaintext = "/plaintext"
+urls.json = "/json"
+approach = "Realistic"
+classification = "Micro"
+database = "None"
+database_os = "Linux"
+os = "Linux"
+orm = "Raw"
+platform = "Netty"
+webserver = "None"
+versus = "None"
+
+[netty-zio]
+urls.plaintext = "/plaintext"
+urls.json = "/json"
+approach = "Realistic"
+classification = "Micro"
+database = "None"
+database_os = "Linux"
+os = "Linux"
+orm = "Raw"
+platform = "Netty"
+webserver = "None"
+versus = "None"
+
+[pekko-http]
+urls.plaintext = "/plaintext"
+urls.json = "/json"
+approach = "Realistic"
+classification = "Micro"
+database = "None"
+database_os = "Linux"
+os = "Linux"
+orm = "Raw"
+platform = "Netty"
+webserver = "None"
+versus = "None"

+ 55 - 0
frameworks/Scala/tapir/http4s-server-zio/src/main/scala/TapirHttp4sServerZioBenchmark.scala

@@ -0,0 +1,55 @@
+import io.circe.generic.auto.*
+import org.http4s.HttpRoutes
+import org.http4s.blaze.server.BlazeServerBuilder
+import org.http4s.server.Router
+import sttp.model.{Header, HeaderNames, StatusCode}
+import sttp.tapir.generic.auto.*
+import sttp.tapir.json.circe.*
+import sttp.tapir.server.http4s.ztapir.ZHttp4sServerInterpreter
+import sttp.tapir.ztapir.*
+import zio.*
+
+import java.time.format.DateTimeFormatter
+import java.time.{Clock, LocalDateTime, ZoneOffset, ZonedDateTime}
+import java.util.Date
+import java.util.concurrent.locks.LockSupport
+import scala.concurrent.ExecutionContext
+import zio.interop.catz.*
+
+object TapirHttp4sServerZioBenchmark extends ZIOAppDefault:
+  private val STATIC_SERVER_NAME = "tapir-http4s-server-zio"
+
+  private val plainTextMessage: String = "Hello, World!"
+
+  private val plaintextEndpoint: ZServerEndpoint[Any, Any] =
+    endpoint.get.in("plaintext")
+      .out(header(HeaderNames.Server, STATIC_SERVER_NAME))
+      .out(stringBody)
+      .zServerLogic(_ => ZIO.succeed(plainTextMessage))
+
+  private val jsonEndpoint: ZServerEndpoint[Any, Any] =
+    endpoint.get.in("json")
+      .out(header(HeaderNames.Server, STATIC_SERVER_NAME))
+      .out(jsonBody[Payload])
+      .zServerLogic(_ => ZIO.succeed(Payload(plainTextMessage)))
+
+
+  private val declaredPort = 8080
+  private val declaredHost = "0.0.0.0"
+
+  private val routes: HttpRoutes[Task] = ZHttp4sServerInterpreter()
+    .from(List(plaintextEndpoint, jsonEndpoint))
+    .toRoutes
+
+  implicit val ec: ExecutionContext = scala.concurrent.ExecutionContext.Implicits.global
+
+  override def run: URIO[Any, ExitCode] =
+    ZIO.executor.flatMap(executor =>
+      BlazeServerBuilder[Task]
+        .withExecutionContext(executor.asExecutionContext)
+        .bindHttp(8080, declaredHost)
+        .withHttpApp(Router("/" -> routes).orNotFound)
+        .serve
+        .compile
+        .drain
+    ).exitCode

+ 50 - 0
frameworks/Scala/tapir/http4s-server/src/main/scala/TapirHttp4sServerBenchmark.scala

@@ -0,0 +1,50 @@
+import cats.effect.{ExitCode, IO, IOApp}
+import io.circe.generic.auto.*
+import org.http4s.HttpRoutes
+import org.http4s.blaze.server.BlazeServerBuilder
+import org.http4s.server.Router
+import sttp.model.{Header, HeaderNames, StatusCode}
+import sttp.tapir.*
+import sttp.tapir.generic.auto.*
+import sttp.tapir.json.circe.*
+import sttp.tapir.server.http4s.Http4sServerInterpreter
+
+import java.time.format.DateTimeFormatter
+import java.time.{Clock, LocalDateTime, ZoneOffset, ZonedDateTime}
+import java.util.Date
+import java.util.concurrent.locks.LockSupport
+import scala.concurrent.ExecutionContext
+
+object TapirHttp4sServerBenchmark extends IOApp:
+  private val STATIC_SERVER_NAME = "tapir-http4s-server"
+
+  private val plainTextMessage: String = "Hello, World!"
+
+  val plaintextEndpoint =
+    endpoint.get.in("plaintext")
+      .out(header(HeaderNames.Server, STATIC_SERVER_NAME))
+      .out(stringBody)
+      .serverLogic(_ => IO.pure[Either[Unit, String]](Right(plainTextMessage)))
+
+  val jsonEndpoint =
+    endpoint.get.in("json")
+      .out(header(HeaderNames.Server, STATIC_SERVER_NAME))
+      .out(jsonBody[Payload])
+      .serverLogic(_ => IO.pure[Either[Unit, Payload]](Right(Payload(plainTextMessage))))
+
+
+  private val declaredPort = 8080
+  private val declaredHost = "0.0.0.0"
+
+  val routes = Http4sServerInterpreter[IO]().toRoutes(List(plaintextEndpoint, jsonEndpoint))
+
+  implicit val ec: ExecutionContext = scala.concurrent.ExecutionContext.Implicits.global
+
+  override def run(args: List[String]): IO[ExitCode] =
+    BlazeServerBuilder[IO]
+      .withExecutionContext(ec)
+      .bindHttp(declaredPort, declaredHost)
+      .withHttpApp(Router("/" -> routes).orNotFound)
+      .resource
+      .useForever
+      .as(ExitCode.Success)

+ 54 - 0
frameworks/Scala/tapir/netty-cats-server/src/main/scala/TapirNettyCatsServerBenchmark.scala

@@ -0,0 +1,54 @@
+import cats.effect.{IO, IOApp}
+import io.circe.generic.auto.*
+import sttp.model.{Header, HeaderNames}
+import sttp.tapir.*
+import sttp.tapir.generic.auto.*
+import sttp.tapir.json.circe.*
+import sttp.tapir.server.netty.cats.NettyCatsServer
+
+import java.time.Instant
+
+object TapirNettyCatsServerBenchmark extends IOApp.Simple:
+  private val STATIC_SERVER_NAME = "tapir-netty-cats-server"
+
+  private val plainTextMessage: String = "Hello, World!"
+
+  val plaintextEndpoint =
+    endpoint.get.in("plaintext")
+      .out(header(HeaderNames.Server, STATIC_SERVER_NAME))
+      .out(header[String](HeaderNames.Date))
+      .out(stringBody)
+      .serverLogic(_ =>
+        for {
+          now <- IO.realTime.map(time => Instant.ofEpochMilli(time.toMillis))
+        } yield Right(Header.toHttpDateString(now) -> plainTextMessage)
+      )
+
+  val jsonEndpoint =
+    endpoint.get.in("json")
+      .out(header(HeaderNames.Server, STATIC_SERVER_NAME))
+      .out(header[String](HeaderNames.Date))
+      .out(jsonBody[Payload])
+      .serverLogic(_ =>
+        for {
+          now <- IO.realTime.map(time => Instant.ofEpochMilli(time.toMillis))
+        } yield Right(Header.toHttpDateString(now) -> Payload(plainTextMessage))
+      )
+
+
+  private val declaredPort = 8080
+  private val declaredHost = "0.0.0.0"
+
+  override def run = NettyCatsServer
+    .io()
+    .use { server =>
+      for {
+        _ <- server
+          .port(declaredPort)
+          .host(declaredHost)
+          .addEndpoint(plaintextEndpoint)
+          .addEndpoint(jsonEndpoint)
+          .start()
+        _ <- IO.never
+      } yield ()
+    }

+ 6 - 2
frameworks/Scala/kyo-tapir/src/main/scala/Main.scala → frameworks/Scala/tapir/netty-kyo-server/src/main/scala/TapirNettyKyoServerBenchmark.scala

@@ -3,12 +3,16 @@ import sttp.model.{Header, HeaderNames}
 import sttp.tapir.*
 import sttp.tapir.json.zio.*
 import sttp.tapir.server.netty.*
+import zio.json.*
 
-object Main extends KyoApp {
+object TapirNettyKyoServerBenchmark extends KyoApp {
   private val STATIC_SERVER_NAME = "kyo-tapir"
 
   private val plainTextMessage: String = "Hello, World!"
 
+  private given JsonCodec[Payload] = DeriveJsonCodec.gen
+  private given Schema[Payload] = Schema.derived
+
   run {
     val plaintextRoute: Unit < Routes =
       Routes.add(
@@ -38,7 +42,7 @@ object Main extends KyoApp {
       .withSocketKeepAlive
       .copy(lingerTimeout = None)
     
-    val server = NettyKyoServer(config).host("0.0.0.0").port(9999)
+    val server = NettyKyoServer(config).host("0.0.0.0").port(8080)
 
     val binding: NettyKyoServerBinding < Async =
       Routes.run(server)(plaintextRoute.andThen(jsonRoute))

+ 57 - 0
frameworks/Scala/tapir/netty-zio-server/src/main/scala/TapirNettyZioServerBenchmark.scala

@@ -0,0 +1,57 @@
+import sttp.model.{Header, HeaderNames}
+import sttp.tapir.Schema
+import sttp.tapir.json.zio.*
+import sttp.tapir.server.netty.zio.NettyZioServer
+import sttp.tapir.ztapir.*
+import zio.*
+import zio.json.*
+import sttp.tapir.server.netty.*
+
+object TapirNettyZioServerBenchmark extends ZIOAppDefault {
+  private val STATIC_SERVER_NAME = "tapir-netty-zio-server"
+
+  private val plainTextMessage: String = "Hello, World!"
+
+  given JsonCodec[Payload] = DeriveJsonCodec.gen
+  given Schema[Payload] = Schema.derived
+
+  override def run = {
+    val plaintextRoute: ZServerEndpoint[Any, Any] =
+        endpoint.get.in("plaintext")
+          .out(header(HeaderNames.Server, STATIC_SERVER_NAME))
+          .out(header[String](HeaderNames.Date))
+          .out(stringBody)
+          .zServerLogic { _ =>
+            for {
+              now <- Clock.currentDateTime
+            } yield Header.toHttpDateString(now.toInstant) -> plainTextMessage
+          }
+
+    val jsonRoute: ZServerEndpoint[Any, Any] =
+        endpoint.get.in("json")
+          .out(header(HeaderNames.Server, STATIC_SERVER_NAME))
+          .out(header[String](HeaderNames.Date))
+          .out(jsonBody[Payload])
+          .zServerLogic { _ =>
+            for {
+              now <- Clock.currentDateTime
+            } yield Header.toHttpDateString(now.toInstant) -> Payload(plainTextMessage)
+          }
+
+    val config = NettyConfig.default
+      .withSocketKeepAlive
+      .copy(lingerTimeout = None)
+
+
+    
+    val server = NettyZioServer[Any](config)
+      .addEndpoint(plaintextRoute)
+      .addEndpoint(jsonRoute)
+      .host("0.0.0.0")
+      .port(8080)
+
+    server.start().flatMap { _ =>
+      ZIO.never
+    }
+  }
+}

+ 42 - 0
frameworks/Scala/tapir/pekko-http-server/src/main/scala/Main.scala

@@ -0,0 +1,42 @@
+import io.circe.generic.auto.*
+import org.apache.pekko.actor.ActorSystem
+import org.apache.pekko.http.scaladsl.Http
+import sttp.model.HeaderNames
+import sttp.tapir.*
+import sttp.tapir.json.circe.*
+import sttp.tapir.server.pekkohttp.PekkoHttpServerInterpreter
+
+import scala.concurrent.Future
+
+@main def tapirPekkoServerBenchmark(): Unit = {
+  implicit val actorSystem: ActorSystem = ActorSystem()
+  import actorSystem.dispatcher
+
+  val STATIC_SERVER_NAME = "tapir-pekko-http-server"
+
+  val plainTextMessage: String = "Hello, World!"
+
+  given Schema[Payload] = Schema.derived
+
+  val plaintextRoute =
+      endpoint.get
+        .in("plaintext")
+        .out(header(HeaderNames.Server, STATIC_SERVER_NAME))
+        .out(stringBody)
+        .serverLogic[Future] { _ =>
+          Future.successful(Right(plainTextMessage))
+        }
+
+  val jsonRoute =
+      endpoint.get
+        .in("json")
+        .out(header(HeaderNames.Server, STATIC_SERVER_NAME))
+        .out(jsonBody[Payload])
+        .serverLogic[Future] { _ =>
+          Future.successful(Right(Payload(plainTextMessage)))
+        }
+
+  val route = PekkoHttpServerInterpreter().toRoute(List(plaintextRoute, jsonRoute))
+
+  Http().newServerAt("0.0.0.0", 8080).bindFlow(route)
+}

+ 1 - 0
frameworks/Scala/tapir/project/build.properties

@@ -0,0 +1 @@
+sbt.version = 1.10.11

+ 0 - 0
frameworks/Scala/kyo-tapir/project/plugins.sbt → frameworks/Scala/tapir/project/plugins.sbt


+ 11 - 0
frameworks/Scala/tapir/tapir-http4s-zio.dockerfile

@@ -0,0 +1,11 @@
+FROM sbtscala/scala-sbt:eclipse-temurin-21.0.6_7_1.10.11_3.6.4
+
+WORKDIR /tapir-benchmark
+COPY http4s-server-zio http4s-server-zio
+COPY common common
+COPY project project
+COPY build.sbt build.sbt
+RUN sbt http4s-server-zio/assembly
+
+EXPOSE 8080
+CMD ["java", "-Xms2G", "-Xmx2G", "-server", "-jar", "/tapir-benchmark/http4s-server-zio/target/scala-3.6.4/tapir-http4s-server-zio-assembly-1.0.0.jar"]

+ 11 - 0
frameworks/Scala/tapir/tapir-http4s.dockerfile

@@ -0,0 +1,11 @@
+FROM sbtscala/scala-sbt:eclipse-temurin-21.0.6_7_1.10.11_3.6.4
+
+WORKDIR /tapir-benchmark
+COPY http4s-server http4s-server
+COPY common common
+COPY project project
+COPY build.sbt build.sbt
+RUN sbt http4s-server/assembly
+
+EXPOSE 8080
+CMD ["java", "-Xms2G", "-Xmx2G", "-server", "-jar", "/tapir-benchmark/http4s-server/target/scala-3.6.4/tapir-http4s-server-assembly-1.0.0.jar"]

+ 11 - 0
frameworks/Scala/tapir/tapir-netty-cats.dockerfile

@@ -0,0 +1,11 @@
+FROM sbtscala/scala-sbt:eclipse-temurin-21.0.6_7_1.10.11_3.6.4
+
+WORKDIR /tapir-benchmark
+COPY netty-cats-server netty-cats-server
+COPY common common
+COPY project project
+COPY build.sbt build.sbt
+RUN sbt netty-cats-server/assembly
+
+EXPOSE 8080
+CMD ["java", "-Xms2G", "-Xmx2G", "-server", "-Dio.netty.leakDetection.level=disabled", "-Dio.netty.recycler.maxCapacityPerThread=0", "-jar", "/tapir-benchmark/netty-cats-server/target/scala-3.6.4/tapir-netty-cats-server-assembly-1.0.0.jar"]

+ 11 - 0
frameworks/Scala/tapir/tapir-netty-kyo.dockerfile

@@ -0,0 +1,11 @@
+FROM sbtscala/scala-sbt:eclipse-temurin-21.0.6_7_1.10.11_3.6.4
+
+WORKDIR /tapir-benchmark
+COPY netty-kyo-server netty-kyo-server
+COPY common common
+COPY project project
+COPY build.sbt build.sbt
+RUN sbt netty-kyo-server/assembly
+
+EXPOSE 8080
+CMD ["java", "-Xms2G", "-Xmx2G", "-server", "-Dio.netty.leakDetection.level=disabled", "-Dio.netty.recycler.maxCapacityPerThread=0", "-jar", "/tapir-benchmark/netty-kyo-server/target/scala-3.6.4/tapir-netty-kyo-server-assembly-1.0.0.jar"]

+ 11 - 0
frameworks/Scala/tapir/tapir-netty-zio.dockerfile

@@ -0,0 +1,11 @@
+FROM sbtscala/scala-sbt:eclipse-temurin-21.0.6_7_1.10.11_3.6.4
+
+WORKDIR /tapir-benchmark
+COPY netty-zio-server netty-zio-server
+COPY common common
+COPY project project
+COPY build.sbt build.sbt
+RUN sbt netty-zio-server/assembly
+
+EXPOSE 8080
+CMD ["java", "-Xms2G", "-Xmx2G", "-server", "-Dio.netty.leakDetection.level=disabled", "-Dio.netty.recycler.maxCapacityPerThread=0", "-jar", "/tapir-benchmark/netty-zio-server/target/scala-3.6.4/tapir-netty-zio-server-assembly-1.0.0.jar"]

+ 11 - 0
frameworks/Scala/tapir/tapir-pekko-http.dockerfile

@@ -0,0 +1,11 @@
+FROM sbtscala/scala-sbt:eclipse-temurin-21.0.6_7_1.10.11_3.6.4
+
+WORKDIR /tapir-benchmark
+COPY pekko-http-server pekko-http-server
+COPY common common
+COPY project project
+COPY build.sbt build.sbt
+RUN sbt pekko-http-server/assembly
+
+EXPOSE 8080
+CMD ["java", "-Xms2G", "-Xmx2G", "-server", "-jar", "/tapir-benchmark/pekko-http-server/target/scala-3.6.4/tapir-pekko-http-server-assembly-1.0.0.jar"]

+ 11 - 0
frameworks/Scala/tapir/tapir.dockerfile

@@ -0,0 +1,11 @@
+FROM sbtscala/scala-sbt:eclipse-temurin-21.0.6_7_1.10.11_3.6.4
+
+WORKDIR /tapir-benchmark
+COPY zio-http-server zio-http-server
+COPY common common
+COPY project project
+COPY build.sbt build.sbt
+RUN sbt zio-http-server/assembly
+
+EXPOSE 8080
+CMD ["java", "-Xms2G", "-Xmx2G", "-server", "-Dio.netty.leakDetection.level=disabled", "-Dio.netty.recycler.maxCapacityPerThread=0", "-jar", "/tapir-benchmark/zio-http-server/target/scala-3.6.4/tapir-zio-http-server-assembly-1.0.0.jar"]

+ 56 - 0
frameworks/Scala/tapir/zio-http-server/src/main/scala/TapirZioHttpServerBenchmark.scala

@@ -0,0 +1,56 @@
+import sttp.model.{Header, HeaderNames}
+import sttp.tapir.Schema
+import sttp.tapir.json.zio.*
+import sttp.tapir.server.ziohttp.ZioHttpInterpreter
+import sttp.tapir.ztapir.*
+import zio.*
+import zio.http.netty.NettyConfig
+import zio.http.netty.NettyConfig.LeakDetectionLevel
+import zio.http.{Request, Response, Routes, Server}
+import zio.json.*
+
+import java.lang.Runtime as JRuntime
+
+object TapirZioHttpServerBenchmark extends ZIOAppDefault {
+  private val STATIC_SERVER_NAME = "zio-http-tapir"
+
+  private val plainTextMessage: String = "Hello, World!"
+  private val NUM_PROCESSORS     = JRuntime.getRuntime.availableProcessors()
+
+  given JsonCodec[Payload] = DeriveJsonCodec.gen
+  given Schema[Payload] = Schema.derived
+
+  override def run = {
+    val plaintextRoute: ZServerEndpoint[Any, Any] =
+        endpoint.get.in("plaintext")
+          .out(header(HeaderNames.Server, STATIC_SERVER_NAME))
+          .out(stringBody)
+          .zServerLogic { _ =>
+            ZIO.succeed(plainTextMessage)
+          }
+
+    val jsonRoute: ZServerEndpoint[Any, Any] =
+        endpoint.get.in("json")
+          .out(header(HeaderNames.Server, STATIC_SERVER_NAME))
+          .out(jsonBody[Payload])
+          .zServerLogic { _ =>
+            ZIO.succeed(Payload(plainTextMessage))
+          }
+
+    val app = ZioHttpInterpreter().toHttp(List(plaintextRoute, jsonRoute))
+
+
+    val config = Server.Config.default
+      .port(8080)
+      .enableRequestStreaming
+
+    val nettyConfig = NettyConfig.default
+      .leakDetection(LeakDetectionLevel.DISABLED)
+      .maxThreads(NUM_PROCESSORS)
+
+    val configLayer = ZLayer.succeed(config)
+    val nettyConfigLayer = ZLayer.succeed(nettyConfig)
+
+    Server.serve(app).provide(configLayer, nettyConfigLayer, Server.customized).exitCode
+  }
+}

+ 1 - 1
frameworks/Scala/zio-http/README.md

@@ -9,7 +9,7 @@ This is the ZIO Http portion of a [benchmarking test suite](../) comparing a var
 
 ## Versions
 
-* [Java OpenJDK 11](https://openjdk.java.net/)
+* [Java OpenJDK 21](https://openjdk.java.net/)
 
 ## Test URLs
 

+ 2 - 2
frameworks/Scala/zio-http/build.sbt

@@ -1,9 +1,9 @@
 name := "zio-http"
 version := "1.0.0"
-scalaVersion := "2.13.14"
+scalaVersion := "2.13.16"
 lazy val root = (project in file("."))
   .settings(
-    libraryDependencies += "dev.zio" %% "zio-http" % "3.0.0-RC10",
+    libraryDependencies += "dev.zio" %% "zio-http" % "3.2.0",
     testFrameworks += new TestFramework("zio.test.sbt.ZTestFramework"),
     assembly / assemblyMergeStrategy  := {
       case x if x.contains("io.netty.versions.properties") => MergeStrategy.discard

+ 3 - 2
frameworks/Scala/zio-http/src/main/scala/Main.scala

@@ -2,12 +2,13 @@ import zio._
 import zio.http._
 import zio.http.netty.NettyConfig
 import zio.http.netty.NettyConfig.LeakDetectionLevel
+import zio.json.EncoderOps
+
 import java.lang.{Runtime => JRuntime}
 
 object Main extends ZIOAppDefault {
 
   private val plainTextMessage: String = "hello, world!"
-  private val jsonMessage: String      = """{"message": "hello, world!"}"""
 
   private val STATIC_SERVER_NAME = "zio-http"
   private val NUM_PROCESSORS     = JRuntime.getRuntime.availableProcessors()
@@ -22,7 +23,7 @@ object Main extends ZIOAppDefault {
     Method.GET / "/json"      ->
       Handler.fromResponse(
         Response
-          .json(jsonMessage)
+          .json(Payload(plainTextMessage).toJson)
           .addHeader(Header.Server(STATIC_SERVER_NAME)),
       ),
   )

+ 6 - 0
frameworks/Scala/zio-http/src/main/scala/Payload.scala

@@ -0,0 +1,6 @@
+import zio.json.{DeriveJsonCodec, JsonCodec}
+
+case class Payload(message: String)
+object Payload {
+  implicit val codec: JsonCodec[Payload] = DeriveJsonCodec.gen
+}

+ 1 - 1
frameworks/Scala/zio-http/zio-http.dockerfile

@@ -1,4 +1,4 @@
-FROM hseeberger/scala-sbt:11.0.12_1.5.5_2.13.6
+FROM sbtscala/scala-sbt:eclipse-temurin-21.0.6_7_1.10.11_2.13.16
 
 WORKDIR /zhttp
 COPY src src