I have debugged through com.opensymphony.oscache.plugins.diskpersistence.HashDiskPersistenceListener and have found that the Set of cache keys for a group is stored to disk. Every time that a get or put occurs, the entire set of keys for the cache group is deserialized or serialized to a file. This is not scalable and causes an increasing lag as the number of cache entries in a group increases.
I have implemented a fix that stores the cache keys in a ConcurrentHashMap and have a separate thread that periodically saves the group keys to disk. I also use spring's lifecycle to save the group keys to disk on server shutdown.
Here's my solution for anyone experiencing the same issue
Cheers,
Lance.
Config:
cache.persistence.class=foo.bar.MyHashDiskPersistenceListener
Java:
/**
* An extension of the default disk persistence where the group meta data is stored in
* memory rather than to disk. The oscache implementation of this class is constantly
* reading and writing the group meta data to disk which slows the application down
* when the number of keys in a group increases.
* @author semmlan
*/
public class MyHashDiskPersistenceListener extends HashDiskPersistenceListener {
private static final Logger log = Logger.getLogger(MyHashDiskPersistenceListener.class);
/**
* Groups are stored in a static variable because oscache uses it's own factory and the
* persistence listener instance that is used by oscache can not be referenced
*/
private static ConcurrentHashMap<String, Set<String>> groups = new ConcurrentHashMap<String, Set<String>>();
/**
* Verify if a group exists in the cache
*
* @param group The group name to check
* @return True if it exists
* @throws CachePersistenceException
*/
@Override
public boolean isGroupStored(String groupName) throws CachePersistenceException {
return groups.containsKey(groupName);
}
/**
* Deletes an entire group from the cache.
*
* @param groupName The name of the group to delete
* @throws CachePersistenceException
*/
@Override
public void removeGroup(String groupName) throws CachePersistenceException {
groups.remove(groupName);
}
/**
* Retrieves a group from the cache, or <code>null</code> if the group
* file could not be found.
*
* @param groupName The name of the group to retrieve.
* @return A <code>Set</code> containing keys of all of the cache
* entries that belong to this group.
* @throws CachePersistenceException
*/
@Override
public Set retrieveGroup(String groupName) throws CachePersistenceException {
return groups.get(groupName);
}
/**
* Stores a group in the persistent cache. This will overwrite any existing
* group with the same name
*/
@Override
public void storeGroup(String groupName, Set group) throws CachePersistenceException {
groups.put(groupName, group);
}
/**
* This method should be called periodically by a thread.
* It should also be called on server shutdown
*/
public void backupGroups() {
for (Map.Entry<String, Set<String>> entry : groups.entrySet()) {
try {
super.storeGroup(entry.getKey(), entry.getValue());
} catch (CachePersistenceException e) {
log.error("Error storing group meta data for " + entry.getKey(), e);
}
}
}
}