Google App Engine: Understanding Non-Invasive Caching

Posted on Monday, October 4, 2010

1


In the last post on understanding caching we talked about the general support of GAE for caching and how we could easily incorporate caching in our application. You would also recollect that we talked about the invasive form of caching in which the business layer was aware about the caching framework. In this post let us talk about the non-invasive way to cache. Since caching is one of the cross cutting concerns like security and logging, the non-invasive way to cache is best implemented by aspects.

Since we are using spring in our application, the solution is biased towards that however, you could use the aspectj framework and the concept remains the same.

A quick reference, a JoinPoint is any Method in your app’s code that you wrote whether you were using AOP or not. A Pointcut is telling AOP which Joinpoints you want your advice code to run with/on. An advice is code you want to run when that JoinPoint method is being called, without adding the code directly into your app’s method. An Aspect is the class that holds the advice code.

Normally, we would know the interesting points to apply the advise because there are the entities that we might be requesting again and again or these entities are expensive to create and hence are created and cached.

Let us see what our advise might look like

public class CacheAspect {
	private final static Logger logger = Logger.getLogger(CacheAspect.class);
	public Object invoke(ProceedingJoinPoint pjp) throws Throwable {
		...
		String cacheKey = getCacheKey(pjp.getArgs(), className, methodName);
		Object returnedValue;
		if ((returnedValue = CacheController.get(cacheKey)) == null) {
			logger.info(" CACHE MISS, starting cache process for = " + cacheKey);
			returnedValue = pjp.proceed();
			CacheController.put(cacheKey, returnedValue);
		} else {
			logger.info(" CACHE HIT! for key = " + cacheKey);
		}
		return returnedValue;
	}

As you would notice that this code is an around advice. This aspect is wrapped around a method. The aspect code checks if the value that we are interested in, identified by the key, exists in the cache or not. If not, then get the return value by invoking the method which is being advised.

returnedValue = pjp.proceed();
and put the value in the cache
CacheController.put(cacheKey, returnedValue);

If you would recall, this is very similar to the code that we wrote in the business layer of the last post. The difference here being that this code is non-invasive since it is not present in the business layer but would be applied as an aspect.

Let us look at what a typical method that this would advice would look like

@SuppressWarnings("unchecked")
	public List findAllActive() {
		List<Customer> customers = new ArrayList<Customer>();
		List resultList = getEntityManager().createQuery("SELECT FROM Customer customer WHERE active = true")
				.getResultList();
		customers.addAll(resultList);
		return customers;
	}

Here we are getting a list of active customers and this does not change that frequently.

So how would our aspect work?

Once it is applied on the findAllActive method, it would check whether the list of customers is present in the cache. If yes, then return the value for the cache else, let the call to the datastore proceed and fetch the values from there. Also, set the values to the cache for subsequent requests.

The pointcut definition looks like this


<bean id="cacheAspect" class="com.bookmyhours.audit.CacheAspect">

<aop:config>
		<aop:aspect id="asp2" ref="cacheAspect">
			<aop:around
				pointcut="execution(public * com.bookmyhours.*.dao.*ProjectAssignmentDao.*findAll*(..))"
				method="invoke" />
		aop:aspect>
aop:config>

So we,

  1. Defined the aspect which had the advice.
  2. Next we identified the joinpoint which is nothing but any method in your class and
  3. Next we defined the bean for the aspect and the
  4. Pointcut definition which defines when the aspect should be applied.

The advantage as mentioned earlier is clean code and a non-invasive way to apply caching to your application.

Advertisements
Posted in: Architecture, Cloud, Java