Announcing results with scalatra
As other posts in the series mentioned, i am trying out some web frameworks and data stores with a small web application which would be used to announce the results of a hypothetical exam. You can find the details in the first post. Today, the example app will be of scalatra.
Scalatra is a sinatra-like lightweight web framework written in scala. For developers living under a rock for a few years, sinatra-like means just enough tools to map a URL to a method. No ORM, no templating, no authentication over LDAP. Just URLs and methods. Scala is a programming language which tries to leverage both object-oriented and functional concepts. Google is full of sites telling reasons why scala is a great language.
It is clear that a programming language and a simple web framework is far from being enough to develop a web application nowadays. In order to query results, they have to be stored in some database first. For this specific example, i’ll use mysql. A simple web-application like this one would not actually require another layer on top of the database but since i am evaluating the ecosystem rather than developing a real-life application, i’ll add scala-query into the mix. I hope it will ease the pain JDBC will induce. On UI side, mixing HTML with application logic is pretty much accepted as a bad practice, so i’ll use scalate as the templating engine. As all of the tools are leaning towards sbt, it will be the bowl holding the soup together.
Since scalatra is the core piece in the environment, it seemed logical to start with it and add other ingredients as i go. There are two ways to create a scalatra project skeleton. First way is simply cloning this repo. Other one requires installation of giter8 and sbt seperately. Both will come scalate included. Difference is former uses sbt 0.7 series while latter uses sbt 0.10. If you’re like me and have a tendency to walk on the edge, you’ll need to “g8 scalatra/scalatra-sbt” after installing these shiny tools. It will ask some questions about the project and create it.
$ g8 scalatra/scalatra-sbt organization [com.example]: name [scalatra-sbt-prototype]: resultannounce servlet_name [MyScalatraFilter]: ResultAnnouncer scala_version [2.9.0-1]: 2.9.0 version [1.0]:
After that is finished cd into your project and run “sbt”. It will download some files and give you the sbt shell. Run an “update” to get your dependencies followed by a “jetty-run” to see some Hello application running on localhost:8080… Now get coding!
First thing i did was changing the default Hello screen to the login page, written in template main using jade.
get("/") {
templateEngine.layout(root+"main.jade")
}
After setting the form to simply POST to a url like /r/idnumber, it was easy to handle values in scalatra side.
post("/r/:idn") {
val result = Result of (params("idn"),params("pass"))
result match {
case Some(_) => templateEngine.layout(
root+"result.jade", Map("result"->result))
case _ => templateEngine.layout(
root+"main.jade", Map("formErr"->"Wrong Details"))
}
}
The Result here is a DAO i have thrown with my limited scala and virtually non-existent scala-query knowledge.
object Result {
val db = Database.forURL("jdbc:mysql:///ss?user=root",
driver="com.mysql.jdbc.Driver")
def of(id:String, passwd:String) = {
db withSession {
val q = for (e <- Results if e.id === id) yield e
q.first
}
}
}
object Results extends
Table[(String, String, String, String)]("results") {
def id = column[String]("id", O PrimaryKey)
def passwd = column[String]("passwd", O NotNull)
def name = column[String]("name", O NotNull)
def result = column[String]("result", O NotNull)
def * = id ~ passwd ~ name ~ result
}
There is a password-hash control step too but i omitted it for this post. The Results object defines the database table and enables us to query without writing any SQL.
After some manual testing to check everything is in working order, i loaded 3M results in that table and started hammering the application with jmeter. Simple stress tests on login screen which does not touch database or anything yielded 658 requests-per-second for 100 concurrent users and 90% of the requests were served within 180ms. Considering the test for couchdb on the same machine gave 500 requests-per-second with 290ms 90% line, scalatra and scalate seems like an improvement. But as i have mentioned before this is hardly a real-life scenario: Users generally won’t bounce off login screen, they will login using their details and try to see their exam results. Using a jmeter workbench for this scenario, requests-per-second dropped to 396 and 90% line increased to 413ms. Now that seems a like a little stepback from what i was able to achieve with couchdb. I’ll try to identify the pieces (scalate, scala-query, mysql) causing the slowdown but that’s for another day.
Scalability side is the same as the couch: application is totally stateless so running mirrors behind a load-balancer should be enough to increase that RPS to the point required. Since there are no writes there shouldn’t be any problem for scaling them.
The whole application took my whole day including the time needed to get to the default Hello screen. Besides mysql, all the tools here were new to me, yet i was able to make something that solves a problem. Although i am a newbie, scala is fun to work with and considering what i was able accomplish in a day, it is nowhere near complex. But couchdb still seems a better fit for this kind of problem. Results are documents and a document store with HTML rendering capabilities is all one can ask for.
UPDATE: Code is now on github