Author: tv
Date: Sun Aug 7 09:49:54 2016
New Revision: 1755432
URL: http://svn.apache.org/viewvc?rev=1755432&view=rev
Log:
JCS-165: BlockDiskCache partial remove / group remove doesn't work
Modified:
commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCache.java
commons/proper/jcs/trunk/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheUnitTestAbstract.java
commons/proper/jcs/trunk/src/changes/changes.xml
Modified:
commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCache.java
URL:
http://svn.apache.org/viewvc/commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCache.java?rev=1755432&r1=1755431&r2=1755432&view=diff
==============================================================================
---
commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCache.java
(original)
+++
commons/proper/jcs/trunk/commons-jcs-core/src/main/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCache.java
Sun Aug 7 09:49:54 2016
@@ -26,6 +26,8 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
@@ -40,6 +42,7 @@ import org.apache.commons.jcs.engine.beh
import org.apache.commons.jcs.engine.behavior.IElementSerializer;
import org.apache.commons.jcs.engine.behavior.IRequireScheduler;
import org.apache.commons.jcs.engine.control.group.GroupAttrName;
+import org.apache.commons.jcs.engine.control.group.GroupId;
import org.apache.commons.jcs.engine.stats.StatElement;
import org.apache.commons.jcs.engine.stats.Stats;
import org.apache.commons.jcs.engine.stats.behavior.IStatElement;
@@ -447,60 +450,17 @@ public class BlockDiskCache<K, V>
try
{
- if ( key instanceof String && key.toString().endsWith(
CacheConstants.NAME_COMPONENT_DELIMITER ) )
+ if (key instanceof String &&
key.toString().endsWith(CacheConstants.NAME_COMPONENT_DELIMITER))
{
- // remove all keys of the same name group.
- Iterator<Map.Entry<K, int[]>> iter =
this.keyStore.entrySet().iterator();
-
- while ( iter.hasNext() )
- {
- Map.Entry<K, int[]> entry = iter.next();
- K k = entry.getKey();
-
- if ( k instanceof String && k.toString().startsWith(
key.toString() ) )
- {
- int[] ded = entry.getValue();
- this.dataFile.freeBlocks( ded );
- iter.remove();
- removed = true;
- // TODO this needs to update the remove count
separately
- }
- }
+ removed = performPartialKeyRemoval((String) key);
}
- else if ( key instanceof GroupAttrName &&
((GroupAttrName<?>)key).attrName == null )
+ else if (key instanceof GroupAttrName && ((GroupAttrName<?>)
key).attrName == null)
{
- // remove all keys of the same name hierarchy.
- Iterator<Map.Entry<K, int[]>> iter =
this.keyStore.entrySet().iterator();
- while ( iter.hasNext() )
- {
- Map.Entry<K, int[]> entry = iter.next();
- K k = entry.getKey();
-
- if ( k instanceof GroupAttrName &&
-
((GroupAttrName<?>)k).groupId.equals(((GroupAttrName<?>)key).groupId))
- {
- int[] ded = entry.getValue();
- this.dataFile.freeBlocks( ded );
- iter.remove();
- removed = true;
- }
- }
+ removed = performGroupRemoval(((GroupAttrName<?>)
key).groupId);
}
else
{
- // remove single item.
- int[] ded = this.keyStore.remove( key );
- removed = ded != null;
- if ( removed )
- {
- this.dataFile.freeBlocks( ded );
- }
-
- if ( log.isDebugEnabled() )
- {
- log.debug( logCacheName + "Disk removal: Removed from key
hash, key [" + key + "] removed = "
- + removed );
- }
+ removed = performSingleKeyRemoval(key);
}
}
catch ( Exception e )
@@ -522,6 +482,102 @@ public class BlockDiskCache<K, V>
}
/**
+ * Remove all elements from the group. This does not use the iterator to
remove. It builds a
+ * list of group elements and then removes them one by one.
+ * <p>
+ * This operates under a lock obtained in doRemove().
+ * <p>
+ *
+ * @param key
+ * @return true if an element was removed
+ */
+ private boolean performGroupRemoval(GroupId key)
+ {
+ boolean removed = false;
+
+ // remove all keys of the same name group.
+ List<K> itemsToRemove = new LinkedList<K>();
+
+ // remove all keys of the same name hierarchy.
+ for (K k : keyStore.keySet())
+ {
+ if (k instanceof GroupAttrName && ((GroupAttrName<?>)
k).groupId.equals(key))
+ {
+ itemsToRemove.add(k);
+ }
+ }
+
+ // remove matches.
+ for (K fullKey : itemsToRemove)
+ {
+ // Don't add to recycle bin here
+ // https://issues.apache.org/jira/browse/JCS-67
+ performSingleKeyRemoval(fullKey);
+ removed = true;
+ // TODO this needs to update the remove count separately
+ }
+
+ return removed;
+ }
+
+ /**
+ * Iterates over the keyset. Builds a list of matches. Removes all the
keys in the list. Does
+ * not remove via the iterator, since the map impl may not support it.
+ * <p>
+ * This operates under a lock obtained in doRemove().
+ * <p>
+ *
+ * @param key
+ * @return true if there was a match
+ */
+ private boolean performPartialKeyRemoval(String key)
+ {
+ boolean removed = false;
+
+ // remove all keys of the same name hierarchy.
+ List<K> itemsToRemove = new LinkedList<K>();
+
+ for (K k : keyStore.keySet())
+ {
+ if (k instanceof String && k.toString().startsWith(key))
+ {
+ itemsToRemove.add(k);
+ }
+ }
+
+ // remove matches.
+ for (K fullKey : itemsToRemove)
+ {
+ // Don't add to recycle bin here
+ // https://issues.apache.org/jira/browse/JCS-67
+ performSingleKeyRemoval(fullKey);
+ removed = true;
+ // TODO this needs to update the remove count separately
+ }
+
+ return removed;
+ }
+
+
+ private boolean performSingleKeyRemoval(K key) {
+ boolean removed;
+ // remove single item.
+ int[] ded = this.keyStore.remove( key );
+ removed = ded != null;
+ if ( removed )
+ {
+ this.dataFile.freeBlocks( ded );
+ }
+
+ if ( log.isDebugEnabled() )
+ {
+ log.debug( logCacheName + "Disk removal: Removed from key
hash, key [" + key + "] removed = "
+ + removed );
+ }
+ return removed;
+ }
+
+ /**
* Resets the keyfile, the disk file, and the memory key map.
* <p>
* @see org.apache.commons.jcs.auxiliary.disk.AbstractDiskCache#removeAll()
Modified:
commons/proper/jcs/trunk/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheUnitTestAbstract.java
URL:
http://svn.apache.org/viewvc/commons/proper/jcs/trunk/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheUnitTestAbstract.java?rev=1755432&r1=1755431&r2=1755432&view=diff
==============================================================================
---
commons/proper/jcs/trunk/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheUnitTestAbstract.java
(original)
+++
commons/proper/jcs/trunk/commons-jcs-core/src/test/java/org/apache/commons/jcs/auxiliary/disk/block/BlockDiskCacheUnitTestAbstract.java
Sun Aug 7 09:49:54 2016
@@ -20,13 +20,18 @@ package org.apache.commons.jcs.auxiliary
*/
import java.io.File;
+import java.io.IOException;
import java.io.Serializable;
import java.util.Map;
import junit.framework.TestCase;
import org.apache.commons.jcs.engine.CacheElement;
+import org.apache.commons.jcs.engine.ElementAttributes;
import org.apache.commons.jcs.engine.behavior.ICacheElement;
+import org.apache.commons.jcs.engine.behavior.IElementAttributes;
+import org.apache.commons.jcs.engine.control.group.GroupAttrName;
+import org.apache.commons.jcs.engine.control.group.GroupId;
import org.apache.commons.jcs.utils.serialization.StandardSerializer;
/** Unit tests for the Block Disk Cache */
@@ -327,8 +332,8 @@ public abstract class BlockDiskCacheUnit
oneLoadFromDisk();
}
}
-
- public void testAppendToDisk() throws Exception
+
+ public void testAppendToDisk() throws Exception
{
String cacheName = "testAppendToDisk";
BlockDiskCacheAttributes cattr = getCacheAttributes();
@@ -403,6 +408,155 @@ public abstract class BlockDiskCacheUnit
diskCache.dispose();
}
+ /**
+ * Add some items to the disk cache and then remove them one by one.
+ *
+ * @throws IOException
+ */
+ public void testRemoveItems() throws IOException
+ {
+ BlockDiskCacheAttributes cattr = getCacheAttributes();
+ cattr.setCacheName("testRemoveItems");
+ cattr.setMaxKeySize(100);
+ cattr.setDiskPath("target/test-sandbox/BlockDiskCacheUnitTest");
+ BlockDiskCache<String, String> disk = new BlockDiskCache<String,
String>(cattr);
+
+ disk.processRemoveAll();
+
+ int cnt = 25;
+ for (int i = 0; i < cnt; i++)
+ {
+ IElementAttributes eAttr = new ElementAttributes();
+ eAttr.setIsSpool(true);
+ ICacheElement<String, String> element = new CacheElement<String,
String>("testRemoveItems", "key:" + i, "data:" + i);
+ element.setElementAttributes(eAttr);
+ disk.processUpdate(element);
+ }
+
+ // remove each
+ for (int i = 0; i < cnt; i++)
+ {
+ disk.remove("key:" + i);
+ ICacheElement<String, String> element = disk.processGet("key:" +
i);
+ assertNull("Should not have received an element.", element);
+ }
+ }
+
+ /**
+ * Add some items to the disk cache and then remove them one by one.
+ * <p>
+ *
+ * @throws IOException
+ */
+ public void testRemove_PartialKey() throws IOException
+ {
+ BlockDiskCacheAttributes cattr = getCacheAttributes();
+ cattr.setCacheName("testRemove_PartialKey");
+ cattr.setMaxKeySize(100);
+ cattr.setDiskPath("target/test-sandbox/BlockDiskCacheUnitTest");
+ BlockDiskCache<String, String> disk = new BlockDiskCache<String,
String>(cattr);
+
+ disk.processRemoveAll();
+
+ int cnt = 25;
+ for (int i = 0; i < cnt; i++)
+ {
+ IElementAttributes eAttr = new ElementAttributes();
+ eAttr.setIsSpool(true);
+ ICacheElement<String, String> element = new CacheElement<String,
String>("testRemove_PartialKey", i + ":key", "data:"
+ + i);
+ element.setElementAttributes(eAttr);
+ disk.processUpdate(element);
+ }
+
+ // verify each
+ for (int i = 0; i < cnt; i++)
+ {
+ ICacheElement<String, String> element = disk.processGet(i +
":key");
+ assertNotNull("Shoulds have received an element.", element);
+ }
+
+ // remove each
+ for (int i = 0; i < cnt; i++)
+ {
+ disk.remove(i + ":");
+ ICacheElement<String, String> element = disk.processGet(i +
":key");
+ assertNull("Should not have received an element.", element);
+ }
+ }
+
+
+ /**
+ * Verify that group members are removed if we call remove with a group.
+ *
+ * @throws IOException
+ */
+ public void testRemove_Group() throws IOException
+ {
+ // SETUP
+ BlockDiskCacheAttributes cattr = getCacheAttributes();
+ cattr.setCacheName("testRemove_Group");
+ cattr.setMaxKeySize(100);
+ cattr.setDiskPath("target/test-sandbox/BlockDiskCacheUnitTest");
+ BlockDiskCache<GroupAttrName<String>, String> disk = new
BlockDiskCache<GroupAttrName<String>, String>(cattr);
+
+ disk.processRemoveAll();
+
+ String cacheName = "testRemove_Group_Region";
+ String groupName = "testRemove_Group";
+
+ int cnt = 25;
+ for (int i = 0; i < cnt; i++)
+ {
+ GroupAttrName<String> groupAttrName = getGroupAttrName(cacheName,
groupName, i + ":key");
+ CacheElement<GroupAttrName<String>, String> element = new
CacheElement<GroupAttrName<String>, String>(cacheName,
+ groupAttrName, "data:" + i);
+
+ IElementAttributes eAttr = new ElementAttributes();
+ eAttr.setIsSpool(true);
+ element.setElementAttributes(eAttr);
+
+ disk.processUpdate(element);
+ }
+
+ // verify each
+ for (int i = 0; i < cnt; i++)
+ {
+ GroupAttrName<String> groupAttrName = getGroupAttrName(cacheName,
groupName, i + ":key");
+ ICacheElement<GroupAttrName<String>, String> element =
disk.processGet(groupAttrName);
+ assertNotNull("Should have received an element.", element);
+ }
+
+ // DO WORK
+ // remove the group
+ disk.remove(getGroupAttrName(cacheName, groupName, null));
+
+ for (int i = 0; i < cnt; i++)
+ {
+ GroupAttrName<String> groupAttrName = getGroupAttrName(cacheName,
groupName, i + ":key");
+ ICacheElement<GroupAttrName<String>, String> element =
disk.processGet(groupAttrName);
+
+ // VERIFY
+ assertNull("Should not have received an element.", element);
+ }
+
+ }
+
+ /**
+ * Internal method used for group functionality.
+ * <p>
+ *
+ * @param cacheName
+ * @param group
+ * @param name
+ * @return GroupAttrName
+ */
+ private GroupAttrName<String> getGroupAttrName(String cacheName, String
group, String name)
+ {
+ GroupId gid = new GroupId(cacheName, group);
+ return new GroupAttrName<String>(gid, name);
+ }
+
/** Holder for a string and byte array. */
static class X implements Serializable
{
Modified: commons/proper/jcs/trunk/src/changes/changes.xml
URL:
http://svn.apache.org/viewvc/commons/proper/jcs/trunk/src/changes/changes.xml?rev=1755432&r1=1755431&r2=1755432&view=diff
==============================================================================
--- commons/proper/jcs/trunk/src/changes/changes.xml (original)
+++ commons/proper/jcs/trunk/src/changes/changes.xml Sun Aug 7 09:49:54 2016
@@ -20,6 +20,9 @@
</properties>
<body>
<release version="2.0" date="unreleased" description="JDK 1.6 based
major release">
+ <action issue="JCS-165" dev="tv" type="fix" due-to="Wiktor
Niesiobedzki">
+ Fix: BlockDiskCache partial remove / group remove doesn't work
+ </action>
<action issue="JCS-156" dev="sebb" type="fix" due-to="Ryan Fong">
BlockDiskCache is limited to 2GB
</action>