Author: asmuts
Date: Sun Aug 27 11:20:07 2006
New Revision: 437431
URL: http://svn.apache.org/viewvc?rev=437431&view=rev
Log:
I made the block disk cache far more memory efficient.
It now only stores the block addresses in memory. The elememt descriptor is
used only for key persistence.
I made some initial optimizations on the serialization of the descriptor.
I added a unit test for the store.
Added:
jakarta/jcs/trunk/src/test/org/apache/jcs/auxiliary/disk/block/BlockDiskCacheKeyStoreUnitTest.java
Modified:
jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/block/BlockDiskCache.java
jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/block/BlockDiskElementDescriptor.java
jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/block/BlockDiskKeyStore.java
jakarta/jcs/trunk/src/java/org/apache/jcs/utils/struct/LRUMap.java
jakarta/jcs/trunk/src/test/org/apache/jcs/auxiliary/disk/block/BlockDiskElementDescriptorUnitTest.java
jakarta/jcs/trunk/src/test/org/apache/jcs/auxiliary/disk/block/BlockDiskUnitTest.java
Modified:
jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/block/BlockDiskCache.java
URL:
http://svn.apache.org/viewvc/jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/block/BlockDiskCache.java?rev=437431&r1=437430&r2=437431&view=diff
==============================================================================
---
jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/block/BlockDiskCache.java
(original)
+++
jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/block/BlockDiskCache.java
Sun Aug 27 11:20:07 2006
@@ -205,10 +205,10 @@
storageLock.readLock().acquire();
try
{
- BlockDiskElementDescriptor ded = this.keyStore.get( key );
+ int[] ded = this.keyStore.get( key );
if ( ded != null )
{
- object = (ICacheElement) this.dataFile.read(
ded.getBlocks() );
+ object = (ICacheElement) this.dataFile.read( ded );
}
}
finally
@@ -253,7 +253,7 @@
return;
}
- BlockDiskElementDescriptor old = null;
+ int[] old = null;
try
{
// make sure this only locks for one particular cache region
@@ -264,16 +264,12 @@
if ( old != null )
{
- this.dataFile.freeBlocks( old.getBlocks() );
+ this.dataFile.freeBlocks( old );
}
int[] blocks = this.dataFile.write( element );
- BlockDiskElementDescriptor newElement = new
BlockDiskElementDescriptor();
- newElement.setKey( element.getKey() );
- newElement.setBlocks( blocks );
-
- this.keyStore.put( element.getKey(), newElement );
+ this.keyStore.put( element.getKey(), blocks );
}
finally
{
@@ -333,8 +329,8 @@
if ( k instanceof String && k.toString().startsWith(
key.toString() ) )
{
- BlockDiskElementDescriptor ded = this.keyStore.get(
key );
- this.dataFile.freeBlocks( ded.getBlocks() );
+ int[] ded = this.keyStore.get( key );
+ this.dataFile.freeBlocks( ded );
iter.remove();
removed = true;
// TODO this needs to update the rmove count separately
@@ -352,8 +348,8 @@
if ( k instanceof GroupAttrName && ( (GroupAttrName) k
).groupId.equals( key ) )
{
- BlockDiskElementDescriptor ded = this.keyStore.get(
key );
- this.dataFile.freeBlocks( ded.getBlocks() );
+ int[] ded = this.keyStore.get( key );
+ this.dataFile.freeBlocks( ded );
iter.remove();
removed = true;
}
@@ -362,11 +358,11 @@
else
{
// remove single item.
- BlockDiskElementDescriptor ded = this.keyStore.remove( key );
+ int[] ded = this.keyStore.remove( key );
removed = ( ded != null );
if ( ded != null )
{
- this.dataFile.freeBlocks( ded.getBlocks() );
+ this.dataFile.freeBlocks( ded );
}
if ( log.isDebugEnabled() )
@@ -450,9 +446,10 @@
/**
* Internal method that handles the disposal.
- * @throws InterruptedException
+ * @throws InterruptedException
*/
- private void disposeInternal() throws InterruptedException
+ private void disposeInternal()
+ throws InterruptedException
{
if ( !alive )
{
Modified:
jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/block/BlockDiskElementDescriptor.java
URL:
http://svn.apache.org/viewvc/jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/block/BlockDiskElementDescriptor.java?rev=437431&r1=437430&r2=437431&view=diff
==============================================================================
---
jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/block/BlockDiskElementDescriptor.java
(original)
+++
jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/block/BlockDiskElementDescriptor.java
Sun Aug 27 11:20:07 2006
@@ -1,27 +1,29 @@
package org.apache.jcs.auxiliary.disk.block;
/*
- * Copyright 2001-2004 The Apache Software Foundation. Licensed under the
Apache
- * License, Version 2.0 (the "License") you may not use this file except in
- * compliance with the License. You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
- * or agreed to in writing, software distributed under the License is
- * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the specific language
- * governing permissions and limitations under the License.
+ * Copyright 2001-2004 The Apache Software Foundation. Licensed under the
Apache License, Version
+ * 2.0 (the "License") you may not use this file except in compliance with the
License. You may
+ * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by
+ * applicable law or agreed to in writing, software distributed under the
License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or implied. See
+ * the License for the specific language governing permissions and limitations
under the License.
*/
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
import java.io.Serializable;
/**
- * This represents an element on disk. It must be kept small since these are
stored in memory. We
- * store the key and then offset in memory. That's all. We don't need the
length, since we don't
- * have a receyle bin.
+ * This represents an element on disk. This is used when we persist the keys.
We only store the
+ * block addresses in memory. We don't need the length here, since all the
blocks are the same size
+ * receyle bin.
* <p>
* @author Aaron Smuts
*/
public class BlockDiskElementDescriptor
- implements Serializable
+ implements Serializable, Externalizable
{
private static final long serialVersionUID = -1400659301208101411L;
@@ -81,5 +83,31 @@
}
buf.append( "]" );
return buf.toString();
+ }
+
+ /**
+ * Saves on reflection.
+ * <p>
+ * (non-Javadoc)
+ * @see java.io.Externalizable#readExternal(java.io.ObjectInput)
+ */
+ public void readExternal( ObjectInput input )
+ throws IOException, ClassNotFoundException
+ {
+ this.key = (Serializable) input.readObject();
+ this.blocks = (int[]) input.readObject();
+ }
+
+ /**
+ * Saves on reflection.
+ * <p>
+ * (non-Javadoc)
+ * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
+ */
+ public void writeExternal( ObjectOutput output )
+ throws IOException
+ {
+ output.writeObject( this.key );
+ output.writeObject( this.blocks );
}
}
Modified:
jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/block/BlockDiskKeyStore.java
URL:
http://svn.apache.org/viewvc/jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/block/BlockDiskKeyStore.java?rev=437431&r1=437430&r2=437431&view=diff
==============================================================================
---
jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/block/BlockDiskKeyStore.java
(original)
+++
jakarta/jcs/trunk/src/java/org/apache/jcs/auxiliary/disk/block/BlockDiskKeyStore.java
Sun Aug 27 11:20:07 2006
@@ -11,12 +11,15 @@
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
+import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
+import java.io.Serializable;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.Map;
import java.util.Set;
@@ -46,6 +49,7 @@
private int maxKeySize;
+ // we need this so we can communicate free blocks to the data store when
keys fall off the LRU
private BlockDiskCache blockDiskCache;
private File rootDirectory;
@@ -72,11 +76,16 @@
if ( log.isInfoEnabled() )
{
- log.info( logCacheName + "Cache file root directory: " +
rootDirName );
+ log.info( logCacheName + "Cache file root directory [" +
rootDirName + "]" );
}
this.keyFile = new File( rootDirectory, fileName + ".key" );
+ if ( log.isInfoEnabled() )
+ {
+ log.info( logCacheName + "Key File [" +
this.keyFile.getAbsolutePath() + "]" );
+ }
+
if ( keyFile.length() > 0 )
{
loadKeys();
@@ -89,7 +98,8 @@
}
/**
- * Saves key file to disk. This converts the LRUMap to a HashMap for
deserialzation.
+ * Saves key file to disk. This gets the LRUMap entry set and write the
entries out one by one
+ * after putting them in a wrapper.
*/
protected void saveKeys()
{
@@ -99,34 +109,40 @@
int numKeys = keyHash.size();
if ( log.isInfoEnabled() )
{
- log.info( logCacheName + "Saving keys to: " + fileName + ",
key count: " + numKeys );
+ log.info( logCacheName + "Saving keys to [" +
this.keyFile.getAbsolutePath() + "], key count ["
+ + numKeys + "]" );
}
keyFile.delete();
- HashMap keys = new HashMap();
- keys.putAll( keyHash );
-
- if ( keys.size() > 0 )
+ keyFile = new File( rootDirectory, fileName + ".key" );
+ FileOutputStream fos = new FileOutputStream( keyFile );
+ BufferedOutputStream bos = new BufferedOutputStream( fos, 1024 );
+ ObjectOutputStream oos = new ObjectOutputStream( bos );
+ try
{
- FileOutputStream fos = new FileOutputStream( keyFile );
- BufferedOutputStream bos = new BufferedOutputStream( fos );
- ObjectOutputStream oos = new ObjectOutputStream( bos );
- try
- {
- oos.writeObject( keys );
- oos.flush();
- }
- finally
+ // don't need to synchronize, since the underlying collection
makes a copy
+ Iterator keyIt = keyHash.entrySet().iterator();
+ while ( keyIt.hasNext() )
{
- oos.close();
+ Map.Entry entry = (Map.Entry) keyIt.next();
+ BlockDiskElementDescriptor descriptor = new
BlockDiskElementDescriptor();
+ descriptor.setKey( (Serializable) entry.getKey() );
+ descriptor.setBlocks( (int[]) entry.getValue() );
+ // stream these out in the loop.
+ oos.writeObject( descriptor );
}
}
+ finally
+ {
+ oos.flush();
+ oos.close();
+ }
if ( log.isInfoEnabled() )
{
log.info( logCacheName + "Finished saving keys. It took " +
timer.getElapsedTimeString() + " to store "
- + numKeys + " keys." );
+ + numKeys + " keys. Key file length [" + keyFile.length()
+ "]" );
}
}
catch ( Exception e )
@@ -149,6 +165,14 @@
}
/**
+ * This is mainly used for testing. It leave the disk in tact, and just
clears memory.
+ */
+ protected void clearMemoryMap()
+ {
+ this.keyHash.clear();
+ }
+
+ /**
* Create the map for keys that contain the index position on disk.
*/
private void initKeyMap()
@@ -175,17 +199,17 @@
}
/**
- * Loads the keys from the .key file. The keys are stored in a HashMap on
disk. This is
- * converted into a LRUMap.
+ * Loads the keys from the .key file. The keys are stored individually on
disk. They are added
+ * one by one to an LRUMap..
* <p>
* @throws InterruptedException
*/
protected void loadKeys()
throws InterruptedException
{
- if ( log.isDebugEnabled() )
+ if ( log.isInfoEnabled() )
{
- log.debug( logCacheName + "Loading keys for " + keyFile.toString()
);
+ log.info( logCacheName + "Loading keys for " + keyFile.toString()
);
}
try
@@ -193,21 +217,32 @@
// create a key map to use.
initKeyMap();
- HashMap keys = null;
+ HashMap keys = new HashMap();
FileInputStream fis = new FileInputStream( keyFile );
BufferedInputStream bis = new BufferedInputStream( fis );
ObjectInputStream ois = new ObjectInputStream( bis );
try
{
- keys = (HashMap) ois.readObject();// 0, keyFile.length() );
+ while ( true )
+ {
+ BlockDiskElementDescriptor descriptor =
(BlockDiskElementDescriptor) ois.readObject();
+ if ( descriptor != null )
+ {
+ keys.put( descriptor.getKey(), descriptor.getBlocks()
);
+ }
+ }
+ }
+ catch ( EOFException eof )
+ {
+ // nothing
}
finally
{
ois.close();
}
- if ( keys != null )
+ if ( !keys.isEmpty() )
{
if ( log.isDebugEnabled() )
{
@@ -260,23 +295,23 @@
}
/**
- * gets the object fot he key.
+ * gets the object for the key.
* <p>
* @param key
* @return Object
*/
- public BlockDiskElementDescriptor get( Object key )
+ public int[] get( Object key )
{
- return (BlockDiskElementDescriptor) this.keyHash.get( key );
+ return (int[]) this.keyHash.get( key );
}
/**
- * Puts a BlockDiskElementDescriptor in the keyStore.
+ * Puts a int[] in the keyStore.
* <p>
* @param key
* @param value
*/
- public void put( Object key, BlockDiskElementDescriptor value )
+ public void put( Object key, int[] value )
{
this.keyHash.put( key, value );
}
@@ -287,9 +322,9 @@
* @param key
* @return BlockDiskElementDescriptor if it was present, else null
*/
- public BlockDiskElementDescriptor remove( Object key )
+ public int[] remove( Object key )
{
- return (BlockDiskElementDescriptor) this.keyHash.remove( key );
+ return (int[]) this.keyHash.remove( key );
}
/**
@@ -328,7 +363,7 @@
*/
protected void processRemovedLRU( Object key, Object value )
{
- blockDiskCache.freeBlocks( ( (BlockDiskElementDescriptor) value
).getBlocks() );
+ blockDiskCache.freeBlocks( (int[]) value );
if ( log.isDebugEnabled() )
{
log.debug( logCacheName + "Removing key: [" + key + "] from
key store." );
Modified: jakarta/jcs/trunk/src/java/org/apache/jcs/utils/struct/LRUMap.java
URL:
http://svn.apache.org/viewvc/jakarta/jcs/trunk/src/java/org/apache/jcs/utils/struct/LRUMap.java?rev=437431&r1=437430&r2=437431&view=diff
==============================================================================
--- jakarta/jcs/trunk/src/java/org/apache/jcs/utils/struct/LRUMap.java
(original)
+++ jakarta/jcs/trunk/src/java/org/apache/jcs/utils/struct/LRUMap.java Sun Aug
27 11:20:07 2006
@@ -170,10 +170,9 @@
* <p>
* @see java.util.Map#entrySet()
*/
- public Set entrySet()
+ public synchronized Set entrySet()
{
// todo, we should return a defensive copy
- // this is not thread safe.
Set entries = map.entrySet();
Set unWrapped = new HashSet();
@@ -182,9 +181,7 @@
while ( it.hasNext() )
{
Entry pre = (Entry) it.next();
-
Entry post = new LRUMapEntry( pre.getKey(), (
(LRUElementDescriptor) pre.getValue() ).getPayload() );
-
unWrapped.add( post );
}
Added:
jakarta/jcs/trunk/src/test/org/apache/jcs/auxiliary/disk/block/BlockDiskCacheKeyStoreUnitTest.java
URL:
http://svn.apache.org/viewvc/jakarta/jcs/trunk/src/test/org/apache/jcs/auxiliary/disk/block/BlockDiskCacheKeyStoreUnitTest.java?rev=437431&view=auto
==============================================================================
---
jakarta/jcs/trunk/src/test/org/apache/jcs/auxiliary/disk/block/BlockDiskCacheKeyStoreUnitTest.java
(added)
+++
jakarta/jcs/trunk/src/test/org/apache/jcs/auxiliary/disk/block/BlockDiskCacheKeyStoreUnitTest.java
Sun Aug 27 11:20:07 2006
@@ -0,0 +1,116 @@
+package org.apache.jcs.auxiliary.disk.block;
+
+/*
+ * Copyright 2001-2004 The Apache Software Foundation. Licensed under the
Apache License, Version
+ * 2.0 (the "License") you may not use this file except in compliance with the
License. You may
+ * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by
+ * applicable law or agreed to in writing, software distributed under the
License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or implied. See
+ * the License for the specific language governing permissions and limitations
under the License.
+ */
+
+import java.util.Random;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for the keyStore.
+ * <p>
+ * @author Aaron Smuts
+ */
+public class BlockDiskCacheKeyStoreUnitTest
+ extends TestCase
+{
+ private String rootDirName = "target/test-sandbox/block";
+
+ /**
+ * Put a bunch of keys inthe key store and verify that they are present.
+ * <p>
+ * @throws Exception
+ */
+ public void testPutKeys()
+ throws Exception
+ {
+ // SETUP
+ String regionName = "testPutKeys";
+ int maxKeys = 1000;
+ int bytesPerBlock = 2000;
+
+ BlockDiskCacheAttributes attributes = new BlockDiskCacheAttributes();
+ attributes.setCacheName( regionName );
+ attributes.setDiskPath( rootDirName );
+ attributes.setMaxKeySize( maxKeys );
+ attributes.setBlockSizeBytes( bytesPerBlock );
+
+ BlockDiskCache blockDiskCache = new BlockDiskCache( attributes );
+
+ BlockDiskKeyStore keyStore = new BlockDiskKeyStore( attributes,
blockDiskCache );
+
+ // DO WORK
+ int numElements = 100;
+ for ( int i = 0; i < numElements; i++ )
+ {
+ keyStore.put( String.valueOf( i ), new int[i] );
+ }
+ System.out.println( "testPutKeys " + keyStore );
+
+ // VERIFY
+ assertEquals( "Wrong number of keys", numElements, keyStore.size() );
+ for ( int i = 0; i < numElements; i++ )
+ {
+ int[] result = keyStore.get( String.valueOf( i ) );
+ assertEquals( "Wrong array returned.", i, result.length );
+ }
+ }
+
+ /**
+ * Verify that we can load keys that we saved. Add a bunch. Save them.
Clear the memory keyhash.
+ * Load the keys. Verify.
+ * <p>
+ * @throws Exception
+ */
+ public void testSaveLoadKeys()
+ throws Exception
+ {
+ // SETUP
+ String regionName = "testSaveLoadKeys";
+ int maxKeys = 10000;
+ int bytesPerBlock = 2000;
+
+ BlockDiskCacheAttributes attributes = new BlockDiskCacheAttributes();
+ attributes.setCacheName( regionName );
+ attributes.setDiskPath( rootDirName );
+ attributes.setMaxKeySize( maxKeys );
+ attributes.setBlockSizeBytes( bytesPerBlock );
+
+ BlockDiskCache blockDiskCache = new BlockDiskCache( attributes );
+
+ BlockDiskKeyStore keyStore = new BlockDiskKeyStore( attributes,
blockDiskCache );
+
+ // DO WORK
+ int numElements = 1000;
+ Random random = new Random( 89 );
+ for ( int i = 0; i < numElements; i++ )
+ {
+ int blocks = random.nextInt( 10 );
+ keyStore.put( String.valueOf( i ), new int[blocks] );
+ }
+ System.out.println( "testSaveLoadKeys " + keyStore );
+
+ // VERIFY
+ assertEquals( "Wrong number of keys", numElements, keyStore.size() );
+
+ // DO WORK
+ keyStore.saveKeys();
+ keyStore.clearMemoryMap();
+
+ // VERIFY
+ assertEquals( "Wrong number of keys after clearing memory", 0,
keyStore.size() );
+
+ // DO WORK
+ keyStore.loadKeys();
+
+ // VERIFY
+ assertEquals( "Wrong number of keys after loading", numElements,
keyStore.size() );
+ }
+}
Modified:
jakarta/jcs/trunk/src/test/org/apache/jcs/auxiliary/disk/block/BlockDiskElementDescriptorUnitTest.java
URL:
http://svn.apache.org/viewvc/jakarta/jcs/trunk/src/test/org/apache/jcs/auxiliary/disk/block/BlockDiskElementDescriptorUnitTest.java?rev=437431&r1=437430&r2=437431&view=diff
==============================================================================
---
jakarta/jcs/trunk/src/test/org/apache/jcs/auxiliary/disk/block/BlockDiskElementDescriptorUnitTest.java
(original)
+++
jakarta/jcs/trunk/src/test/org/apache/jcs/auxiliary/disk/block/BlockDiskElementDescriptorUnitTest.java
Sun Aug 27 11:20:07 2006
@@ -1,5 +1,14 @@
package org.apache.jcs.auxiliary.disk.block;
+/*
+ * Copyright 2001-2004 The Apache Software Foundation. Licensed under the
Apache License, Version
+ * 2.0 (the "License") you may not use this file except in compliance with the
License. You may
+ * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by
+ * applicable law or agreed to in writing, software distributed under the
License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or implied. See
+ * the License for the specific language governing permissions and limitations
under the License.
+ */
+
import junit.framework.TestCase;
/**
Modified:
jakarta/jcs/trunk/src/test/org/apache/jcs/auxiliary/disk/block/BlockDiskUnitTest.java
URL:
http://svn.apache.org/viewvc/jakarta/jcs/trunk/src/test/org/apache/jcs/auxiliary/disk/block/BlockDiskUnitTest.java?rev=437431&r1=437430&r2=437431&view=diff
==============================================================================
---
jakarta/jcs/trunk/src/test/org/apache/jcs/auxiliary/disk/block/BlockDiskUnitTest.java
(original)
+++
jakarta/jcs/trunk/src/test/org/apache/jcs/auxiliary/disk/block/BlockDiskUnitTest.java
Sun Aug 27 11:20:07 2006
@@ -1,5 +1,14 @@
package org.apache.jcs.auxiliary.disk.block;
+/*
+ * Copyright 2001-2004 The Apache Software Foundation. Licensed under the
Apache License, Version
+ * 2.0 (the "License") you may not use this file except in compliance with the
License. You may
+ * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by
+ * applicable law or agreed to in writing, software distributed under the
License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or implied. See
+ * the License for the specific language governing permissions and limitations
under the License.
+ */
+
import java.io.File;
import junit.framework.TestCase;
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]