diff --git a/frameworks/Scala/kyo-scheduler/README.md b/frameworks/Scala/kyo-scheduler/README.md
new file mode 100644
index 00000000000..38244346a1f
--- /dev/null
+++ b/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
diff --git a/frameworks/Scala/kyo-scheduler/benchmark_config.json b/frameworks/Scala/kyo-scheduler/benchmark_config.json
new file mode 100644
index 00000000000..e665c94a2fe
--- /dev/null
+++ b/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"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/frameworks/Scala/kyo-scheduler/build.sbt b/frameworks/Scala/kyo-scheduler/build.sbt
new file mode 100644
index 00000000000..22db16be04d
--- /dev/null
+++ b/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)
\ No newline at end of file
diff --git a/frameworks/Scala/kyo-scheduler/config.toml b/frameworks/Scala/kyo-scheduler/config.toml
new file mode 100644
index 00000000000..515c42b6557
--- /dev/null
+++ b/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"
diff --git a/frameworks/Scala/kyo-scheduler/http4s/src/main/resources/application.properties b/frameworks/Scala/kyo-scheduler/http4s/src/main/resources/application.properties
new file mode 100644
index 00000000000..614284866be
--- /dev/null
+++ b/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
diff --git a/frameworks/Scala/kyo-scheduler/http4s/src/main/resources/logback.xml b/frameworks/Scala/kyo-scheduler/http4s/src/main/resources/logback.xml
new file mode 100644
index 00000000000..378a2fc929b
--- /dev/null
+++ b/frameworks/Scala/kyo-scheduler/http4s/src/main/resources/logback.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
diff --git a/frameworks/Scala/kyo-scheduler/http4s/src/main/scala/http4s/techempower/benchmark/DatabaseService.scala b/frameworks/Scala/kyo-scheduler/http4s/src/main/scala/http4s/techempower/benchmark/DatabaseService.scala
new file mode 100644
index 00000000000..a08883458b9
--- /dev/null
+++ b/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)))
+}
diff --git a/frameworks/Scala/kyo-scheduler/http4s/src/main/scala/http4s/techempower/benchmark/WebServer.scala b/frameworks/Scala/kyo-scheduler/http4s/src/main/scala/http4s/techempower/benchmark/WebServer.scala
new file mode 100644
index 00000000000..ffabe43f354
--- /dev/null
+++ b/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)
+}
diff --git a/frameworks/Scala/kyo-scheduler/http4s/src/main/twirl/index.scala.html b/frameworks/Scala/kyo-scheduler/http4s/src/main/twirl/index.scala.html
new file mode 100644
index 00000000000..70dfcc42ca1
--- /dev/null
+++ b/frameworks/Scala/kyo-scheduler/http4s/src/main/twirl/index.scala.html
@@ -0,0 +1,14 @@
+@import http4s.techempower.benchmark.Fortune
+@(fortunes: Seq[Fortune])
+
+
+
Fortunes
+
+
+ | id | message |
+ @for(fortune <- fortunes) {
+ | @fortune.id | @fortune.message |
+ }
+
+
+
\ No newline at end of file
diff --git a/frameworks/Scala/kyo-scheduler/kyo-scheduler-http4s.dockerfile b/frameworks/Scala/kyo-scheduler/kyo-scheduler-http4s.dockerfile
new file mode 100644
index 00000000000..6ecf32cc149
--- /dev/null
+++ b/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
diff --git a/frameworks/Scala/kyo-scheduler/kyo-scheduler.dockerfile b/frameworks/Scala/kyo-scheduler/kyo-scheduler.dockerfile
new file mode 100644
index 00000000000..3b7645aeda6
--- /dev/null
+++ b/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"]
\ No newline at end of file
diff --git a/frameworks/Scala/kyo-tapir/project/build.properties b/frameworks/Scala/kyo-scheduler/project/build.properties
similarity index 100%
rename from frameworks/Scala/kyo-tapir/project/build.properties
rename to frameworks/Scala/kyo-scheduler/project/build.properties
diff --git a/frameworks/Scala/kyo-scheduler/project/plugins.sbt b/frameworks/Scala/kyo-scheduler/project/plugins.sbt
new file mode 100644
index 00000000000..5bd83c79d15
--- /dev/null
+++ b/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")
\ No newline at end of file
diff --git a/frameworks/Scala/kyo-tapir/src/main/scala/Payload.scala b/frameworks/Scala/kyo-scheduler/zio-http/src/main/scala/Payload.scala
similarity index 54%
rename from frameworks/Scala/kyo-tapir/src/main/scala/Payload.scala
rename to frameworks/Scala/kyo-scheduler/zio-http/src/main/scala/Payload.scala
index 53b27e476c2..4806bf3380d 100644
--- a/frameworks/Scala/kyo-tapir/src/main/scala/Payload.scala
+++ b/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
-}
\ No newline at end of file
+}
diff --git a/frameworks/Scala/kyo-scheduler/zio-http/src/main/scala/ZioHttp.scala b/frameworks/Scala/kyo-scheduler/zio-http/src/main/scala/ZioHttp.scala
new file mode 100644
index 00000000000..448fd878f56
--- /dev/null
+++ b/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
+}
\ No newline at end of file
diff --git a/frameworks/Scala/kyo-tapir/README.md b/frameworks/Scala/kyo-tapir/README.md
deleted file mode 100644
index 4fcc3d2c677..00000000000
--- a/frameworks/Scala/kyo-tapir/README.md
+++ /dev/null
@@ -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
diff --git a/frameworks/Scala/kyo-tapir/benchmark_config.json b/frameworks/Scala/kyo-tapir/benchmark_config.json
deleted file mode 100644
index 3b005996a8f..00000000000
--- a/frameworks/Scala/kyo-tapir/benchmark_config.json
+++ /dev/null
@@ -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"
- }
- }
- ]
-}
\ No newline at end of file
diff --git a/frameworks/Scala/kyo-tapir/build.sbt b/frameworks/Scala/kyo-tapir/build.sbt
deleted file mode 100644
index c7ee01108fc..00000000000
--- a/frameworks/Scala/kyo-tapir/build.sbt
+++ /dev/null
@@ -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)
- }
- )
diff --git a/frameworks/Scala/kyo-tapir/config.toml b/frameworks/Scala/kyo-tapir/config.toml
deleted file mode 100644
index f210a34bf48..00000000000
--- a/frameworks/Scala/kyo-tapir/config.toml
+++ /dev/null
@@ -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"
\ No newline at end of file
diff --git a/frameworks/Scala/kyo-tapir/kyo-tapir.dockerfile b/frameworks/Scala/kyo-tapir/kyo-tapir.dockerfile
deleted file mode 100644
index 4dc5370acf2..00000000000
--- a/frameworks/Scala/kyo-tapir/kyo-tapir.dockerfile
+++ /dev/null
@@ -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"]
\ No newline at end of file
diff --git a/frameworks/Scala/tapir/README.md b/frameworks/Scala/tapir/README.md
new file mode 100644
index 00000000000..8375201828c
--- /dev/null
+++ b/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
diff --git a/frameworks/Scala/tapir/benchmark_config.json b/frameworks/Scala/tapir/benchmark_config.json
new file mode 100644
index 00000000000..96c27368335
--- /dev/null
+++ b/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"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/frameworks/Scala/tapir/build.sbt b/frameworks/Scala/tapir/build.sbt
new file mode 100644
index 00000000000..99947e9a16c
--- /dev/null
+++ b/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
+ )
diff --git a/frameworks/Scala/tapir/common/src/main/scala/Payload.scala b/frameworks/Scala/tapir/common/src/main/scala/Payload.scala
new file mode 100644
index 00000000000..2b886ef91b8
--- /dev/null
+++ b/frameworks/Scala/tapir/common/src/main/scala/Payload.scala
@@ -0,0 +1 @@
+case class Payload(message: String)
diff --git a/frameworks/Scala/tapir/config.toml b/frameworks/Scala/tapir/config.toml
new file mode 100644
index 00000000000..895f262659b
--- /dev/null
+++ b/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"
\ No newline at end of file
diff --git a/frameworks/Scala/tapir/http4s-server-zio/src/main/scala/TapirHttp4sServerZioBenchmark.scala b/frameworks/Scala/tapir/http4s-server-zio/src/main/scala/TapirHttp4sServerZioBenchmark.scala
new file mode 100644
index 00000000000..8e93bd7445c
--- /dev/null
+++ b/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
diff --git a/frameworks/Scala/tapir/http4s-server/src/main/scala/TapirHttp4sServerBenchmark.scala b/frameworks/Scala/tapir/http4s-server/src/main/scala/TapirHttp4sServerBenchmark.scala
new file mode 100644
index 00000000000..f8dadd8d446
--- /dev/null
+++ b/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)
diff --git a/frameworks/Scala/tapir/netty-cats-server/src/main/scala/TapirNettyCatsServerBenchmark.scala b/frameworks/Scala/tapir/netty-cats-server/src/main/scala/TapirNettyCatsServerBenchmark.scala
new file mode 100644
index 00000000000..824b538a079
--- /dev/null
+++ b/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 ()
+ }
diff --git a/frameworks/Scala/kyo-tapir/src/main/scala/Main.scala b/frameworks/Scala/tapir/netty-kyo-server/src/main/scala/TapirNettyKyoServerBenchmark.scala
similarity index 83%
rename from frameworks/Scala/kyo-tapir/src/main/scala/Main.scala
rename to frameworks/Scala/tapir/netty-kyo-server/src/main/scala/TapirNettyKyoServerBenchmark.scala
index 6c543de17e2..f8cbdb4618c 100644
--- a/frameworks/Scala/kyo-tapir/src/main/scala/Main.scala
+++ b/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))
diff --git a/frameworks/Scala/tapir/netty-zio-server/src/main/scala/TapirNettyZioServerBenchmark.scala b/frameworks/Scala/tapir/netty-zio-server/src/main/scala/TapirNettyZioServerBenchmark.scala
new file mode 100644
index 00000000000..fbd2fa34477
--- /dev/null
+++ b/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
+ }
+ }
+}
\ No newline at end of file
diff --git a/frameworks/Scala/tapir/pekko-http-server/src/main/scala/Main.scala b/frameworks/Scala/tapir/pekko-http-server/src/main/scala/Main.scala
new file mode 100644
index 00000000000..ebcdd901368
--- /dev/null
+++ b/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)
+}
\ No newline at end of file
diff --git a/frameworks/Scala/tapir/project/build.properties b/frameworks/Scala/tapir/project/build.properties
new file mode 100644
index 00000000000..00a19d65b65
--- /dev/null
+++ b/frameworks/Scala/tapir/project/build.properties
@@ -0,0 +1 @@
+sbt.version = 1.10.11
\ No newline at end of file
diff --git a/frameworks/Scala/kyo-tapir/project/plugins.sbt b/frameworks/Scala/tapir/project/plugins.sbt
similarity index 100%
rename from frameworks/Scala/kyo-tapir/project/plugins.sbt
rename to frameworks/Scala/tapir/project/plugins.sbt
diff --git a/frameworks/Scala/tapir/tapir-http4s-zio.dockerfile b/frameworks/Scala/tapir/tapir-http4s-zio.dockerfile
new file mode 100644
index 00000000000..7ef30135459
--- /dev/null
+++ b/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"]
\ No newline at end of file
diff --git a/frameworks/Scala/tapir/tapir-http4s.dockerfile b/frameworks/Scala/tapir/tapir-http4s.dockerfile
new file mode 100644
index 00000000000..892210bc692
--- /dev/null
+++ b/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"]
\ No newline at end of file
diff --git a/frameworks/Scala/tapir/tapir-netty-cats.dockerfile b/frameworks/Scala/tapir/tapir-netty-cats.dockerfile
new file mode 100644
index 00000000000..5dec450e3d5
--- /dev/null
+++ b/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"]
\ No newline at end of file
diff --git a/frameworks/Scala/tapir/tapir-netty-kyo.dockerfile b/frameworks/Scala/tapir/tapir-netty-kyo.dockerfile
new file mode 100644
index 00000000000..ed7a5991624
--- /dev/null
+++ b/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"]
\ No newline at end of file
diff --git a/frameworks/Scala/tapir/tapir-netty-zio.dockerfile b/frameworks/Scala/tapir/tapir-netty-zio.dockerfile
new file mode 100644
index 00000000000..aa9c3607080
--- /dev/null
+++ b/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"]
\ No newline at end of file
diff --git a/frameworks/Scala/tapir/tapir-pekko-http.dockerfile b/frameworks/Scala/tapir/tapir-pekko-http.dockerfile
new file mode 100644
index 00000000000..f21480ae5be
--- /dev/null
+++ b/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"]
\ No newline at end of file
diff --git a/frameworks/Scala/tapir/tapir.dockerfile b/frameworks/Scala/tapir/tapir.dockerfile
new file mode 100644
index 00000000000..33e2438eea7
--- /dev/null
+++ b/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"]
\ No newline at end of file
diff --git a/frameworks/Scala/tapir/zio-http-server/src/main/scala/TapirZioHttpServerBenchmark.scala b/frameworks/Scala/tapir/zio-http-server/src/main/scala/TapirZioHttpServerBenchmark.scala
new file mode 100644
index 00000000000..59e189b01bd
--- /dev/null
+++ b/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
+ }
+}
\ No newline at end of file
diff --git a/frameworks/Scala/zio-http/README.md b/frameworks/Scala/zio-http/README.md
index b5ea957391f..e9eb2759004 100644
--- a/frameworks/Scala/zio-http/README.md
+++ b/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
diff --git a/frameworks/Scala/zio-http/build.sbt b/frameworks/Scala/zio-http/build.sbt
index a985d9b5dc9..4c0157b1354 100644
--- a/frameworks/Scala/zio-http/build.sbt
+++ b/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
diff --git a/frameworks/Scala/zio-http/src/main/scala/Main.scala b/frameworks/Scala/zio-http/src/main/scala/Main.scala
index 8af102fd7f6..652a4a43357 100644
--- a/frameworks/Scala/zio-http/src/main/scala/Main.scala
+++ b/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)),
),
)
diff --git a/frameworks/Scala/zio-http/src/main/scala/Payload.scala b/frameworks/Scala/zio-http/src/main/scala/Payload.scala
new file mode 100644
index 00000000000..86b4fc705d7
--- /dev/null
+++ b/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
+}
\ No newline at end of file
diff --git a/frameworks/Scala/zio-http/zio-http.dockerfile b/frameworks/Scala/zio-http/zio-http.dockerfile
index f05ead620fe..5c814e5887a 100644
--- a/frameworks/Scala/zio-http/zio-http.dockerfile
+++ b/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