High Performance Grails

I’ve been looking at performance issues for the site we’re building at work. The database is obviously going to be the biggest bottleneck, so we’ve configured 2nd-level caching for read-only tables and some read-mostly tables. We’ve also configured query caching for obvious candidates.

Currently the big hitter is the search page, and some profiling showed that we just needed a few extra indexes and some extra 2nd-level caching, but otherwise it performs pretty well. I still need to test it on production hardware with a large database – the stuff I’ve done so far is on my own high-end developer box with the database, server, and clients all running on the same box.

So I’m now working on applying the 14 rules from Steve Souders and the Yahoo performance team. YSlow has been very useful to measure improvement after each change, and of course Firebug is indispensable.

This is a Grails app, and Grails makes several parts of the process a lot easier, but of course all of these approaches apply to non-Grails apps too.


We’d already put the CSS at the top of the pages and the JavaScript at the bottom, so the next thing I looked at was GZipping content. I configured Tomcat to gzip HTML and text types by adding “compression=’on’ compressableMimeType=’text/html,text/xml,text/plain'” to the HTTP <Connector> element in conf/server.xml. This will change to Apache’s mod_deflate if we end up fronting the Tomcat instances with Apache.

I’m not having Tomcat compress CSS and JavaScript since these are static and I don’t want to keep re-compressing these files for each client, trading I/O cost for CPU cost. So instead I’m compressing once during the build.

This step is currently a little clumsy. Eventually we’ll hook into the war building process using GANT events, but currently I’m unpacking the war after it’s built, compressing each file (leaving the original for the rare client that doesn’t support gzip) and re-packing the war. While I’m doing this, I also use the YUI Compressor to minify all CSS and JavaScript files. This has the benefit of allowing us to develop with readable files but send optimized files to the client.

We also want to configure caching by using far-future Expires header. The best approach for this is to add an Expires header for the year 2038, 10 years from now, etc. and embed a version number in the url but serve the current file. For example instead of:

<script src='/js/foo/bar.js'></script>

we want:

<script src='/js/foo/bar_v2364.js'></script>

The client will get the latest contents of bar.js and the Expires header will tell the browser to cache forever. The version helps us when we deploy a new build and want to ensure that clients re-request the file to get the new latest version. After deploying the new version we’d want to render the new version:

<script src='/js/foo/bar_v2789.js'></script>

We’ll still send them the contents of bar.js but it’ll be newer and since the name is different, the old version will be ignored (and removed from the client’s cache once it’s full) and they’ll use the latest code.

So how to get the version in the urls? Grails has a <g:javascript> taglib but in the past I’d written custom tags to reduce a lot of the boilerplate for <script> tags, CSS <link> tags, and <img> tags and I’ve ported these to this app. Since all JavaScript, CSS , and image files are in the /js/, /css/, and /images/ directories respectively, and we know the extension for JavaScript and CSS files, the custom tags allow us to specify the minimum information, e.g.

<ns:javascript src='bar'/>

generates

<script type='text/javascript' src='/context/js/bar.js></script>


<ns:css name='styles'/>

generates

<link rel='stylesheet' type='text/css' href='/context/css/styles.css' />

and

<ns:image src='spacer.png'/>

generates

<img src='/context/images/spacer.png' border='0' />

where ‘context’ is the context the app is deployed under (blank for the root context), and ‘ns’ is whatever namespace the tag is registered under. Each tag allows extra attributes and these are just passed through to the HTML (e.g. to specify the id, style, etc.)

Since we’re rendering the urls to most static resources using these three tags, we can do a lot of extra work. During the build, I look up the latest SVN version of the root directory and use that to version the resources. I gzip each JavaScript and CSS file and embed the version in the gzipped and non-gzipped file names, and also rename images.

I write out the version into a properties file for use by the custom tags at runtime so they can append the version into the rendered <script>, <link>, and <img> tag urls so they match up with the names in the war.

Embedded image urls in CSS files are a problem using this versioning scheme, but since I’m processing each file during the build, I take that opportunity to rewrite the urls inside all CSS files with the version embedded, so they also agree with the names in the war, e.g.

background-image: url(/images/cluetip/wait.gif);

becomes

background-image: url(/images/cluetip/wait_v2364.gif);


The final optimization I’m working on is reducing the number of files. We have many small JavaScript files being sent to each client and have partitioned our CSS files but typically send the same few files in most pages. So another step in the build process involves bundling JavaScript and CSS files into larger single files. These are minified and gzipped like the rest, and the net effect is fewer requests with a smaller total size.

And I’m also putting together sprites where practical (stretched single-pixel background images and animated images aren’t candidates) to reduce the number of image requests. csssprites.com makes bundling together individual images easy and generates a table showing the position offsets for each image.

Using these techniques I’m seeing YSlow scores of 93-99 on all of our pages when deployed in production mode in Tomcat. We’re planning on using a CDN eventually, but for now I’ve cheated and added ‘localhost’ and ‘maps.google.com’ to the YSlow ‘extensions.firebug.yslow.cdnHostnames’ property since the YSlow score is significantly affected when not using a CDN that it knows about.

Of course developing locally we don’t use war files, so all of this must be disabled unless we’re in production mode. Grails makes this easy (using application.isWarDeployed() and GrailsUtil.environment), so the custom tags skip the step of appending the version in urls, and we’re able to serve uncompressed, unminified files from the file system with no Expires headers.

10 Responses to “High Performance Grails”

  1. Mike says:

    This is awesome Burt! Any chance it might make it into a plugin some day?

  2. Burt says:

    We talked about that at work. There’s a lot of overlap with the functionality that the Jawr plugin provides, but I think this being more Grails-specific might justify creating a plugin.

    I’d need to clean up a few parts, like the integration with GANT events and figure out a proper way to determine the version number for each build.

  3. Jon Chase says:

    You should take a look at Jawr (https://jawr.dev.java.net/) – it handles minification, compression, gzipping, bundling, and more, and it even has a grails plugin.

    I’m using it on a currently internal Grails project and it’s great.

    For example, it will automatically gzip if the client supports it, it will minify files only if you tell it to (so you can debug easily during dev but minify in prod), will adds the caching headers automatically, automatically detects changes in the underlying source files and updates them as needed. It also adds the version id (or something like that) to the end of the src files so that they can easily be updated on redeploy if needed.

    And no, I have no affiliation with the project other than being a happy user.

  4. Jon Chase says:

    Sorry, didn’t see the above post on Jawr.

  5. Burt says:

    My colleague Kris was working with the Jawr plugin but stopped using it. I’ll check with him to see why, but a lot of the ideas for this implementation came from him, based on past experience and using Jawr.

  6. […] wrote up some notes in my previous post about some recent work I’ve done implementing the Yahoo Performance Team’s 14 rules for […]

  7. […] An Army of Solipsists » Blog Archive » High Performance Grails – A nice description of the steps to improve the performance of a Grails based web application using the techniques described in YSlow […]

  8. Martin Kremers says:

    Custom taglibs aren’t a good way!
    It may be much better, if you can use the Plugin “on the fly”!! It should detect css names on the fly and replace them with ther css_xxxy version names in the .jar!

  9. Burt says:

    @Martin,

    There are several ways to implement this – I think the current implementation is very efficient. The alternative that I think you’re suggesting is to wrap each resource request with a Filter, but this would be unnecessarily expensive for a large number of resources. Using the taglibs to generate correct urls in the HTML involves the least amount of work. It’s also a better approach if you use Apache or lighttpd or some other fast front-end server for static resources, or a CDN.

  10. […] This post was Twitted by duydo […]

Creative Commons License
This work is licensed under a Creative Commons Attribution 3.0 License.