ALF-9613: (fake) slow content store and CachingContentStore tests to use it.

Created a stub content store that reads and writes very slowly and end-to-end tests that utilise the slow store to prove there is a speed up on reading and writing through the CachingContentStore.

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@29838 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Matt Ward
2011-08-17 15:12:35 +00:00
parent 3c84a0b4dc
commit c3ca707bff
3 changed files with 377 additions and 0 deletions

View File

@@ -0,0 +1,229 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.content.caching.test;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import org.alfresco.repo.content.AbstractContentReader;
import org.alfresco.repo.content.AbstractContentStore;
import org.alfresco.repo.content.AbstractContentWriter;
import org.alfresco.repo.content.filestore.FileContentStore;
import org.alfresco.service.cmr.repository.ContentIOException;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentWriter;
/**
* Package-private class - only for testing the CachingContentStore.
*
* @author Matt Ward
*/
class SlowContentStore extends AbstractContentStore
{
/*
* @see org.alfresco.repo.content.ContentStore#isWriteSupported()
*/
@Override
public boolean isWriteSupported()
{
return true;
}
/*
* @see org.alfresco.repo.content.ContentStore#getReader(java.lang.String)
*/
@Override
public ContentReader getReader(String contentUrl)
{
return new SlowReader(contentUrl);
}
@Override
protected ContentWriter getWriterInternal(ContentReader existingContentReader, String newContentUrl)
{
if (newContentUrl == null)
newContentUrl = FileContentStore.createNewFileStoreUrl() + ".slow";
return new SlowWriter(newContentUrl, existingContentReader);
}
@Override
public boolean exists(String contentUrl)
{
return false;
}
private class SlowWriter extends AbstractContentWriter
{
protected SlowWriter(String contentUrl, ContentReader existingContentReader)
{
super(contentUrl, existingContentReader);
}
@Override
public long getSize()
{
return 20;
}
@Override
protected ContentReader createReader() throws ContentIOException
{
return new SlowReader(getContentUrl());
}
@Override
protected WritableByteChannel getDirectWritableChannel() throws ContentIOException
{
return new WritableByteChannel()
{
private boolean closed = false;
private int left = 200;
@Override
public boolean isOpen()
{
return !closed;
}
@Override
public void close() throws IOException
{
closed = true;
}
@Override
public int write(ByteBuffer src) throws IOException
{
try
{
Thread.sleep(50);
}
catch (InterruptedException error)
{
throw new RuntimeException(error);
}
if (left > 0)
{
src.get();
left--;
return 1;
}
return 0;
}
};
}
}
private class SlowReader extends AbstractContentReader
{
protected SlowReader(String contentUrl)
{
super(contentUrl);
}
@Override
public boolean exists()
{
return true;
}
@Override
public long getLastModified()
{
return 0L;
}
@Override
public long getSize()
{
return 20;
}
@Override
protected ContentReader createReader() throws ContentIOException
{
return new SlowReader(getContentUrl());
}
@Override
protected ReadableByteChannel getDirectReadableChannel() throws ContentIOException
{
return new ReadableByteChannel()
{
private final byte[] content = "This is the content for my slow ReadableByteChannel".getBytes();
private int index = 0;
private boolean closed = false;
@Override
public boolean isOpen()
{
return !closed;
}
@Override
public void close() throws IOException
{
closed = true;
}
@Override
public int read(ByteBuffer dst) throws IOException
{
if (index < content.length)
{
try
{
Thread.sleep(50);
}
catch (InterruptedException error)
{
throw new RuntimeException(error);
}
dst.put(content[index++]);
return 1;
}
else
{
return 0;
}
}
};
}
}
public static void main(String[] args)
{
SlowContentStore scs = new SlowContentStore();
ContentReader reader = scs.getReader("store://something/bin");
String content = reader.getContentString();
System.out.println("Content: " + content);
}
}

View File

@@ -0,0 +1,140 @@
/*
* Copyright (C) 2005-2011 Alfresco Software Limited.
*
* This file is part of Alfresco
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.content.caching.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.alfresco.repo.content.ContentContext;
import org.alfresco.repo.content.caching.CachingContentStore;
import org.alfresco.util.ApplicationContextHelper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Tests that exercise the CachingContentStore in conjunction with a backing store
* that runs deliberately slowly.
*
* @author Matt Ward
*/
public class SlowContentStoreTest
{
private ClassPathXmlApplicationContext ctx;
private CachingContentStore cachingStore;
private static final Log logger = LogFactory.getLog(SlowContentStoreTest.class);
public SlowContentStoreTest()
{
String conf = "classpath:cachingstore/test-context.xml";
String slowconf = "classpath:cachingstore/test-slow-context.xml";
ctx = (ClassPathXmlApplicationContext) ApplicationContextHelper.getApplicationContext(new String[] { conf, slowconf });
cachingStore = (CachingContentStore) ctx.getBean("cachingContentStore");
cachingStore.setCacheOnInbound(false);
}
@Test
public void readsAreFasterFromCache()
{
// First read will hit the SLOW backing store
TimedStoreReader storeReader = new TimedStoreReader();
storeReader.execute();
assertTrue("First read should take a while", storeReader.timeTakenMillis() > 1000);
logger.info(String.format("First read took %ds", storeReader.timeTakenMillis()));
// The content came from the slow backing store...
assertEquals("This is the content for my slow ReadableByteChannel", storeReader.content);
// Subsequent reads will hit the cache
for (int i = 0; i < 5; i++)
{
storeReader = new TimedStoreReader();
storeReader.execute();
assertTrue("Subsequent reads should be fast", storeReader.timeTakenMillis() < 100);
logger.info(String.format("Cache read took %ds", storeReader.timeTakenMillis()));
// The content came from the slow backing store, but was cached...
assertEquals("This is the content for my slow ReadableByteChannel", storeReader.content);
}
}
@Test
public void writeThroughCacheResultsInFastReadFirstTime()
{
cachingStore.setCacheOnInbound(true);
// This content will be cached on the way in
cachingStore.getWriter(new ContentContext(null, "any-url")).
putContent("Content written from " + getClass().getSimpleName());
// First read will hit cache
TimedStoreReader storeReader = new TimedStoreReader();
storeReader.execute();
assertTrue("First read should be fast", storeReader.timeTakenMillis() < 100);
logger.info(String.format("First read took %ds", storeReader.timeTakenMillis()));
assertEquals("Content written from " + getClass().getSimpleName(), storeReader.content);
// Subsequent reads will also hit the cache
for (int i = 0; i < 5; i++)
{
storeReader = new TimedStoreReader();
storeReader.execute();
assertTrue("Subsequent reads should be fast", storeReader.timeTakenMillis() < 100);
logger.info(String.format("Cache read took %ds", storeReader.timeTakenMillis()));
// The original cached content, still cached...
assertEquals("Content written from " + getClass().getSimpleName(), storeReader.content);
}
}
private class TimedStoreReader extends TimedExecutor
{
String content;
@Override
protected void doExecute()
{
content = cachingStore.getReader("any-url").getContentString();
logger.info("Read content: " + content);
}
}
private abstract class TimedExecutor
{
private long start;
private long finish;
public void execute()
{
start = System.currentTimeMillis();
doExecute();
finish = System.currentTimeMillis();
}
public long timeTakenMillis()
{
return finish - start;
}
protected abstract void doExecute();
}
}

View File

@@ -0,0 +1,8 @@
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
<beans>
<bean id="backingStore" class="org.alfresco.repo.content.caching.test.SlowContentStore"/>
</beans>