Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How handle cache misses: NotFoundException, contains() or `if (null == result)`?

Maybe this is slightly academic, but if I implement a cache for speeding up an application, how should I best handle cache misses? (In my case, the language would be Java, but maybe the answer can be more general.)

Throw an exception:

ResultType res;
try {
    res = Cache.resLookup(someKey);
} catch (NotFoundException e) {
    res = Cache.resInsert(someKey, SlowDataSource.resLookup(someKey));
}

Ask before fetch:

ResultType res;
if (Cache.contains(someKey) {
    res = Cache.resLookup(someKey);
} else {
    res = Cache.resInsert(someKey, SlowDataSource.resLookup(someKey));
}

Return null:

ResultType res;
res = Cache.resLookup(someKey);
if (null == res) {
    res = Cache.resInsert(someKey, SlowDataSource.resLookup(someKey));
}

Throwing an Exception seems wrong, after all, this isn't an error. Letting the Cache do a look up for contains() and then again to retrieve the data seems wasteful, especially as this would occur every time. And checking for null of course requires that null can never be a valid result...

like image 965
Hanno Fietz Avatar asked Dec 14 '22 03:12

Hanno Fietz


1 Answers

The first is excessive I think and not a good use for exceptions. Do you have an expectation that there will be a cache hit? A cache miss is a fairly normal occurrence I would think and thus an exception becomes simply flow control. Not good imho.

The second is a race condition. There is a time delay between checking on the existence of the cache entry and querying it. That could lead to all sorts of trouble.

Returning null is probably appropriate in the general sense but that comes with some qualifications.

Firstly, what type of cache is it? Ideally you'd be talking to a read-through cache in which case if it's not in the cache it'll simply get it from the source, which is not the style of code you've written there.

Secondly, the get then insert is another race condition. Look at the interface for ConcurrentHashMap for a good general way of dealing with this kind of thing. Most notably, the putIfAbsent() call is an atomic operation that does the equivalent of you're third call.

like image 90
cletus Avatar answered Dec 15 '22 16:12

cletus