Sunday, January 11, 2009

AOP, Caching, and memcached

Last week I started playing around with Aspect-Oriented Programming (AOP) in ColdSpring and was pretty amazed at how useful it could be. To be able to alter the functionality of your application without touching your core application code is an extremely powerful concept, but I wasn't sure where to begin.

The example on the ColdSpring site shows how AOP can be applied to handle logging across an application, but that doesn't seem all that useful (or sexy) to me. I wanted a better scenario.

After reviewing some code in a project I've been working on, I noticed a prime example of where I could use AOP: caching.

There was code inside multiple function calls that would perform caching based on the component, method, and arguments being passed into the method. The code was using non-deterministic caching, meaning if the data didn't exist in the cache, the system would fetch the data then put it into the cache. The caching code looks similar to this:


<cfset key = application.cache.getKey("reportDAO","getReportByID",arguments) />

<cfif application.cache.hasData(key)>

<cfset report = application.cache.getData(key) />

<cfelse>

... build the report ...

<cfset application.cache.setData(key,report) />

</cfif>


The code worked fine, but I had to add those same 4 lines of code to each method I wanted to cache. Plus, it led to a low level of cohesion; the method needed to know how to build a report as well as how to store and retrieve itself in memory.

With AOP, I could now configure ColdSpring to intercept each method call I wanted to cache without having to flood my code with caching logic.

As a nice side bonus, the non-deterministic key/value caching strategy I was using turned out to be similar to how memcached, a high-performance, distributed memory object caching system, caches its data. Knowing this, I was able to modify the caching advice component I created and plugin memcached in a matter of minutes.

I'm currently working on an XML based caching component that will handle bean/method caching in the request scope, session scope, and memcached. I'll post more details when I have something to show.

2 comments:

  1. Hey Tony. This is actually really interesting; I think I'm running into the same issue with a Flex/AS3 app I'm writing; my caching code is starting to proliferate, much to my annoyance, and I find it really annoying.
    Did this AO direction you were heading off to ever work out? It certainly seems intriguing...

    ReplyDelete
  2. It's worked out pretty well so far. I'm only doing request/session level caching right now, since I don't feel comfortable enough with memcached to put it into a production environment.

    Here's what I got hung up on, in case you try to write something similar:

    1) ColdSpring's AOP doesn't handle dynamic default values for arguments. Since the proxy is created by introspecting the component's metadata, any dynamic default values, such as "#now()#", will be converted to "[Runtime Expression]" or something like that. This isn't really a ColdSpring problem, but a limitation of ColdFusion's getMetaData(). So if you're trying to intercept a method that uses dynamic defaults, you might want to use switch your logic to check if the key exists in the arguments scope instead.

    2) The data gets cached using a unique key generated from the method's arguments, ensuring that if the same method is called again with the same set of arguments, the result will be fetched from the cache. Trying to generate the key for complex data types can be tricky, although you could maybe serialize your objects to JSON and try that out. However, I'm not sure if ColdFusion serializes JSON the same way (case, order) every time or not though.

    3) Cache invalidation. http://martinfowler.com/bliki/TwoHardThings.html

    If I were to re-write what I have, I might try to combine the AOP functionality from ColdSpring with the caching functionality from LogBox. I haven't used LogBox yet, but from what I've read it's worth looking into.

    ReplyDelete