Wednesday, November 21, 2007

Java TTL Cache, the Quick and Dirty Way

There are many caching systems in java. I really like some of them (especially Ian Schumacher's LRU Cache). But sometimes you just to drop a quick few lines to get something in a local cache. Here is one way of doing it:

private static Map urlItemCache = Collections.synchronizedMap(new HashMap());
private static Map urlItemCacheTtl = Collections.synchronizedMap(new HashMap());
private static final long TTL=1000*60*60;//one hour in milliseconds

public static String findItemByUrl( String url) {
String item="";
Long now=Long.valueOf(Calendar.getInstance().getTimeInMillis());

//try to get the item from the local cache
item = urlItemCache.get(url);
if ( !isEmpty(item)) {
//urlItemCacheTtl.get(url) is guaranteed to have a value, since it's added at the same time urlItemCache is.
if (now.longValue() - urlItemCacheTtl.get(url).longValue() < TTL ) {
urlItemCacheTtl.put(url, now); //see comment below
return item;
} else {
urlItemCache.remove(url);
urlItemCacheTtl.remove(url);
}
}
//not found in cache
//... do you application magic here to get the item ...
if ( !isEmpty(item) ) {
urlItemCache.put(url, item); //add to local cache
urlItemCacheTtl.put(url, now);
return item;
} //else
return null;
}

3 comments:

  1. Has 2 common problems, it's not threadsafe, and hot objects never expire.


    Nice, but you need some locking to make it threadsafe.

    Threadsafety:
    private final Object lockObject=new Object();

    and this around the get/puts.

    synchronized (lockObject) {
    };

    Hot objects:

    Don't do this:
    if (now.longValue() - urlItemCacheTtl.get(url).longValue() < TTL ) {
    urlItemCacheTtl.put(url, now);

    that will cause frequent objects to never be re-requested.

    ReplyDelete
  2. Thanks Allen for the comment. I'll address the issues:

    1) Hot items: you are absolutely right. Stupid mistake on my side. fixing..

    2) Thread safety: I am using synchronizedMap so the collection itself is theadsafe. You are right in that that it's possible to be inserting two objects almost together, however I prefer that risk (I don't really care if that happens since key/value pairs are always the same) as i wanted to keep the code simple and fast. YMMV.

    ReplyDelete
  3. Small point, but should likely use System.nanoTime() offsets to calculate TTL's to avoid odd behavour around daylight-savings events.

    ReplyDelete

[Due to much spam, comments are now moderated and will be posted after review]