Cache slows down with too many cache keys

View: New views
1 Messages — Rating Filter:   Alert me  

Cache slows down with too many cache keys

by Lance Java :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

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);
         }
      }
   }
}