Anton Keks
23 Jan 2012

Reducing memory usage of Scala/Scalate web application

Antud lugu on saadaval ainult inglise keeles

We have a couple of web applications written in Scala using the Scalatra web framework from LinkedIn. A good option with Scalatra is to use  Scalate as a template engine.

We use SCAML syntax, which is a scala-based HAML.

Anyway, the problem is that Scalate uses scala compiler to compile templates on-the-fly. This is perfect for development (you change the file and just push reload in the browser), but is very memory-hungry on production: scala compiler runs several threads and caches lots of stuff in the heap as well as all reloaded templates consume permgen - the memory that is never freed by garbage collector.

To reduce memory usage and increase first request performance, it makes sense to precompile your templates. Unfortunately, Scalate user guide only provides some hints on how to do it with Maven (there is a plugin) and SBT (there is also a plugin). We prefer to use Ant for our builds, so there is no a ready-made solution.

Actually, as part of the research process I tried to switch to SBT, but found it a bit too difficult to customize - it uses the same horrible directory layout as Maven by default, but we are very happy with out much simpler approach.

I actually got SBT working with out project, but the precompile plugin mentioned in the user guide is built for an older version of SBT and doesn’t seem to be supported by the author anymore.

The solution was to figure out a lightweight way of precompiling the templates. After examining sources of Scalate and experimenting, I came out with this:

object TemplatePrecompiler {
  val workdir = new File(System.getProperty("scalate.workdir", "tmp"))

  def main(args: Array[String]) {
    val precompiler = new org.fusesource.scalate.support.Precompiler
    precompiler.sources = Array(new File("webapp/WEB-INF/"))
    precompiler.workingDirectory = workdir
    // precompiler.targetDirectory - set this to your WEB-INF/classes 
    // path if you are packaging a war file
    precompiler.execute()
  }
}

Fortunately, the Precompiler class is already included in the scalatra-core jar.

The biggest trick here is to make sure the precompiled templates get into the correct place where classloader will look for them. Note the use of scalate.workdir system property. Do not leave this unset - or it will generate a new directory each time under the /tmp. Set this to some writeable directory that doesn’t change for this application. We just use “tmp” under the application directory, just like Play Framework does.

Your precompiled classes will be put under tmp/classes and generated scala sources to tmp/src. You can ignore the sources - they are not needed when Scalate tries to load precompiled templates. It looks for them first in scalate.workdir/classes  (tmp/classes in our case) and then your normal Webapp classloader, so you can also put them to WEB-INF/classes.

We then run this precompiler class from Ant at the end of the build with java ant task. The result - decreased memory usage by about 300 Mb, from 400-500 to about 130 Mb.

Our recent stories