Grails - Springcache Plugin

Springcache Plugin

The Springcache plugin allows you to apply caching and flushing pointcuts to methods on your Grails artefacts (typically services) with a minimum of fuss.

The plugin is primarily intended for scenarios when you have Grails service methods that invoke expensive operations such as HTTP gets, web service calls, filesystem IO, etc. Although you can use the Springcache plugin to cache service methods that query or update GORM domain objects you are almost certainly better off using the Hibernate 2nd level cache (see the relevant sections in the Grails documentation).

Version 1.1 of the plugin is a complete re-write necessitated by the fact that the spring-modules-cache project that provided the underlying implementation for previous versions of the plugin has been abandoned whilst Grails has moved on to Spring 3.0 which breaks compatibility with spring-modules-cache. The plugin now contains its own Spring 3.0 compatible caching/flushing implementation.

Version 1.1 of the plugin requires Grails 1.2-M3 or higher. If you are still using an earlier version of Grails you should can use grails install-plugin springcache 1.0.1 to install a compatible version.

By default the plugin uses EHCache but it is reasonably simple to create and configure your own cache 'provider' for the plugin if you want to use an different caching library. An example of doing so can be found in test/projects/alt-cache-impl under the plugin source tree.

Upgrading From Earlier Plugin Versions

If you are upgrading from an earlier version of the Springcache plugin there are a few things to be aware of:

  • You will need to change the import statements for your Cacheable and CacheFlush annotations to point to the grails.plugin.springcache.annotations package.
  • The default map cache is no more. Grails includes EHCache and it's very easy to configure so that is now the default.
  • If you were using a cache provider other than EHCache you will need to write some code to implement your own grails.plugin.springcache.CacheProvider - see below.
  • Caching and flushing model configuration has changed slightly and is now simpler. It should be no more than a couple of minutes work to adapt your existing config. See the examples below.

Applying Caching and Flushing Behaviour to Methods

Simply add @grails.plugin.springcache.annotations.Cacheable(modelId="ACachingModel") to methods that should cache their results and @grails.plugin.springcache.annotations.CacheFlush(modelId="AFlushingModel") to methods that should flush the cache.

Be aware that the annotations will only have any effect on Spring-managed beans. If you create instances of your class directly rather than getting them from the application context they will not be decorated with caching/flushing behaviour.

The modelId attributes tell the plugin which cache should be used for a particular method's results and which cache(s) should be flushed by particular methods. Using models allows different cache providers to map models to caches in any appropriate manner. The default EHCache provider simply uses named caches.

The model Ids you specify on your annotations should match models configured in your application's Config.groovy, e.g.:

springcache {
	caching	Models {
		myCachingModel1 {
			cacheName = "CACHE_1"
		}
		myCachingModel2 {
			cacheName = "CACHE_2"
		}
	}
	flushingModels {
		myFlushingModel {
			cacheNames = "CACHE_1,CACHE_2"
		}
	}
}

For EHCache you simply need to configure a single cacheName property for each caching model and a cacheNames property for each flushing model. The cacheNames property for flushing models can be a single cache name or a comma- separated list which means that methods annotated with that flushing model will flush multiple caches at once.

A simple example might be:

PiracyService.groovy

@Cacheable(modelId="Pirates")
def getPirates() {
	Pirate.list()
}

@Cacheable(modelId="Pirates") def findPirates(name) { Pirate.findAllByNameIlike("%$name%") }

@Cacheable(modelId="Ships") def getShips() { Ship.list() }

@CacheFlush(modelId="Pirates") void registerNewPirate(Pirate sailor) { sailor.save(failOnError: true) }

@CacheFlush(modelId="Ships") void registerNewShip(Ship ship) { ship.save(failOnError: true) }

@CacheFlush(modelId="All") void registerNewShipWithCrew(Ship ship, Collection<Sailor> crew) { ship.save(failOnError: true) crew*.save(failOnError: true) }

Config.groovy

springcache {
	cachingModels {
		Pirates.cacheName = "PirateCache"
		Ships.cacheName = "ShipCache"
	}
	flushingModels {
		Pirates.cacheNames = "PirateCache"
		Ships.cacheNames = "ShipCache"
		All.cacheNames = "PirateCache,ShipCache"
	}
}

This ties the flushes on the register* methods to the particular caches they affect, so after calling registerNewPirate the methods getPirates and findPirates will re-populate their cached results but getShips would still use any cached results from previous calls. Calling registerNewShipWithCrew will flush both caches as it is changing data associated with both.

Note that it is fine for multiple methods to share the same caching model (or flushing model). Both getPirates and findPirates in the example above share the same caching model. Cache entries are keyed on target object (the service instance in this case), method name and call parameters so there should be no confusion when using the same caches on multiple methods.

There are various strategies you can adopt in naming and grouping caches and their associated caching and flushing models, this example shouldn't be seen as definitive.

Using a Different Cache Provider

EHCache is the default cache provider for the Springcache plugin but it is straightforward to substitute a different caching library if you would prefer. Basically you need to:

  • Implement grails.plugin.springcache.CacheProvider, grails.plugin.springcache.CacheFacade, grails.plugin.springcache.CachingModel and grails.plugin.springcache.FlushingModel to provide an adapter for your caching library.
  • Register your CacheProvider implementation as a bean in the Spring context.
  • Set the config value springcache.provider.bean to the name of the bean.
  • Configure caching and flushing models using properties appropriate to your caching library.
  • Ensure any external configuration your caching library requires is present.
There is a useful abstract base class grails.plugin.springcache.AbstractCacheProvider that you can extend. This will handle mapping of model Ids to CachingModel and FlushingModel instances. You only need to add the following method implementations:
  • createCachingModel(String, Properties) - creates a configured CachingModel instance. The method is passed the model id and properties from Config.groovy.
  • createFlushingModel(String, Properties) - the equivalent method for creating a configured FlushingModel instance.
  • getCache(CachingModel) - returns a CacheFacade instance appropriate for the CachingModel passed in.
  • getCaches(FlushingModel) - returns a Collection of CacheFacade instances appropriate for the FlushingModel passed in.
Different caching providers may require or allow additional properties for caching and flushing models. The Config.groovy entries can be extended e.g.:

springcache {
	cachingModels {
		myCachingModel {
			cacheName = "someCache"
			group = "someGroup"
			ttl = 300
		}
	}
}

In this example your CacheProvider implementation's createCachingModel method would be called with the String "myCachingModel" and a Properties object containing [cacheName: "someCache", group: "someGroup", ttl: "300"].

The default EHCache provider classes in the package grails.plugin.springcache.provider.ehcache are a good example of how to implement the required classes.

Tips

Disabling

Caching can be disabled with the config key springcache.disabled=true, for exaple you may want to do this in your test environment to avoid having methods you are testing returning cached results.

Logging Output

To see logging from the plugin set the logging level on "grails.plugin.springcache" in your Config.groovy.

FAQ

My cache config doesn't seem to be working.

Ensure all your config for the Springcache plugin is nested inside a single 'springcache' block in Config.groovy otherwise only the last block will take effect.

Release Notes

1.1

  • Complete rewrite to support Grails 1.2 and Spring 3.0.

1.0.1

  • Fixes bug where plugin causes crash if disabled when debug logging is switched on.
  • Fixes compatibility with Java 1.5.

1.0

  • Configure alternate caching providers via Config.groovy rather than having to override bean definitions in resources.groovy
  • Removed dependency on joda-time which was only there for testing
  • Better synchronization for getting caches from the mapcache CacheManager

0.2

  • Configure caching and flushing models via Config.groovy
  • Flag to disable plugin entirely for testing environments

0.1

  • Initial release

Contact

The plugin code is hosted on GitHub. Please feel free to fork the plugin and contribute patches.

Please raise defects or enhancements on JIRA.

Questions, comments? rob@energizedwork.com.