[Opensymphony-oscache] Cache slows down with too many cache keys
Lance Java <[email protected]> Mon, 12 Oct 2009 15:27:16 +0100
| Newsgroups | gmane.comp.java.open-symphony.os-cache |
|---|---|
| Message-ID | <[email protected]> |
--00151744790efe02300475bdb965
Content-Type: text/plain; charset=ISO-8859-1
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);
}
}
}
}
--00151744790efe02300475bdb965
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
<div>I have debugged through <font size=3D"2">com.opensymphony.oscache.plug=
ins.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 seria=
lized to a file. This is not scalable and causes an increasing lag as the n=
umber of cache entries in a group increases.</font></div>
<div>=A0</div>
<div>I have implemented a fix that stores the cache keys in a ConcurrentHas=
hMap and have a separate thread that periodically saves the group keys to d=
isk. I also use spring's lifecycle to save the group keys to disk on se=
rver shutdown.</div>
<div>=A0</div>
<div>Here's my solution=A0for anyone experiencing the same issue</div>
<div>=A0</div>
<div>Cheers,</div>
<div>Lance.</div>
<div>=A0</div>
<div>=A0</div>
<div>Config:</div>
<div>cache.persistence.class=3Dfoo.bar.MyHashDiskPersistenceListener</div>
<div>=A0</div>
<div>Java:=A0</div>
<div>/**<br>=A0* An extension of the default disk persistence where the gro=
up meta data is stored in<br>=A0* memory rather than to disk. The oscache i=
mplementation of this class is constantly<br>=A0* reading and writing the g=
roup meta data to disk which slows the application down<br>
=A0* when the number of keys in a group increases.<br>=A0* @author semmlan<=
br>=A0*/<br>public class MyHashDiskPersistenceListener extends HashDiskPers=
istenceListener {<br>=A0=A0 private static final Logger log =3D Logger.getL=
ogger(MyHashDiskPersistenceListener.class);<br>
=A0=A0 <br>=A0=A0 /**<br>=A0=A0=A0 * Groups are stored in a static variable=
because oscache uses it's own factory and the<br>=A0=A0=A0 * persisten=
ce listener instance that is used by oscache can not be referenced<br>=A0=
=A0=A0 */<br>=A0=A0 private static ConcurrentHashMap<String, Set<Stri=
ng>> groups =3D new ConcurrentHashMap<String, Set<String>>=
;();=A0 <br>
=A0=A0 <br>=A0=A0 /**<br>=A0=A0=A0 * Verify if a group exists in the cache<=
br>=A0=A0=A0 *<br>=A0=A0=A0 * @param group The group name to check<br>=A0=
=A0=A0 * @return True if it exists<br>=A0=A0=A0 * @throws CachePersistenceE=
xception<br>=A0=A0=A0 */<br>=A0=A0 @Override<br>
=A0=A0 public boolean isGroupStored(String groupName) throws CachePersisten=
ceException {<br>=A0=A0=A0=A0=A0 return groups.containsKey(groupName);<br>=
=A0=A0 }</div>
<div>=A0=A0 /**<br>=A0=A0=A0 * Deletes an entire group from the cache.<br>=
=A0=A0=A0 *<br>=A0=A0=A0 * @param groupName The name of the group to delete=
<br>=A0=A0=A0 * @throws CachePersistenceException<br>=A0=A0=A0 */<br>=A0=A0=
@Override<br>=A0=A0 public void removeGroup(String groupName) throws Cache=
PersistenceException {<br>
=A0=A0=A0=A0=A0 groups.remove(groupName);<br>=A0=A0 }</div>
<div>=A0=A0 /**<br>=A0=A0=A0 * Retrieves a group from the cache, or <cod=
e>null</code> if the group<br>=A0=A0=A0 * file could not be found.=
<br>=A0=A0=A0 *<br>=A0=A0=A0 * @param groupName The name of the group to re=
trieve.<br>=A0=A0=A0 * @return A <code>Set</code> containing ke=
ys of all of the cache<br>
=A0=A0=A0 * entries that belong to this group.<br>=A0=A0=A0 * @throws Cache=
PersistenceException<br>=A0=A0=A0 */<br>=A0=A0 @Override<br>=A0=A0 public S=
et retrieveGroup(String groupName) throws CachePersistenceException {<br>=
=A0=A0=A0=A0=A0 return groups.get(groupName);<br>
=A0=A0 }</div>
<div>=A0=A0 /**<br>=A0=A0=A0 * Stores a group in the persistent cache. This=
will overwrite any existing<br>=A0=A0=A0 * group with the same name<br>=A0=
=A0=A0 */<br>=A0=A0 @Override<br>=A0=A0 public void storeGroup(String group=
Name, Set group) throws CachePersistenceException {<br>
=A0=A0=A0=A0=A0 groups.put(groupName, group);<br>=A0=A0 }<br>=A0=A0=A0<br>=
=A0=A0 /**</div>
<div>=A0=A0=A0 * This method should be called periodically by a thread.</di=
v>
<div>=A0=A0=A0 * It should also be called on server shutdown</div>
<div>=A0=A0=A0 */</div>
<div>=A0=A0 public void backupGroups() {<br>=A0=A0=A0=A0=A0 for (Map.Entry&=
lt;String, Set<String>> entry : groups.entrySet()) {<br>=A0=A0=A0=
=A0=A0=A0=A0=A0 try {<br>=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 super.storeGroup=
(entry.getKey(), entry.getValue());<br>=A0=A0=A0=A0=A0=A0=A0=A0 } catch (Ca=
chePersistenceException e) {<br>
=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 log.error("Error storing group meta =
data for " + entry.getKey(), e);<br>=A0=A0=A0=A0=A0=A0=A0=A0 }<br>=A0=
=A0=A0=A0=A0 }<br>=A0=A0 }<br>}<br></div>
--00151744790efe02300475bdb965--