Browse Source

Add Scala framework: Scalene (#5429)

* adding scalene framework test

* use function completion for all routes
Dan Simon 5 years ago
parent
commit
c116e31351

+ 1 - 1
.travis.yml

@@ -74,7 +74,7 @@ env:
     - 'TESTDIR="Rust/warp-rust"'
     - 'TESTDIR="Scala/akka-http Scala/blaze Scala/cask Scala/colossus Scala/finagle"'
     - 'TESTDIR="Scala/finatra Scala/finch Scala/http4s"'
-    - 'TESTDIR="Scala/play2-scala Scala/youi"'
+    - 'TESTDIR="Scala/play2-scala Scala/scalene Scala/youi"'
     - 'TESTDIR="Scala/vertx-web-scala"'
     - "TESTLANG=Scheme"
     - "TESTLANG=Swift"

+ 20 - 0
frameworks/Scala/scalene/README.md

@@ -0,0 +1,20 @@
+# Scalene Benchmarking Test
+
+Scalene is a lightweight reactive HTTP server framework written in Scala.  It is
+implemented directly on Java NIO and handles all TCP connection management,
+message parsing/encoding, and request routing.
+
+https://github.com/DanSimon/scalene
+
+## Test Source Code Organization
+
+All source for this test is in [Benchmark.scala](src/main/scala/Benchmark.scala)
+
+Dependencies are defined in [build.sbt](build.sbt).
+
+## Test URLs
+
+* Plaintext - `http://localhost:8080/plaintext`
+* JSON - `http://localhost:8080/json`
+* DB - `http://localhost:8080/db`
+* Queries - `http://localhost:8080/queries/<queries>`

+ 24 - 0
frameworks/Scala/scalene/benchmark_config.json

@@ -0,0 +1,24 @@
+{
+  "framework": "scalene",
+  "tests": [{
+    "default": {
+      "orm": "Raw",
+      "database_os": "Linux",
+      "db_url": "/db",
+      "query_url": "/queries/",
+      "json_url": "/json",
+      "plaintext_url": "/plaintext",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Micro",
+      "database": "Postgres",
+      "framework": "scalene",
+      "language": "Scala",
+      "platform": "None",
+      "webserver": "None",
+      "os": "Linux",
+      "display_name": "scalene",
+      "notes": ""
+    }
+  }]
+}

+ 18 - 0
frameworks/Scala/scalene/build.sbt

@@ -0,0 +1,18 @@
+val scaleneUri = uri("https://github.com/DanSimon/Scalene.git#0.1.0")
+
+lazy val scaleneRouting = ProjectRef(scaleneUri,"scalene-routing")
+lazy val scaleneSQL = ProjectRef(scaleneUri,"scalene-sql")
+
+lazy val `scalene-benchmark` = (project in file("."))
+  .dependsOn(scaleneRouting)
+  .dependsOn(scaleneSQL)
+
+scalaVersion := "2.12.10"
+version := "0.1.0-SNAPSHOT"
+
+libraryDependencies ++= Seq(
+  "org.postgresql" % "postgresql"        % "42.2.0",
+  "org.json4s"                   %% "json4s-jackson"       % "3.5.3",
+  "com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.9.2"
+)
+

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

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

+ 1 - 0
frameworks/Scala/scalene/project/plugins.sbt

@@ -0,0 +1 @@
+addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.6")

+ 23 - 0
frameworks/Scala/scalene/scalene.dockerfile

@@ -0,0 +1,23 @@
+FROM adoptopenjdk/openjdk13
+
+ARG SBT_VERSION=1.3.7
+
+RUN \
+  apt-get update && \
+  apt-get -y install git
+
+# Install sbt
+RUN \
+  curl -L -o sbt-$SBT_VERSION.deb https://dl.bintray.com/sbt/debian/sbt-$SBT_VERSION.deb && \
+  dpkg -i sbt-$SBT_VERSION.deb && \
+  rm sbt-$SBT_VERSION.deb && \
+  apt-get update && \
+  apt-get install sbt && \
+  sbt sbtVersion
+
+WORKDIR /scalene
+COPY project project
+COPY src src
+COPY build.sbt build.sbt
+RUN sbt assembly -batch
+CMD ["java", "-server", "-jar", "target/scala-2.12/scalene-benchmark-assembly-0.1.0-SNAPSHOT.jar"]

+ 83 - 0
frameworks/Scala/scalene/src/main/scala/Benchmark.scala

@@ -0,0 +1,83 @@
+package scalene.benchmark
+
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.module.scala.DefaultScalaModule
+import scalene.actor.Pool
+import scalene.routing._
+import scalene.http.{BodyData, BodyFormatter, ContentType}
+import scalene.sql._
+import BasicConversions._
+
+object Main extends App {
+
+  trait JsonMessage
+  case class JsonRouteMessage(message: String) extends JsonMessage
+  case class DBRouteMessage(id: Int, randomnumber: Int) extends JsonMessage
+  case class MultiDBRouteMessage(items: Array[DBRouteMessage]) extends JsonMessage
+
+  implicit val messageFormatter = new BodyFormatter[JsonMessage] {
+    val mapper: ObjectMapper = new ObjectMapper().registerModule(DefaultScalaModule)
+    def format(msg: JsonMessage) = {
+      val obj = msg match {
+        case MultiDBRouteMessage(items) => items
+        case other => other
+      }
+      BodyData.Static(mapper.writeValueAsBytes(obj))
+    }
+    val contentType = Some(ContentType.`application/json`)
+  }
+
+  val settings = Settings.basic(
+    serverName = "scalene",
+    port = 8080,
+    server = ServerSettings.Default
+  )
+
+  
+  implicit val pool = new Pool
+  val worldClient = MiniSQL.client(
+    "world-client",
+    "jdbc:postgresql://tfb-database:5432/hello_world",
+    "benchmarkdbuser",
+    "benchmarkdbpass"
+  )
+
+  val random = new java.util.Random
+  
+  def randomWorld(session: MiniSQLSession): Option[DBRouteMessage] = {
+    val stmt = session.prepared("SELECT id, randomnumber FROM world WHERE id = (?)")
+    stmt.setInt(1, math.abs(random.nextInt) % 10000 + 1)
+    val rs = stmt.executeQuery()
+    if (rs.next()) {
+      Some(DBRouteMessage(rs.getInt(1), rs.getInt(2)))
+    } else {
+      None
+    }      
+  }
+
+  val dbRoute = GET / "db" to {_ =>
+    worldClient.query{session =>
+      randomWorld(session).map{_.ok}.getOrElse("N/A".notFound)
+    }
+  }
+
+  val QueryNum = ![Int]
+    .map{i => if (i < 1) 1 else if (i > 500) 500 else i}
+    .recover{_ => 1}
+
+  val multiRoute = GET / "queries" / QueryNum to {num =>
+    worldClient.query{session =>
+      MultiDBRouteMessage(Array.fill(num)(randomWorld(session).get)).ok
+    }
+  }
+
+  val routes = Routes(
+    GET / "plaintext" to {_ => "Hello, World!".ok},
+    GET / "json"      to {_ => JsonRouteMessage("Hello, World!").ok},
+    dbRoute,
+    multiRoute
+  )
+
+  Routing.start(settings, routes)
+}
+