By changing the layout of my site the other day I caused myself some problems. I knew that my markup processing code – that translates a combination of textile formatted text with embedded source fragments – was a tad inefficient. I rolled my own processor that uses Textile4J to process the text with an added filter-style system for the source fragments, formatted with JHighlight. So a fragment of this page may look like this:
processor so that any subsequent call to the same tag–library with the same value will use
the cached version.
<textfu:jhighlight language="xml">
<tf:out value="${blogPost.body}" engine="textile" useCache="true"/>
</textfu:jhighlight>
_Voila!_ Faster loading pages and a much happier VPS(Virtual Private Server).
h2. Caching implementation
I knew it was inefficient but it was brought home quite how inefficient yesterday.
The day before yesterday I changed the way my site is laid out so that there is more content on the home page and the listing pages. No more excerpt or intro text, straight to the main article. This meant that my cobbled together – or mashed up perhaps (makes it sound cool rather than stupid) – markup processor was doing a lot more work per request.
It may be a coincidence but my Gandi–hosted virtual server bombed yesterday. Happy to accept that I may have been to blame, I concluded that I should at least eliminate the code from my enquiries.
Incidentally, Gandi have still not responded to the support ticket I raised yesterday. As my friend Keeran quite rightly put it:
[Gandi] has potential to be huge (because of the pricing), but if the support is that weak they‘re going to FAIL.
Caching
The solution I came up with isn‘t ideal and I haven‘t given much thought to best–practice or the design that much.
I have this additional code in my tag–library class. And yes, I called my markup processor Textfu. I thought for ages about a suitable, meaningful and succinct name for it. In the end I failed, so Textfu had to do. I wonder if the inventor of attachment_fu went through the same process and ended up just settling on that name.
if (useCache) {
log.debug("Using CacheFu");
String key = new BASE64Encoder().encode(MessageDigest.getInstance("MD5").digest(bodyText.getBytes()));
if(CacheFu.get(key) != null) {
log.debug("Found cached string for key " + key);
html = CacheFu.get(key);
} else {
log.debug("No value found in cache for " + key);
html = TextFuFactory.getProcessor(engine).decorate(bodyText);
CacheFu.put(key, html);
}
} else {
html = TextFuFactory.getProcessor(engine).decorate(bodyText);
}
Where before I had a tag–library that read the following:
I know have one with some extra functionality. Namely the option to cache the output of the markup processor so that any subsequent call to the same tag–library with the same value will use the cached version.
Voila! Faster loading pages and a much happier VPS.
Caching implementation
I am uncertain as to whether what I‘ve implemented is a suitable solution for any large scale project or whether it‘s a big no no in terms of best–practice. It seems to be working well for my site in any case. If what I've done has any flaws, please let me know. Note the imaginative name of the class again.
import java.util.Map;
import java.util.HashMap;
public class CacheFu {
//----------------------------------------------------------------------- Static Properties and Constants
private static ThreadLocal<Map<String,String>> LOCAL_CACHE = new ThreadLocal<Map<String, String>>();
//----------------------------------------------------------------------- Static Methods
public static String get(String key) {
return getCache().get(key);
}
public static void put(String key, String value) {
getCache().put(key, value);
}
private static Map<String, String> getCache() {
if (LOCAL_CACHE.get() == null) {
LOCAL_CACHE.set(new HashMap<String, String>());
}
return LOCAL_CACHE.get();
}
}
First published on Jan 28, 2009. Last updated on: Jan 29, 2009.