Fork me on GitHub

23 Feb 2010

Full page caching in Grails with Ehcache-Web

I'm on the verge of releasing a new version of the Springcache plugin that will feature a pretty cool new annotation-driven page fragment caching feature based on Ehcache-web. However one of the things that came up during discussions on the mailing list was full page caching. I mentioned that it was pretty straightforward and promised to blog about how to do it, so…

Install Ehcache-web

Add the following to your dependencies in BuildConfig.groovy:
runtime("net.sf.ehcache:ehcache-web:2.0.0") {
 excludes "ehcache-core"
}

Install Grails templates

Run: grails install-templates

Set up a page caching filter

Edit src/templates/war/web.xml and add this to the filters section:
<filter>
 <filter-name>MyPageCachingFilter</filter-name>
 <filter-class>net.sf.ehcache.constructs.web.filter.SimplePageCachingFilter</filter-class>
 <init-param>
  <param-name>cacheName</param-name>
  <param-value>myPageCache</param-value>
 </init-param>
</filter>
And this as the first entry in the filter-mappings section:
<filter-mapping>
 <filter-name>MyPageCachingFilter</filter-name>
 <url-pattern>/controller/action/*</url-pattern>
 <dispatcher>REQUEST</dispatcher>
</filter-mapping>

Configure the cache

Unfortunately you can't configure the cache in grails-app/conf/spring/resources.groovy using EhCacheFactoryBean instances as the servlet filter initialises before the Spring context. The cache definitions need to be added to a ehcache.xml which would normally be placed in grails-app/conf. Here is an example that works for the filter definitions above:
<ehcache>

    <diskStore path="java.io.tmpdir"/>

    <defaultCache
            maxElementsInMemory="10"
            eternal="false"
            timeToIdleSeconds="5"
            timeToLiveSeconds="10"
            overflowToDisk="true"
            />

    <cache name="myPageCache"
           maxElementsInMemory="10"
           eternal="false"
           timeToIdleSeconds="10000"
           timeToLiveSeconds="10000"
           overflowToDisk="true">
    </cache>

</ehcache>

You will need to add another filter and filter-mapping and potentially another cache for each URL pattern you want to cache separately.

Full page caching is fine for simple pages but isn't as flexible as fragment caching. For example, any authenticated state (such as a "Welcome back xxx" label) on the page would get cached across all users. Because Grails uses Sitemesh and has the <g:include> tag for including output from other controllers a fragment caching solution is a good fit. The 1.2 release of the Springcache plugin will add fragment caching functionality. However, I can imagine using full-page caching like this for things like RSS feeds.

No comments: