Cache Helper Class for Java

The Freeze.Cache class allows you to efficiently maintain a cache that is backed by secondary storage, such as a Berkeley DB database, without holding a lock on the entire cache while values are being loaded from the database. If you want to create evictors for servants that store their state in a database, the Cache class can simplify your evictor implementation considerably.

The Freeze evictors are implemented using Freeze.Cache, but Freeze.Cache is not tied to Berkeley DB - you can use this template class for any evictor implementation.

The Cache class has the following interface:

Java
package Freeze;

public class Cache
{
    public Cache(Store store);

    public Object pin(Object key);
    public Object pin(Object key, Object o);
    public Object unpin(Object key);

    public Object putIfAbsent(Object key, Object newObj);
    public Object getIfPinned(Object key);

    public void clear();
    public int size();
}

Internally, a Cache maintains a map of name-value pairs. The implementation of Cache takes care of maintaining the map; in particular, it ensures that concurrent lookups by callers are possible without blocking even if some of the callers are currently loading values from the backing store. In turn, this is useful for evictor implementations. The Cache class does not limit the number of entries in the cache — it is the job of the evictor implementation to limit the map size by calling unpin on elements of the map that it wants to evict.

The Cache class works in conjunction with a Store interface for which you must provide an implementation. The Store interface is trivial:

Java
package Freeze;

public interface Store
{
    Object load(Object key);
}

You must implement the load method in a class that you derive from Store. The Cache implementation calls load when it needs to retrieve the value for the passed key from the backing store. If load cannot locate a record for the given key because no such record exists, it must return null. If load fails for some other reason, it can throw an exception derived from java.lang.RuntimeException, which is propagated back to the application code.

The public member functions of Cache behave as follows:

Cache(Store s)

The constructor initializes the cache with your implementation of the Store interface.

Object pin(Object key, Object val)

To add a key-value pair to the cache, your evictor can call pin. The return value is null if the key and value were added; otherwise, if the map already contains an entry with the given key, the entry is unchanged and pin returns the original value for that key.

This version of pin does not call load to retrieve the entry from backing store if it is not yet in the cache. This is useful when you add a newly-created object to the cache.

Object pin(Object key)

This version of pin returns the value stored in the cache for the given key if the cache already contains an entry for that key. If no entry with the given key is in the cache, pin calls load to retrieve the corresponding value (if any) from the backing store. pin returns the value returned by load, that is, the value if load could retrieve it, null if load could not retrieve it, or any exception thrown by load.

Object unpin(Object key)

unpin removes the entry for the given key from the cache. If the cache contained an entry for the key, the return value is the value for that key; otherwise, the return value is null.

Object putIfAbsent(Object key, Object val)

This function adds a key-value pair to the cache. If the cache already contains an entry for the given key, putIfAbsent returns the original value for that key. If no entry with the given key is in the cache, putIfAbsent calls load to retrieve the corresponding entry (if any) from the backing store and returns the value returned by load.

If the cache does not contain an entry for the given key and load does not retrieve a value for the key, the method adds the new entry and returns null.

Object getIfPinned(Object key)

This function returns the value stored for the given key. If an entry for the given key is in the map, the function returns the corresponding value; otherwise, the function returns null. getIfPinned does not call load.

void clear()

This function removes all entries in the map.

int size()

This function returns the number of entries in the map.