package com.bigdata.rwstore.sector;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Properties;
import junit.extensions.proxy.ProxyTestSuite;
import junit.framework.Test;
import com.bigdata.btree.IIndex;
import com.bigdata.btree.IndexMetadata;
import com.bigdata.io.DirectBufferPool;
import com.bigdata.journal.AbstractJournalTestCase;
import com.bigdata.journal.AbstractMRMWTestCase;
import com.bigdata.journal.AbstractMROWTestCase;
import com.bigdata.journal.BufferMode;
import com.bigdata.journal.Journal;
import com.bigdata.journal.Journal.Options;
import com.bigdata.journal.TestJournalBasics;
import com.bigdata.rawstore.AbstractRawStoreTestCase;
import com.bigdata.rawstore.IRawStore;
import com.bigdata.rwstore.IRWStrategy;
import com.bigdata.rwstore.IRawTx;
import com.bigdata.service.AbstractTransactionService;
/**
* Test suite for {@link MemStore}.
*
* @author thompsonbry
*/
public class TestMemStore extends AbstractJournalTestCase {
public TestMemStore() {
}
public TestMemStore(String name) {
super(name);
}
public Properties getProperties() {
final Properties properties = super.getProperties();
return setProperties(properties);
}
static public Properties setProperties(final Properties properties) {
properties.setProperty(Journal.Options.COLLECT_PLATFORM_STATISTICS,
"false");
properties.setProperty(Journal.Options.COLLECT_QUEUE_STATISTICS,
"false");
properties.setProperty(Journal.Options.HTTPD_PORT, "-1"/* none */);
properties.setProperty(Options.BUFFER_MODE, BufferMode.MemStore
.toString());
/*
* Make sure that we are not create a backing file. The CREATE_TEMP_FILE
* option does not require a file name. So, make sure there is no file
* name and make sure that we CREATE_TEMP_FILE is not true.
*/
{
assertNull(properties.getProperty(Options.FILE));
if (Boolean
.valueOf(properties.getProperty(Options.CREATE_TEMP_FILE,
Options.DEFAULT_CREATE_TEMP_FILE))) {
properties.setProperty(Options.CREATE_TEMP_FILE, "false");
}
}
// properties.setProperty(Options.BUFFER_MODE,
// BufferMode.TemporaryRW.toString());
// properties.setProperty(Options.CREATE_TEMP_FILE, "true");
// properties.setProperty(Options.FILE,
// "/Volumes/SSDData/TestRW/tmp.rw");
properties.setProperty(Options.DELETE_ON_EXIT, "true");
// ensure history retention to force deferredFrees
// properties.setProperty(AbstractTransactionService.Options.MIN_RELEASE_AGE,
// "1"); // Non-zero
// Set OVERWRITE_DELETE
// properties.setProperty(RWStore.Options.OVERWRITE_DELETE, "true");
return properties;
}
static Journal getJournal(final Properties props) {
return new Journal(props);
}
public static Test suite() {
final TestMemStore delegate = new TestMemStore(); // !!!! THIS CLASS !!!!
/*
* Use a proxy test suite and specify the delegate.
*/
final ProxyTestSuite suite = new ProxyTestSuite(delegate,
"MemStore Test Suite");
/*
* List any non-proxied tests (typically bootstrapping tests).
*/
// test suite for the IRawStore api.
suite.addTestSuite( TestRawStore.class );
// Note: test suite not used since there is no file channel to be closed by interrupts.
// suite.addTestSuite( TestInterrupts.class );
// test suite for MROW correctness.
suite.addTestSuite( TestMROW.class );
// test suite for MRMW correctness.
suite.addTestSuite( TestMRMW.class );
/*
* Pickup the basic journal test suite. This is a proxied test suite, so
* all the tests will run with the configuration specified in this test
* class and its optional .properties file.
*/
suite.addTest(TestJournalBasics.suite());
return suite;
}
/**
* Verify normal operation and basic assumptions when creating a new journal
* using {@link BufferMode#Transient}.
*
* @throws IOException
*/
public void test_create_transient01() throws IOException {
final IRawStore store = new MemStore(DirectBufferPool.INSTANCE, 1/* nbuffers */);
try {
assertFalse("isStable", store.isStable());
assertTrue("isFullyBuffered", store.isFullyBuffered());
} finally {
store.destroy();
}
}
/**
* Test suite integration for {@link AbstractRawStoreTestCase}.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
*/
public static class TestRawStore extends AbstractRawStoreTestCase {
public TestRawStore() {
super();
}
public TestRawStore(String name) {
super(name);
}
/** Note: Properties are not used. */
public Properties getProperties() {
return setProperties(new Properties());
}
protected IRawStore getStore() {
return new MemStore(DirectBufferPool.INSTANCE);
}
/**
* Can be tested by removing RWStore call to journal.removeCommitRecordEntries
* in freeDeferrals.
*
* final int commitPointsRemoved = journal.removeCommitRecordEntries(fromKey, toKey);
*
* replaced with
*
* final int commitPointsRemoved = commitPointsRecycled;
*
*/
public void testVerifyCommitRecordIndex() {
final Properties properties = new Properties(getProperties());
properties.setProperty(
AbstractTransactionService.Options.MIN_RELEASE_AGE, "400");
final Journal store = getJournal(properties);
try {
MemStrategy bs = (MemStrategy) store.getBufferStrategy();
for (int r = 0; r < 10; r++) {
ArrayList<Long> addrs = new ArrayList<Long>();
for (int i = 0; i < 100; i++) {
addrs.add(bs.write(randomData(45)));
}
store.commit();
for (long addr : addrs) {
bs.delete(addr);
}
store.commit();
}
// Age the history (of the deletes!)
Thread.currentThread().sleep(400);
verifyCommitIndex(store, 20);
store.close();
} catch (InterruptedException e) {
} finally {
store.destroy();
}
}
private void verifyCommitIndex(final Journal jrnl, int expectedRecords) {
// Check if journal is DISKRW
if (jrnl.getBufferStrategy().getBufferMode() != BufferMode.MemStore) {
System.err.println("Buffer mode should be MemStore not " + jrnl.getBufferStrategy().getBufferMode());
return;
}
final MemStrategy strategy = (MemStrategy) jrnl.getBufferStrategy();
final IIndex commitRecordIndex = jrnl.getReadOnlyCommitRecordIndex();
if (commitRecordIndex == null) {
System.err.println("Unexpected null commit record index");
return;
}
final IndexMetadata metadata = commitRecordIndex
.getIndexMetadata();
final byte[] zeroKey = metadata.getTupleSerializer()
.serializeKey(0L);
final byte[] releaseKey = metadata.getTupleSerializer()
.serializeKey(System.currentTimeMillis());
final int removed = jrnl.removeCommitRecordEntries(zeroKey, releaseKey);
assertTrue(removed == expectedRecords);
jrnl.commit();
}
/**
* Tests whether tasks are able to access and modify data safely by
* emulating transactions by calling activateTx and deactivateTx
* directly.
*/
public void test_sessionProtection() {
final Properties p = getProperties();
// Note: No longer the default. Must be explicitly set.
p.setProperty(AbstractTransactionService.Options.MIN_RELEASE_AGE, "0");
final Journal store = getJournal(p);
try {
final IRWStrategy bs = (IRWStrategy) store.getBufferStrategy();
final byte[] buf = new byte[300]; // Just some data
r.nextBytes(buf);
final ByteBuffer bb = ByteBuffer.wrap(buf);
final long faddr = store.write(bb); // rw.alloc(buf, buf.length);
final IRawTx tx = bs.newTx();
ByteBuffer rdBuf = store.read(faddr);
bb.position(0);
assertEquals(bb, rdBuf);
store.delete(faddr); // deletion protected by session
bb.position(0);
rdBuf = store.read(faddr);
// should be able to successfully read from freed address
assertEquals(bb, rdBuf);
tx.close();
store.commit();
} finally {
store.destroy();
}
}
public void test_stressSessionProtection() {
// Sequential logic
final Properties p = getProperties();
// Note: No longer the default. Must be explicitly set.
p.setProperty(AbstractTransactionService.Options.MIN_RELEASE_AGE, "0");
final Journal store = getJournal(p);
try {
final IRWStrategy bs = (IRWStrategy) store.getBufferStrategy();
byte[] buf = new byte[300]; // Just some data
r.nextBytes(buf);
ByteBuffer bb = ByteBuffer.wrap(buf);
IRawTx tx = bs.newTx();
ArrayList<Long> addrs = new ArrayList<Long>();
// We just want to stress a single allocator, so do not
// make more allocations than a single allocator can
// handle since this would prevent predictable
// recycling
for (int i = 0; i < 1000; i++) {
addrs.add(store.write(bb));
bb.flip();
}
for (int i = 0; i < 1000; i += 2) {
store.delete(addrs.get(i));
}
// now release session to make addrs reavailable
tx.close();
for (int i = 0; i < 1000; i += 2) {
long addr = store.write(bb);
assertTrue(addr == addrs.get(i));
bb.flip();
}
store.commit();
bb.position(0);
for (int i = 0; i < 500; i++) {
bb.position(0);
ByteBuffer rdBuf = store.read(addrs.get(i));
// should be able to
assertEquals(bb, rdBuf);
}
store.commit();
} finally {
store.destroy();
}
}
public void test_allocCommitFree() {
final Journal store = getJournal(getProperties());
try {
MemStrategy bs = (MemStrategy) store.getBufferStrategy();
final long addr = bs.write(randomData(78));
store.commit();
bs.delete(addr);
assertTrue(bs.isCommitted(addr));
} finally {
store.destroy();
}
}
public void test_allocCommitFreeWithHistory() {
final Properties properties = new Properties(getProperties());
properties.setProperty(
AbstractTransactionService.Options.MIN_RELEASE_AGE, "100");
final Journal store = getJournal(properties);
try {
MemStrategy bs = (MemStrategy) store.getBufferStrategy();
final long addr = bs.write(randomData(78));
store.commit();
bs.delete(addr);
assertTrue(bs.isCommitted(addr));
} finally {
store.destroy();
}
}
ByteBuffer randomData(final int sze) {
return ByteBuffer.wrap(randomBytes(sze));
}
byte[] randomBytes(final int sze) {
byte[] buf = new byte[sze + 4]; // extra for checksum
r.nextBytes(buf);
return buf;
}
public void test_blobDeferredFrees() {
final Properties properties = new Properties(getProperties());
properties.setProperty(
AbstractTransactionService.Options.MIN_RELEASE_AGE, "100");
final Journal store = getJournal(properties);
try {
MemStrategy bs = (MemStrategy) store.getBufferStrategy();
ArrayList<Long> addrs = new ArrayList<Long>();
for (int i = 0; i < 4000; i++) {
addrs.add(bs.write(randomData(45)));
}
store.commit();
for (long addr : addrs) {
bs.delete(addr);
}
for (int i = 0; i < 4000; i++) {
if(!bs.isCommitted(addrs.get(i))) {
fail("i="+i+", addr="+addrs.get(i));
}
}
store.commit();
// Age the history (of the deletes!)
Thread.currentThread().sleep(6000);
// modify store but do not allocate similar size block
// as that we want to see has been removed
final long addr2 = bs.write(randomData(220)); // modify store
store.commit();
bs.delete(addr2); // modify store
store.commit();
// delete is actioned
for (int i = 0; i < 4000; i++) {
assertFalse(bs.isCommitted(addrs.get(i)));
}
} catch (InterruptedException e) {
} finally {
store.destroy();
}
}
}
/**
* Test suite integration for {@link AbstractMROWTestCase}.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
*/
public static class TestMROW extends AbstractMROWTestCase {
public TestMROW() {
super();
}
public TestMROW(String name) {
super(name);
}
/** Note: Properties are not used. */
public Properties getProperties() {
return new Properties();
}
protected IRawStore getStore() {
return new MemStore(DirectBufferPool.INSTANCE);
}
}
/**
* Test suite integration for {@link AbstractMRMWTestCase}.
*
* @author <a href="mailto:thompsonbry@users.sourceforge.net">Bryan Thompson</a>
*/
public static class TestMRMW extends AbstractMRMWTestCase {
public TestMRMW() {
super();
}
public TestMRMW(String name) {
super(name);
}
/** Note: Properties are not used. */
public Properties getProperties() {
return new Properties();
}
protected IRawStore getStore() {
return new MemStore(DirectBufferPool.INSTANCE);
}
}
}