package org.infinispan.persistence.leveldb;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.fail;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.infinispan.commons.util.Util;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.configuration.cache.PersistenceConfigurationBuilder;
import org.infinispan.container.entries.InternalCacheEntry;
import org.infinispan.marshall.core.MarshalledEntry;
import org.infinispan.persistence.BaseStoreTest;
import org.infinispan.persistence.leveldb.configuration.LevelDBStoreConfigurationBuilder;
import org.infinispan.persistence.spi.AdvancedLoadWriteStore;
import org.infinispan.persistence.spi.PersistenceException;
import org.infinispan.test.TestingUtil;
import org.infinispan.test.fwk.TestCacheManagerFactory;
import org.infinispan.test.fwk.TestInternalCacheEntryFactory;
import org.testng.annotations.AfterClass;
import org.testng.annotations.Test;
@Test(groups = "unit", testName = "persistence.leveldb.LevelDBStoreTest")
public class LevelDBStoreTest extends BaseStoreTest {
private String tmpDirectory = TestingUtil.tmpDirectory(this.getClass());
@AfterClass(alwaysRun = true)
protected void clearTempDir() {
Util.recursiveFileRemove(tmpDirectory);
}
protected LevelDBStoreConfigurationBuilder createCacheStoreConfig(PersistenceConfigurationBuilder lcb) {
LevelDBStoreConfigurationBuilder cfg = lcb.addStore(LevelDBStoreConfigurationBuilder.class);
cfg.location(tmpDirectory + "/data");
cfg.expiredLocation(tmpDirectory + "/expiry");
cfg.clearThreshold(2);
return cfg;
}
@Override
protected AdvancedLoadWriteStore createStore() throws Exception {
clearTempDir();
LevelDBStore fcs = new LevelDBStore();
ConfigurationBuilder cb = TestCacheManagerFactory.getDefaultCacheConfiguration(false);
createCacheStoreConfig(cb.persistence());
fcs.init(createContext(cb.build()));
return fcs;
}
@Test(groups = "stress", timeOut = 15*60*1000)
public void testConcurrentWriteAndRestart() {
concurrentWriteAndRestart(true);
}
@Test(groups = "stress", timeOut = 15*60*1000)
public void testConcurrentWriteAndStop() {
concurrentWriteAndRestart(true);
}
private void concurrentWriteAndRestart(boolean start) {
final int THREADS = 4;
final AtomicBoolean run = new AtomicBoolean(true);
final AtomicInteger writtenPre = new AtomicInteger();
final AtomicInteger writtenPost = new AtomicInteger();
final AtomicBoolean post = new AtomicBoolean(false);
final CountDownLatch started = new CountDownLatch(THREADS);
final CountDownLatch finished = new CountDownLatch(THREADS);
for (int i = 0; i < THREADS; ++i) {
final int thread = i;
fork(new Runnable() {
@Override
public void run() {
try {
started.countDown();
int i = 0;
while (run.get()) {
InternalCacheEntry entry = TestInternalCacheEntryFactory.create("k" + i, "v" + i);
MarshalledEntry me = TestingUtil.marshalledEntry(entry, getMarshaller());
try {
AtomicInteger record = post.get() ? writtenPost : writtenPre;
cl.write(me);
++i;
int prev;
do {
prev = record.get();
if ((prev & (1 << thread)) != 0) break;
} while (record.compareAndSet(prev, prev | (1 << thread)));
} catch (PersistenceException e) {
// when the store is stopped, exceptions are thrown
}
}
} catch (Exception e) {
log.error("Failed", e);
throw new RuntimeException(e);
} finally {
finished.countDown();
}
}
});
}
try {
if (!started.await(30, TimeUnit.SECONDS)) {
fail();
}
Thread.sleep(1000);
cl.stop();
post.set(true);
Thread.sleep(1000);
if (start) {
cl.start();
Thread.sleep(1000);
}
} catch (InterruptedException e) {
fail();
} finally {
run.set(false);
}
try {
if (!finished.await(30, TimeUnit.SECONDS)) {
fail();
}
} catch (InterruptedException e) {
fail();
}
assertEquals(writtenPre.get(), (1 << THREADS) - 1, "pre");
if (start) {
assertEquals(writtenPost.get(), (1 << THREADS) - 1, "post");
} else {
assertEquals(writtenPost.get(), 0, "post");
}
}
}